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

How to make 3rd party rust types available to the VM #5459

Open

Description

@directindex

Summary

I'm building a little data processing application, in which I'd like be able to add data transformations that are written in python but will be executed by rust under the hood. Eventually I want provide a polars dataframe, and modify it via python.

Something like this:

def transform(df):
    df = df.with_columns(
        (pl.col("x") * 2).alias("twice")
    ).collect()
    
    return df

I'm already hitting a road block getting the dataframe in and out of the VM. I tried to build on top of the call_between_rust_and_python example, but it only works with types that are already supported by rustpython or on simple types that are composed of supported types.

I'd be very happy if you'd nugde me into the right direction, given if it's actually possible what I'm trying to achieve.

Activity

  1. youknowone commented on Dec 20, 2024

    @youknowone
    Member

    Still RustStruct in the call_between_rust_and_python is the best option. You have to wrap a rust type to make a python type.
    The inner rust type can be literally any type, though they requires different wrapping strategy.
    Please share the details how you are blocked by building on top of call_between_rust_and_python. Then I could understand the situation better.

  2. directindex commented on Dec 20, 2024

    @directindex
    Author

    Thanks for getting back.

    I can't implement the TryFromBorrowedObject trait for MyLazyFrame as LazyFrame doesn't implement TryFromBorrowedObject, and I can't implement the trait myself as it's from an external crate.

    See the comment in the snippet.

    use polars::prelude::*;
    
    #[pyattr]
    #[pyclass(module = "rust_py_module", name = "LazyFrame")]
    #[derive(PyPayload)]
    pub struct MyLazyFrame {
        pub lazy_frame: LazyFrame,
    }
    
    #[pyclass]
    impl MyLazyFrame {
        #[pygetset]
        fn select(self, columns: Vec<String>) -> self {
            let cols = columns.iter().map(|c| col(c)).collect();
    
            MyLazyFrame {
                lazy_frame: self.lazy_frame.select(cols),
            }
        }
    }
    
    impl<'a> TryFromBorrowedObject<'a> for MyLazyFrame {
        fn try_from_borrowed_object(vm: &VirtualMachine, obj: &'a PyObject) -> PyResult<Self> {
            // this doesn't work as Trait `TryFromBorrowedObject` is not implemented for `LazyFrame` 
            let lazy_frame = obj.get_attr("lazy_frame", vm)?.try_into_value::<LazyFrame>(vm)?; 
            Ok(MyLazyFrame { lazy_frame })
        }
    }
  3. youknowone commented on Jan 9, 2025

    @youknowone
    Member

    LazyFrame is not a Python type and only MyLazyFrame is. You can neither do obj.get_attr("lazy_frame", vm) nor try_into_value::<LazyFrame>(vm).
    I just realized the example is confusing because python code and rust code shares same field name.

    This is probably the flow you are looking for.

    // MyLazyFrame is already a Python type. You don't need any conversion.
    impl<'a> TryFromObject<'a> for LazyFrame {
        // the `obj` here must be a `MyLazyFrame` object.
        fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
            let frame: PyRef<MyLazyFrame> = obj.try_to_value::<MyLazyFrame>(vm)?;
            Ok(frame.lazy_frame.clone())
        }
    }

    If you don't need to retrieve a LazyFrame object directly, you don't need to implement it.
    downcast, try_to_value, try_to_ref will work for MyLazyFrame without additional work.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions