| # Licensed to the Apache Software Foundation (ASF) under one | |
| # or more contributor license agreements. See the NOTICE file | |
| # distributed with this work for additional information | |
| # regarding copyright ownership. The ASF licenses this file | |
| # to you under the Apache License, Version 2.0 (the | |
| # "License"); you may not use this file except in compliance | |
| # with the License. You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, | |
| # software distributed under the License is distributed on an | |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| # KIND, either express or implied. See the License for the | |
| # specific language governing permissions and limitations | |
| # under the License. | |
| """Data catalog providers.""" | |
| from __future__ import annotations | |
| from abc import ABC, abstractmethod | |
| from typing import TYPE_CHECKING, Any, Protocol | |
| import datafusion._internal as df_internal | |
| if TYPE_CHECKING: | |
| import pyarrow as pa | |
| from datafusion import DataFrame, SessionContext | |
| from datafusion.context import TableProviderExportable | |
| from datafusion.expr import CreateExternalTable | |
| try: | |
| from warnings import deprecated # Python 3.13+ | |
| except ImportError: | |
| from typing_extensions import deprecated # Python 3.12 | |
| __all__ = [ | |
| "Catalog", | |
| "CatalogList", | |
| "CatalogProvider", | |
| "CatalogProviderList", | |
| "Schema", | |
| "SchemaProvider", | |
| "Table", | |
| ] | |
| class CatalogList: | |
| """DataFusion data catalog list.""" | |
| def __init__(self, catalog_list: df_internal.catalog.RawCatalogList) -> None: | |
| """This constructor is not typically called by the end user.""" | |
| self.catalog_list = catalog_list | |
| def __repr__(self) -> str: | |
| """Print a string representation of the catalog list.""" | |
| return self.catalog_list.__repr__() | |
| def names(self) -> set[str]: | |
| """This is an alias for `catalog_names`.""" | |
| return self.catalog_names() | |
| def catalog_names(self) -> set[str]: | |
| """Returns the list of schemas in this catalog.""" | |
| return self.catalog_list.catalog_names() | |
| @staticmethod | |
| def memory_catalog(ctx: SessionContext | None = None) -> CatalogList: | |
| """Create an in-memory catalog provider list.""" | |
| catalog_list = df_internal.catalog.RawCatalogList.memory_catalog(ctx) | |
| return CatalogList(catalog_list) | |
| def catalog(self, name: str = "datafusion") -> Catalog: | |
| """Returns the catalog with the given ``name`` from this catalog.""" | |
| catalog = self.catalog_list.catalog(name) | |
| return ( | |
| Catalog(catalog) | |
| if isinstance(catalog, df_internal.catalog.RawCatalog) | |
| else catalog | |
| ) | |
| def register_catalog( | |
| self, | |
| name: str, | |
| catalog: Catalog | CatalogProvider | CatalogProviderExportable, | |
| ) -> Catalog | None: | |
| """Register a catalog with this catalog list.""" | |
| if isinstance(catalog, Catalog): | |
| return self.catalog_list.register_catalog(name, catalog.catalog) | |
| return self.catalog_list.register_catalog(name, catalog) | |
| class Catalog: | |
| """DataFusion data catalog.""" | |
| def __init__(self, catalog: df_internal.catalog.RawCatalog) -> None: | |
| """This constructor is not typically called by the end user.""" | |
| self.catalog = catalog | |
| def __repr__(self) -> str: | |
| """Print a string representation of the catalog.""" | |
| return self.catalog.__repr__() | |
| def names(self) -> set[str]: | |
| """This is an alias for `schema_names`.""" | |
| return self.schema_names() | |
| def schema_names(self) -> set[str]: | |
| """Returns the list of schemas in this catalog.""" | |
| return self.catalog.schema_names() | |
| @staticmethod | |
| def memory_catalog(ctx: SessionContext | None = None) -> Catalog: | |
| """Create an in-memory catalog provider.""" | |
| catalog = df_internal.catalog.RawCatalog.memory_catalog(ctx) | |
| return Catalog(catalog) | |
| def schema(self, name: str = "public") -> Schema: | |
| """Returns the database with the given ``name`` from this catalog.""" | |
| schema = self.catalog.schema(name) | |
| return ( | |
| Schema(schema) | |
| if isinstance(schema, df_internal.catalog.RawSchema) | |
| else schema | |
| ) | |
| def register_schema( | |
| self, | |
| name: str, | |
| schema: Schema | SchemaProvider | SchemaProviderExportable, | |
| ) -> Schema | None: | |
| """Register a schema with this catalog.""" | |
| if isinstance(schema, Schema): | |
| return self.catalog.register_schema(name, schema._raw_schema) | |
| return self.catalog.register_schema(name, schema) | |
| def deregister_schema(self, name: str, cascade: bool = True) -> Schema | None: | |
| """Deregister a schema from this catalog.""" | |
| return self.catalog.deregister_schema(name, cascade) | |
| class Schema: | |
| """DataFusion Schema.""" | |
| def __init__(self, schema: df_internal.catalog.RawSchema) -> None: | |
| """This constructor is not typically called by the end user.""" | |
| self._raw_schema = schema | |
| def __repr__(self) -> str: | |
| """Print a string representation of the schema.""" | |
| return self._raw_schema.__repr__() | |
| @staticmethod | |
| def memory_schema(ctx: SessionContext | None = None) -> Schema: | |
| """Create an in-memory schema provider.""" | |
| schema = df_internal.catalog.RawSchema.memory_schema(ctx) | |
| return Schema(schema) | |
| def names(self) -> set[str]: | |
| """This is an alias for `table_names`.""" | |
| return self.table_names() | |
| def table_names(self) -> set[str]: | |
| """Returns the list of all tables in this schema.""" | |
| return self._raw_schema.table_names | |
| def table(self, name: str) -> Table: | |
| """Return the table with the given ``name`` from this schema.""" | |
| return Table(self._raw_schema.table(name)) | |
| def register_table( | |
| self, | |
| name: str, | |
| table: Table | TableProviderExportable | DataFrame | pa.dataset.Dataset, | |
| ) -> None: | |
| """Register a table in this schema.""" | |
| return self._raw_schema.register_table(name, table) | |
| def deregister_table(self, name: str) -> None: | |
| """Deregister a table provider from this schema.""" | |
| return self._raw_schema.deregister_table(name) | |
| def table_exist(self, name: str) -> bool: | |
| """Determines if a table exists in this schema.""" | |
| return self._raw_schema.table_exist(name) | |
| class Table: | |
| """A DataFusion table. | |
| Internally we currently support the following types of tables: | |
| - Tables created using built-in DataFusion methods, such as | |
| reading from CSV or Parquet | |
| - pyarrow datasets | |
| - DataFusion DataFrames, which will be converted into a view | |
| - Externally provided tables implemented with the FFI PyCapsule | |
| interface (advanced) | |
| """ | |
| __slots__ = ("_inner",) | |
| def __init__( | |
| self, | |
| table: Table | TableProviderExportable | DataFrame | pa.dataset.Dataset, | |
| ctx: SessionContext | None = None, | |
| ) -> None: | |
| """Constructor.""" | |
| self._inner = df_internal.catalog.RawTable(table, ctx) | |
| def __repr__(self) -> str: | |
| """Print a string representation of the table.""" | |
| return repr(self._inner) | |
| @staticmethod | |
| @deprecated("Use Table() constructor instead.") | |
| def from_dataset(dataset: pa.dataset.Dataset) -> Table: | |
| """Turn a :mod:`pyarrow.dataset` ``Dataset`` into a :class:`Table`.""" | |
| return Table(dataset) | |
| @property | |
| def schema(self) -> pa.Schema: | |
| """Returns the schema associated with this table.""" | |
| return self._inner.schema | |
| @property | |
| def kind(self) -> str: | |
| """Returns the kind of table.""" | |
| return self._inner.kind | |
| class TableProviderFactory(ABC): | |
| """Abstract class for defining a Python based Table Provider Factory.""" | |
| @abstractmethod | |
| def create(self, cmd: CreateExternalTable) -> Table: | |
| """Create a table using the :class:`CreateExternalTable`.""" | |
| ... | |
| class TableProviderFactoryExportable(Protocol): | |
| """Type hint for object that has __datafusion_table_provider_factory__ PyCapsule. | |
| https://docs.rs/datafusion/latest/datafusion/catalog/trait.TableProviderFactory.html | |
| """ | |
| def __datafusion_table_provider_factory__(self, session: Any) -> object: ... | |
| class CatalogProviderList(ABC): | |
| """Abstract class for defining a Python based Catalog Provider List.""" | |
| @abstractmethod | |
| def catalog_names(self) -> set[str]: | |
| """Set of the names of all catalogs in this catalog list.""" | |
| ... | |
| @abstractmethod | |
| def catalog( | |
| self, name: str | |
| ) -> CatalogProviderExportable | CatalogProvider | Catalog | None: | |
| """Retrieve a specific catalog from this catalog list.""" | |
| ... | |
| def register_catalog( # noqa: B027 | |
| self, name: str, catalog: CatalogProviderExportable | CatalogProvider | Catalog | |
| ) -> None: | |
| """Add a catalog to this catalog list. | |
| This method is optional. If your catalog provides a fixed list of catalogs, you | |
| do not need to implement this method. | |
| """ | |
| class CatalogProviderListExportable(Protocol): | |
| """Type hint for object that has __datafusion_catalog_provider_list__ PyCapsule. | |
| https://docs.rs/datafusion/latest/datafusion/catalog/trait.CatalogProviderList.html | |
| """ | |
| def __datafusion_catalog_provider_list__(self, session: Any) -> object: ... | |
| class CatalogProvider(ABC): | |
| """Abstract class for defining a Python based Catalog Provider.""" | |
| @abstractmethod | |
| def schema_names(self) -> set[str]: | |
| """Set of the names of all schemas in this catalog.""" | |
| ... | |
| @abstractmethod | |
| def schema(self, name: str) -> Schema | None: | |
| """Retrieve a specific schema from this catalog.""" | |
| ... | |
| def register_schema( # noqa: B027 | |
| self, name: str, schema: SchemaProviderExportable | SchemaProvider | Schema | |
| ) -> None: | |
| """Add a schema to this catalog. | |
| This method is optional. If your catalog provides a fixed list of schemas, you | |
| do not need to implement this method. | |
| """ | |
| def deregister_schema(self, name: str, cascade: bool) -> None: # noqa: B027 | |
| """Remove a schema from this catalog. | |
| This method is optional. If your catalog provides a fixed list of schemas, you | |
| do not need to implement this method. | |
| Args: | |
| name: The name of the schema to remove. | |
| cascade: If true, deregister the tables within the schema. | |
| """ | |
| class CatalogProviderExportable(Protocol): | |
| """Type hint for object that has __datafusion_catalog_provider__ PyCapsule. | |
| https://docs.rs/datafusion/latest/datafusion/catalog/trait.CatalogProvider.html | |
| """ | |
| def __datafusion_catalog_provider__(self, session: Any) -> object: ... | |
| class SchemaProvider(ABC): | |
| """Abstract class for defining a Python based Schema Provider.""" | |
| def owner_name(self) -> str | None: | |
| """Returns the owner of the schema. | |
| This is an optional method. The default return is None. | |
| """ | |
| return None | |
| @abstractmethod | |
| def table_names(self) -> set[str]: | |
| """Set of the names of all tables in this schema.""" | |
| ... | |
| @abstractmethod | |
| def table(self, name: str) -> Table | None: | |
| """Retrieve a specific table from this schema.""" | |
| ... | |
| def register_table( # noqa: B027 | |
| self, name: str, table: Table | TableProviderExportable | Any | |
| ) -> None: | |
| """Add a table to this schema. | |
| This method is optional. If your schema provides a fixed list of tables, you do | |
| not need to implement this method. | |
| """ | |
| def deregister_table(self, name: str, cascade: bool) -> None: # noqa: B027 | |
| """Remove a table from this schema. | |
| This method is optional. If your schema provides a fixed list of tables, you do | |
| not need to implement this method. | |
| """ | |
| @abstractmethod | |
| def table_exist(self, name: str) -> bool: | |
| """Returns true if the table exists in this schema.""" | |
| ... | |
| class SchemaProviderExportable(Protocol): | |
| """Type hint for object that has __datafusion_schema_provider__ PyCapsule. | |
| https://docs.rs/datafusion/latest/datafusion/catalog/trait.SchemaProvider.html | |
| """ | |
| def __datafusion_schema_provider__(self, session: Any) -> object: ... |