Repository navigation
Using PyPayload Macro with Custom Classes #6054
Copy link
Copy link
Open
Labels
C-bugSomething isn't workingSomething isn't working
Description
🧪 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
Labels
C-bugSomething isn't workingSomething isn't working
Activity
youknowone commentedon Aug 1, 2025
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_classsomewhere. This is what the panic meansSvetlozarko commentedon Oct 24, 2025
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).