Skip to content

Using PyPayload Macro with Custom Classes #6054

@topcoco

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!

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