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 :
2932 import pyarrow as pa
3033
31- from datafusion import TableProvider
3234 from datafusion .context import TableProviderExportable
3335
3436try :
@@ -131,12 +133,12 @@ def table(self, name: str) -> Table:
131133 return Table (self ._raw_schema .table (name ))
132134
133135 def register_table (
134- self , name : str , table : Table | TableProvider | TableProviderExportable
136+ self , name : str , table : Table | TableProviderExportable | Any
135137 ) -> None :
136138 """Register a table or table provider in this schema.
137139
138140 Objects implementing ``__datafusion_table_provider__`` are also supported
139- and treated as :class:`TableProvider` instances.
141+ and treated as table provider instances.
140142 """
141143 provider = _normalize_table_provider (table )
142144 return self ._raw_schema .register_table (name , provider )
@@ -151,31 +153,108 @@ class Database(Schema):
151153 """See `Schema`."""
152154
153155
156+ _InternalRawTable = df_internal .catalog .RawTable
157+ _InternalTableProvider = df_internal .TableProvider
158+
159+ # Keep in sync with ``datafusion._internal.TableProvider.from_view``.
160+ _FROM_VIEW_WARN_STACKLEVEL = 2
161+
162+
154163class Table :
155- """DataFusion table."""
164+ """DataFusion table or table provider wrapper ."""
156165
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
166+ __slots__ = ("_table" ,)
167+
168+ def __init__ (
169+ self ,
170+ table : _InternalRawTable | _InternalTableProvider | Table ,
171+ ) -> None :
172+ """Wrap a low level table or table provider."""
173+
174+ if isinstance (table , Table ):
175+ table = table .table
176+
177+ if not isinstance (table , (_InternalRawTable , _InternalTableProvider )):
178+ raise TypeError (EXPECTED_PROVIDER_MSG )
179+
180+ self ._table = table
181+
182+ def __getattribute__ (self , name : str ) -> Any :
183+ """Restrict provider-specific helpers to compatible tables."""
184+
185+ if name == "__datafusion_table_provider__" :
186+ table = object .__getattribute__ (self , "_table" )
187+ if not hasattr (table , "__datafusion_table_provider__" ):
188+ raise AttributeError (name )
189+ return object .__getattribute__ (self , name )
160190
161191 def __repr__ (self ) -> str :
162192 """Print a string representation of the table."""
163- return self .table . __repr__ ( )
193+ return repr ( self ._table )
164194
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 ))
195+ @property
196+ def table (self ) -> _InternalRawTable | _InternalTableProvider :
197+ """Return the wrapped low level table object."""
198+ return self ._table
199+
200+ @classmethod
201+ def from_dataset (cls , dataset : pa .dataset .Dataset ) -> Table :
202+ """Turn a :mod:`pyarrow.dataset` ``Dataset`` into a :class:`Table`."""
203+
204+ return cls (_InternalRawTable .from_dataset (dataset ))
205+
206+ @classmethod
207+ def from_capsule (cls , capsule : Any ) -> Table :
208+ """Create a :class:`Table` from a PyCapsule exported provider."""
209+
210+ provider = _InternalTableProvider .from_capsule (capsule )
211+ return cls (provider )
212+
213+ @classmethod
214+ def from_dataframe (cls , df : Any ) -> Table :
215+ """Create a :class:`Table` from tabular data."""
216+
217+ from datafusion .dataframe import DataFrame as DataFrameWrapper
218+
219+ dataframe = df if isinstance (df , DataFrameWrapper ) else DataFrameWrapper (df )
220+ return dataframe .into_view ()
221+
222+ @classmethod
223+ def from_view (cls , df : Any ) -> Table :
224+ """Deprecated helper for constructing tables from views."""
225+
226+ from datafusion .dataframe import DataFrame as DataFrameWrapper
227+
228+ if isinstance (df , DataFrameWrapper ):
229+ df = df .df
230+
231+ provider = _InternalTableProvider .from_view (df )
232+ warnings .warn (
233+ "Table.from_view is deprecated; use DataFrame.into_view or "
234+ "Table.from_dataframe instead." ,
235+ category = DeprecationWarning ,
236+ stacklevel = _FROM_VIEW_WARN_STACKLEVEL ,
237+ )
238+ return cls (provider )
169239
170240 @property
171241 def schema (self ) -> pa .Schema :
172242 """Returns the schema associated with this table."""
173- return self .table .schema
243+ return self ._table .schema
174244
175245 @property
176246 def kind (self ) -> str :
177247 """Returns the kind of table."""
178- return self .table .kind
248+ return self ._table .kind
249+
250+ def __datafusion_table_provider__ (self ) -> Any :
251+ """Expose the wrapped provider for FFI integrations."""
252+
253+ exporter = getattr (self ._table , "__datafusion_table_provider__" , None )
254+ if exporter is None :
255+ msg = "Underlying object does not export __datafusion_table_provider__()"
256+ raise AttributeError (msg )
257+ return exporter ()
179258
180259
181260class CatalogProvider (ABC ):
@@ -233,15 +312,15 @@ def table(self, name: str) -> Table | None:
233312 ...
234313
235314 def register_table ( # noqa: B027
236- self , name : str , table : Table | TableProvider | TableProviderExportable
315+ self , name : str , table : Table | TableProviderExportable | Any
237316 ) -> None :
238317 """Add a table to this schema.
239318
240319 This method is optional. If your schema provides a fixed list of tables, you do
241320 not need to implement this method.
242321
243322 Objects implementing ``__datafusion_table_provider__`` are also supported
244- and treated as :class:`TableProvider` instances.
323+ and treated as table provider instances.
245324 """
246325
247326 def deregister_table (self , name : str , cascade : bool ) -> None : # noqa: B027
0 commit comments