@@ -20,6 +20,7 @@ use std::ptr::NonNull;
2020use std:: sync:: { Arc , OnceLock } ;
2121use std:: time:: Duration ;
2222
23+ use datafusion:: catalog:: TableProviderFactory ;
2324use datafusion:: datasource:: TableProvider ;
2425use datafusion:: execution:: TaskContext ;
2526use datafusion:: execution:: context:: SessionContext ;
@@ -30,6 +31,7 @@ use datafusion_ffi::physical_optimizer::FFI_PhysicalOptimizerRule;
3031use datafusion_ffi:: proto:: logical_extension_codec:: FFI_LogicalExtensionCodec ;
3132use datafusion_ffi:: proto:: physical_extension_codec:: FFI_PhysicalExtensionCodec ;
3233use datafusion_ffi:: table_provider:: FFI_TableProvider ;
34+ use datafusion_ffi:: table_provider_factory:: FFI_TableProviderFactory ;
3335use datafusion_proto:: physical_plan:: PhysicalExtensionCodec ;
3436use pyo3:: exceptions:: { PyImportError , PyTypeError , PyValueError } ;
3537use pyo3:: prelude:: * ;
@@ -249,6 +251,44 @@ pub fn create_physical_extension_capsule<'py>(
249251/// instead.
250252#[ macro_export]
251253macro_rules! from_pycapsule {
254+ ( $fn_name: ident, $capsule_name: literal, $ffi_type: ty, $output_type: ty, call_args) => {
255+ pub fn $fn_name<' py, A >(
256+ obj: & $crate:: pyo3:: Bound <' py, $crate:: pyo3:: PyAny >,
257+ args: A ,
258+ ) -> $crate:: pyo3:: PyResult <std:: sync:: Arc <$output_type>>
259+ where
260+ A : $crate:: pyo3:: call:: PyCallArgs <' py>,
261+ {
262+ use $crate:: pyo3:: prelude:: * ;
263+ use $crate:: pyo3:: types:: PyCapsule ;
264+
265+ let mut obj = obj. clone( ) ;
266+ if obj. hasattr( concat!( "__" , $capsule_name, "__" ) ) ? {
267+ obj = obj
268+ . getattr( concat!( "__" , $capsule_name, "__" ) ) ?
269+ . call1( args) ?;
270+ }
271+ let capsule = obj. cast:: <PyCapsule >( ) . map_err( |_| {
272+ $crate:: errors:: py_datafusion_err( concat!(
273+ "Invalid " ,
274+ $capsule_name,
275+ ". Does not contain PyCapsule object."
276+ ) )
277+ } ) ?;
278+ $crate:: validate_pycapsule( & capsule, $capsule_name) ?;
279+
280+ let expected_name = std:: ffi:: CString :: new( $capsule_name)
281+ . expect( "capsule name must not contain interior NUL bytes" ) ;
282+ let data: std:: ptr:: NonNull <$ffi_type> = capsule
283+ . pointer_checked( Some ( expected_name. as_c_str( ) ) ) ?
284+ . cast( ) ;
285+ let output_obj = unsafe { data. as_ref( ) } ;
286+ let output_obj: std:: sync:: Arc <$output_type> = output_obj. into( ) ;
287+
288+ Ok ( output_obj)
289+ }
290+ } ;
291+
252292 ( $fn_name: ident, $capsule_name: literal, $ffi_type: ty, $output_type: ty) => {
253293 pub fn $fn_name(
254294 obj: & $crate:: pyo3:: Bound <$crate:: pyo3:: PyAny >,
@@ -340,6 +380,14 @@ from_pycapsule!(
340380 dyn PhysicalOptimizerRule + Send + Sync
341381) ;
342382
383+ from_pycapsule ! (
384+ table_provider_factory_from_pycapsule,
385+ "datafusion_table_provider_factory" ,
386+ FFI_TableProviderFactory ,
387+ dyn TableProviderFactory ,
388+ call_args
389+ ) ;
390+
343391try_from_pycapsule ! (
344392 task_context_from_pycapsule,
345393 "datafusion_task_context_provider" ,
0 commit comments