← 输入框|github.com|刷新
Skip to content

Using PyPayload Macro with Custom Classes #6054

Open

Description

@topcoco

🧪 Problem 1: PyPayload Causes Runtime Panic

When I define a custom Rust structure with #[derive(PyPayload)] and use it in combination with RustPython, calling the following Python code results in a crash:

Python code

from rust_py_module import Base, Wda, image, test
import time
import sys
import io

def python_callback():
    test.test()

Rust code

pub fn main() {
    let interp = rustpython::InterpreterConfig::new()
        .init_stdlib()
        .init_hook(Box::new(|vm| {
            vm.add_native_module(
                "rust_py_module".to_owned(),
                Box::new(rust_py_module::make_module),
            );
        }))
        .interpreter();

    interp.enter(|vm| {
        vm.insert_sys_path(vm.new_pyobj(r"C:\Users\RustroverProjects\python\examples"))
            .expect("add path");

        let module = match vm.import("call_between_rust_and_python", 0) {
            Ok(module) => module,
            Err(exc) => {
                let mut error_output = String::new();
                if let Err(_) = vm.write_exception(&mut error_output, &exc) {
                    println!("");
                } else {
                    println!("{}", error_output);
                }
                panic!()
            }
        };
        let take_string_fn = module.get_attr("python_callback", vm).unwrap();
        match take_string_fn.call((), vm) {
            Ok(result) => {
                println!(": {:?}", result);
            }
            Err(exc) => {
                let mut error_output = String::new();
                if let Err(_) = vm.write_exception(&mut error_output, &exc) {
                    println!("");
                } else {
                    println!("{}", error_output);
                }
            }
        }
    })
}

#[pymodule]
mod rust_py_module {
    use std::mem::ManuallyDrop;
    use super::*;
    use rustpython::vm::{builtins::PyList, convert::ToPyObject, PyObjectRef};
    use rustpython_vm::builtins::PyTypeRef;
    use rustpython_vm::FromArgs;
    use rustpython_vm::types::Constructor;
    use rustpython_vm::PyRef;
    use crate::image::{feature_config, DetectorParams, FeatureDetector, SiftParams, SIFT_MODE};


    #[pyattr]
    #[pyclass(module = "rust_py_module", name = "test")]
    #[derive(Debug, PyPayload)]
    struct Test;
    impl Constructor for Test {
        type Args = ();
        fn py_new(cls: PyTypeRef, _args: Self::Args, vm: &VirtualMachine) -> PyResult {
            let rust_struct = Test ;
            rust_struct.into_ref_with_type(vm, cls).map(Into::into)
        }
    }

    #[pyclass(with(Constructor))]
    impl Test {
        #[pymethod]
        fn test() -> PyResult<crate::feature_config1> {
            Ok(crate::feature_config1::new())
        }
    }
}

Custom Class

#[rustpython_vm::pyclass(module = false, name = "feature_config")]
#[repr(C)]
#[derive(Debug, PyPayload)]
pub struct feature_config1 {
    pub text: String
}
#[rustpython_vm::pyclass]
impl feature_config1 {
    pub fn new() -> Self {
        Self { text: String::from("a") }
    }
}

Panic Error

static type has not been initialized. e.g. the native types defined in different module may be used before importing library.
stack backtrace:
   0: __rustc::rust_begin_unwind
             at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\std\src/panicking.rs:697:5
   1: core::panicking::panic_fmt
             at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\core\src/panicking.rs:75:14
   2: core::option::expect_failed
             at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library\core\src/panicking.rs:269:5
   3: core::option::Option<T>::expect
             at C:\Users\35600\.rustup\toolchains\stable-x86_64-pc-windows-gnu\lib/rustlib/src/rust\library\core\src\option.rs:958:21
   4: rustpython_vm::class::StaticType::static_type
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\class.rs:23:9
   5: <python::feature_config1 as rustpython_vm::object::payload::PyPayload>::class
             at .\src\main.rs:267:17
   6: rustpython_vm::object::payload::PyPayload::into_ref
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\object\payload.rs:49:19
   7: rustpython_vm::object::payload::PyPayload::into_pyobject
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\object\payload.rs:26:9
   8: rustpython_vm::object::ext::<impl rustpython_vm::convert::to_pyobject::ToPyObject for T>::to_pyobject
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\object\ext.rs:563:9
   9: rustpython_vm::object::ext::<impl rustpython_vm::convert::to_pyobject::ToPyResult for core::result::Result<T,E>>::to_pyresult::{{closure}}
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\object\ext.rs:584:24
  10: core::result::Result<T,E>::map
             at C:\Users\35600\.rustup\toolchains\stable-x86_64-pc-windows-gnu\lib/rustlib/src/rust\library\core\src\result.rs:801:25
  11: rustpython_vm::object::ext::<impl rustpython_vm::convert::to_pyobject::ToPyResult for core::result::Result<T,E>>::to_pyresult
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\object\ext.rs:584:9
  12: rustpython_vm::function::builtin::<impl rustpython_vm::function::builtin::sealed::PyNativeFnInternal<(),R,()> for F>::call_
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\function\builtin.rs:172:17
  13: <F as rustpython_vm::function::builtin::IntoPyNativeFn<(T,R,VM)>>::call
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\function\builtin.rs:97:9
  14: rustpython_vm::function::builtin::into_func::{{closure}}
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\function\builtin.rs:50:38
  15: <rustpython_vm::builtins::descriptor::PyMethodDescriptor as rustpython_vm::types::slot::Callable>::call
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\builtins\descriptor.rs:97:9
  16: rustpython_vm::types::slot::Callable::slot_call
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\types\slot.rs:868:9
  17: rustpython_vm::protocol::callable::PyCallable::invoke
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\protocol\callable.rs:51:22
  18: rustpython_vm::protocol::callable::<impl rustpython_vm::object::core::PyObject>::call_with_args
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\protocol\callable.rs:33:9
  19: rustpython_vm::protocol::callable::<impl rustpython_vm::object::core::PyObject>::call  
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\protocol\callable.rs:22:9
  20: rustpython_vm::frame::ExecutingFrame::execute_method_call
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\frame.rs:1504:21
  21: rustpython_vm::frame::ExecutingFrame::execute_instruction
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\frame.rs:1093:17
  22: rustpython_vm::frame::ExecutingFrame::run
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\frame.rs:362:26
  23: rustpython_vm::frame::<impl rustpython_vm::object::core::Py<rustpython_vm::frame::Frame>>::run::{{closure}}
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\frame.rs:242:35
  24: rustpython_vm::frame::<impl rustpython_vm::object::core::Py<rustpython_vm::frame::Frame>>::with_exec
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\frame.rs:237:9
  25: rustpython_vm::frame::<impl rustpython_vm::object::core::Py<rustpython_vm::frame::Frame>>::run
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\frame.rs:242:9
  26: rustpython_vm::vm::VirtualMachine::run_frame::{{closure}}
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\vm\mod.rs:424:42
  27: rustpython_vm::vm::VirtualMachine::with_frame::{{closure}}
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\vm\mod.rs:452:26
  28: rustpython_vm::vm::VirtualMachine::with_recursion
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\vm\mod.rs:440:22
  29: rustpython_vm::vm::VirtualMachine::with_frame
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\vm\mod.rs:450:9
  30: rustpython_vm::vm::VirtualMachine::run_frame
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\vm\mod.rs:424:15
  31: rustpython_vm::builtins::function::PyFunction::invoke_with_locals
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\builtins\function.rs:338:31
  32: rustpython_vm::builtins::function::PyFunction::invoke
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\builtins\function.rs:344:9
  33: <rustpython_vm::builtins::function::PyFunction as rustpython_vm::types::slot::Callable>::call
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\builtins\function.rs:492:9
  34: rustpython_vm::types::slot::Callable::slot_call
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\types\slot.rs:868:9
  35: rustpython_vm::protocol::callable::PyCallable::invoke
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\protocol\callable.rs:51:22
  36: rustpython_vm::protocol::callable::<impl rustpython_vm::object::core::PyObject>::call_with_args
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\protocol\callable.rs:33:9
  37: rustpython_vm::protocol::callable::<impl rustpython_vm::object::core::PyObject>::call  
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\protocol\callable.rs:22:9
  38: python::main::{{closure}}
             at .\src\main.rs:242:15
  39: rustpython_vm::vm::interpreter::Interpreter::enter::{{closure}}
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\vm\interpreter.rs:72:39
  40: <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
             at C:\Users\35600\.rustup\toolchains\stable-x86_64-pc-windows-gnu\lib/rustlib/src/rust\library\core\src\panic\unwind_safe.rs:272:9
  41: std::panicking::try::do_call
             at C:\Users\35600\.rustup\toolchains\stable-x86_64-pc-windows-gnu\lib/rustlib/src/rust\library\std\src\panicking.rs:589:40
  42: __rust_try
  43: std::panicking::try
             at C:\Users\35600\.rustup\toolchains\stable-x86_64-pc-windows-gnu\lib/rustlib/src/rust\library\std\src\panicking.rs:552:19
  44: std::panic::catch_unwind
             at C:\Users\35600\.rustup\toolchains\stable-x86_64-pc-windows-gnu\lib/rustlib/src/rust\library\std\src\panic.rs:359:14
  45: rustpython_vm::vm::thread::enter_vm::{{closure}}
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\vm\thread.rs:31:19
  46: std::thread::local::LocalKey<T>::try_with
             at C:\Users\35600\.rustup\toolchains\stable-x86_64-pc-windows-gnu\lib/rustlib/src/rust\library\std\src\thread\local.rs:315:12
  47: std::thread::local::LocalKey<T>::with
             at C:\Users\35600\.rustup\toolchains\stable-x86_64-pc-windows-gnu\lib/rustlib/src/rust\library\std\src\thread\local.rs:279:15
  48: rustpython_vm::vm::thread::enter_vm
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\vm\thread.rs:28:5
  49: rustpython_vm::vm::interpreter::Interpreter::enter
             at C:\Users\35600\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\rustpython-vm-0.4.0\src\vm\interpreter.rs:72:9
  50: python::main
             at .\src\main.rs:200:5
  51: core::ops::function::FnOnce::call_once
             at C:\Users\35600\.rustup\toolchains\stable-x86_64-pc-windows-gnu\lib/rustlib/src/rust\library\core\src\ops\function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.      
error: process didn't exit successfully: `target\debug\python.exe` (exit code: 101)

However, if I manually implement PyPayload, like this, the error is gone:

impl PyPayload for feature_config1 {
    fn class(ctx: &Context) -> &'static Py<PyType> {
        ctx.types.object_type
    }
}

So the issue seems to be that #[derive(PyPayload)] fails to properly register the static type when used in this context.


🧪 Problem 2: Cannot Call Methods on Returned Custom Object

When I add a method to feature_config1, I cannot call it from Python:

#[rustpython_vm::pyclass]
impl feature_config1 {
    pub fn new() -> Self {
        Self { text: String::from("a") }
    }

    #[pymethod]
    pub fn t(&self) {
        println!("t");
    }
}

Calling:

test.test().t()

results in:

Traceback (most recent call last):
  File "C:\Users\\35600\RustroverProjects\python\examples\call_between_rust_and_python.py", line 8, in python_callback
    test.test().t()
AttributeError: 'object' object has no attribute 't'

But I expected test() to return a feature_config1 instance and be able to call its methods. If I did anything wrong, please let me know how to correctly expose and use custom objects with methods in Python via RustPython.

Thanks in advance!

Activity

  1. youknowone commented on Aug 1, 2025

    @youknowone
    Member

    Did you initialize your class? If you defined your type in #[pymodule], it is initalized when the module is initialized. Otherwise you have to call YourType::make_class somewhere. This is what the panic means

    static type has not been initialized. e.g. the native types defined in different module may be used before importing library.

  2. Svetlozarko commented on Oct 24, 2025

    @Svetlozarko

    Hey, the issue happens because #[derive(PyPayload)] doesn’t automatically register the type with RustPython, so Python can’t see its methods and the static type isn’t initialized. The fix is to manually implement PyPayload and make sure your Rust function returns a proper Python object (PyObjectRef).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions