2020from __future__ import annotations
2121
2222from abc import ABC , abstractmethod
23- from typing import TYPE_CHECKING , Protocol
23+ from typing import TYPE_CHECKING , Any , Protocol
24+
25+ import warnings
2426
2527import datafusion ._internal as df_internal
28+ from datafusion ._internal import EXPECTED_PROVIDER_MSG
2629from datafusion .utils import _normalize_table_provider
2730
2831if TYPE_CHECKING :
@@ -136,7 +139,9 @@ def register_table(
136139 """Register a table or table provider in this schema.
137140
138141 Objects implementing ``__datafusion_table_provider__`` are also supported
139- and treated as :class:`TableProvider` instances.
142+ and treated as table provider instances. The deprecated
143+ :class:`~datafusion.table_provider.TableProvider` wrapper remains accepted
144+ for backwards compatibility.
140145 """
141146 provider = _normalize_table_provider (table )
142147 return self ._raw_schema .register_table (name , provider )
@@ -151,31 +156,108 @@ class Database(Schema):
151156 """See `Schema`."""
152157
153158
159+ _InternalRawTable = df_internal .catalog .RawTable
160+ _InternalTableProvider = df_internal .TableProvider
161+
162+ # Keep in sync with ``datafusion._internal.TableProvider.from_view``.
163+ _FROM_VIEW_WARN_STACKLEVEL = 2
164+
165+
154166class Table :
155- """DataFusion table."""
167+ """DataFusion table or table provider wrapper ."""
156168
157- def __init__ (self , table : df_internal .catalog .RawTable ) -> None :
158- """This constructor is not typically called by the end user."""
159- self .table = table
169+ __slots__ = ("_table" ,)
170+
171+ def __init__ (
172+ self ,
173+ table : _InternalRawTable | _InternalTableProvider | Table ,
174+ ) -> None :
175+ """Wrap a low level table or table provider."""
176+
177+ if isinstance (table , Table ):
178+ table = table .table
179+
180+ if not isinstance (table , (_InternalRawTable , _InternalTableProvider )):
181+ raise TypeError (EXPECTED_PROVIDER_MSG )
182+
183+ self ._table = table
184+
185+ def __getattribute__ (self , name : str ) -> Any :
186+ """Restrict provider-specific helpers to compatible tables."""
187+
188+ if name == "__datafusion_table_provider__" :
189+ table = object .__getattribute__ (self , "_table" )
190+ if not hasattr (table , "__datafusion_table_provider__" ):
191+ raise AttributeError (name )
192+ return object .__getattribute__ (self , name )
160193
161194 def __repr__ (self ) -> str :
162195 """Print a string representation of the table."""
163- return self .table . __repr__ ( )
196+ return repr ( self ._table )
164197
165- @staticmethod
166- def from_dataset (dataset : pa .dataset .Dataset ) -> Table :
167- """Turn a pyarrow Dataset into a Table."""
168- return Table (df_internal .catalog .RawTable .from_dataset (dataset ))
198+ @property
199+ def table (self ) -> _InternalRawTable | _InternalTableProvider :
200+ """Return the wrapped low level table object."""
201+ return self ._table
202+
203+ @classmethod
204+ def from_dataset (cls , dataset : pa .dataset .Dataset ) -> Table :
205+ """Turn a :mod:`pyarrow.dataset` ``Dataset`` into a :class:`Table`."""
206+
207+ return cls (_InternalRawTable .from_dataset (dataset ))
208+
209+ @classmethod
210+ def from_capsule (cls , capsule : Any ) -> Table :
211+ """Create a :class:`Table` from a PyCapsule exported provider."""
212+
213+ provider = _InternalTableProvider .from_capsule (capsule )
214+ return cls (provider )
215+
216+ @classmethod
217+ def from_dataframe (cls , df : Any ) -> Table :
218+ """Create a :class:`Table` from tabular data."""
219+
220+ from datafusion .dataframe import DataFrame as DataFrameWrapper
221+
222+ dataframe = df if isinstance (df , DataFrameWrapper ) else DataFrameWrapper (df )
223+ return dataframe .into_view ()
224+
225+ @classmethod
226+ def from_view (cls , df : Any ) -> Table :
227+ """Deprecated helper for constructing tables from views."""
228+
229+ from datafusion .dataframe import DataFrame as DataFrameWrapper
230+
231+ if isinstance (df , DataFrameWrapper ):
232+ df = df .df
233+
234+ provider = _InternalTableProvider .from_view (df )
235+ warnings .warn (
236+ "Table.from_view is deprecated; use DataFrame.into_view or "
237+ "Table.from_dataframe instead." ,
238+ category = DeprecationWarning ,
239+ stacklevel = _FROM_VIEW_WARN_STACKLEVEL ,
240+ )
241+ return cls (provider )
169242
170243 @property
171244 def schema (self ) -> pa .Schema :
172245 """Returns the schema associated with this table."""
173- return self .table .schema
246+ return self ._table .schema
174247
175248 @property
176249 def kind (self ) -> str :
177250 """Returns the kind of table."""
178- return self .table .kind
251+ return self ._table .kind
252+
253+ def __datafusion_table_provider__ (self ) -> Any :
254+ """Expose the wrapped provider for FFI integrations."""
255+
256+ exporter = getattr (self ._table , "__datafusion_table_provider__" , None )
257+ if exporter is None :
258+ msg = "Underlying object does not export __datafusion_table_provider__()"
259+ raise AttributeError (msg )
260+ return exporter ()
179261
180262
181263class CatalogProvider (ABC ):
@@ -241,7 +323,9 @@ def register_table( # noqa: B027
241323 not need to implement this method.
242324
243325 Objects implementing ``__datafusion_table_provider__`` are also supported
244- and treated as :class:`TableProvider` instances.
326+ and treated as table provider instances. The deprecated
327+ :class:`~datafusion.table_provider.TableProvider` wrapper remains accepted
328+ for backwards compatibility.
245329 """
246330
247331 def deregister_table (self , name : str , cascade : bool ) -> None : # noqa: B027
0 commit comments