Skip to content

Commit

Permalink
refactor(config): remove old config code and port to pydantic
Browse files Browse the repository at this point in the history
Configuration is now backed by pydantic

BREAKING CHANGE: `register_option` is no longer supported, please submit option requests upstream
  • Loading branch information
cpcloud committed Feb 9, 2022
1 parent ff34f3e commit 4bb96d1
Show file tree
Hide file tree
Showing 13 changed files with 207 additions and 962 deletions.
33 changes: 1 addition & 32 deletions ibis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,36 +21,6 @@
__all__ = ['api', 'ir', 'util', 'IbisError', 'options']
__all__ += api.__all__

ibis.config.register_option(
'interactive', False, validator=ibis.config.is_bool
)
ibis.config.register_option('verbose', False, validator=ibis.config.is_bool)
ibis.config.register_option('verbose_log', None)
ibis.config.register_option(
'graphviz_repr',
True,
"""\
Whether to render expressions as GraphViz PNGs when repr-ing in a Jupyter
notebook.
""",
validator=ibis.config.is_bool,
)
ibis.config.register_option('default_backend', None)
with ibis.config.config_prefix('context_adjustment'):
ibis.config.register_option(
'time_col',
'time',
'Name of the timestamp col for execution with a timecontext'
'See ibis.expr.timecontext for details.',
validator=ibis.config.is_str,
)
with ibis.config.config_prefix('sql'):
ibis.config.register_option(
'default_limit',
10_000,
'Number of rows to be retrieved for an unlimited table expression',
)

try:
__version__ = importlib_metadata.version(__name__)
except Exception:
Expand Down Expand Up @@ -99,8 +69,7 @@ def __getattr__(name: str) -> BaseBackend:

# The first time a backend is loaded, we register its options, and we set
# it as an attribute of `ibis`, so `__getattr__` is not called again for it
with ibis.config.config_prefix(name):
backend.register_options()
backend.register_options()

setattr(ibis, name, backend)
return backend
26 changes: 19 additions & 7 deletions ibis/backends/base/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@

import abc
import re
from typing import TYPE_CHECKING, Any, Callable, Iterable, Mapping
from typing import TYPE_CHECKING, Any, Callable, ClassVar, Iterable, Mapping

if TYPE_CHECKING:
import pandas as pd

from cached_property import cached_property

import ibis
import ibis.common.exceptions as exc
import ibis.config
import ibis.expr.operations as ops
import ibis.expr.schema as sch
import ibis.expr.types as ir
Expand Down Expand Up @@ -151,6 +153,7 @@ class BaseBackend(abc.ABC):

database_class = Database
table_class: type[ops.DatabaseTable] = ops.DatabaseTable
name: ClassVar[str]

def __init__(self, *args, **kwargs):
self._con_args: tuple[Any] = args
Expand All @@ -170,11 +173,6 @@ def __hash__(self):
def __eq__(self, other):
return self.db_identity == other.db_identity

@property
@abc.abstractmethod
def name(self) -> str:
"""The name of the backend, for example 'sqlite'."""

@cached_property
def db_identity(self) -> str:
"""Return the identity of the database.
Expand Down Expand Up @@ -390,8 +388,22 @@ def version(self) -> str:
The backend version
"""

def register_options(self) -> None:
@classmethod
def register_options(cls) -> None:
"""Register custom backend options."""
options = ibis.config.options
backend_name = cls.name
try:
backend_options = cls.Options()
except AttributeError:
pass
else:
try:
setattr(options, backend_name, backend_options)
except ValueError as e:
raise exc.BackendConfigurationNotRegistered(
backend_name
) from e

def compile(
self,
Expand Down
14 changes: 7 additions & 7 deletions ibis/backends/clickhouse/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import pandas as pd
from clickhouse_driver.client import Client as _DriverClient
from pydantic import Field

import ibis
import ibis.config
Expand All @@ -30,6 +31,12 @@ class Backend(BaseSQLBackend):
table_expr_class = ClickhouseTable
compiler = ClickhouseCompiler

class Options(ibis.config.BaseModel):
temp_db: str = Field(
default="__ibis_tmp",
description="Database to use for temporary objects.",
)

def do_connect(
self,
host: str = 'localhost',
Expand Down Expand Up @@ -92,13 +99,6 @@ def do_connect(
compression=compression,
)

def register_options(self):
ibis.config.register_option(
'temp_db',
'__ibis_tmp',
'Database to use for temporary tables, views. functions, etc.',
)

@property
def version(self) -> str:
self.con.connection.force_connect()
Expand Down
23 changes: 11 additions & 12 deletions ibis/backends/impala/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import numpy as np
import pandas as pd
from pydantic import Field

import ibis.common.exceptions as com
import ibis.config
Expand Down Expand Up @@ -175,6 +176,16 @@ class Backend(BaseSQLBackend):
WebHDFS = WebHDFS
compiler = ImpalaCompiler

class Options(ibis.config.BaseModel):
temp_db: str = Field(
default="__ibis_tmp",
description="Database to use for temporary objects.",
)
temp_hdfs_path: str = Field(
default="/tmp/hdfs",
description="HDFS path for storage of temporary data",
)

def hdfs_connect(self, *args, **kwargs):
return hdfs_connect(*args, **kwargs)

Expand Down Expand Up @@ -281,18 +292,6 @@ def version(self):
cursor.release()
return result

def register_options(self):
ibis.config.register_option(
'temp_db',
'__ibis_tmp',
'Database to use for temporary tables, views. functions, etc.',
)
ibis.config.register_option(
'temp_hdfs_path',
'/tmp/ibis',
'HDFS path for storage of temporary data',
)

def list_databases(self, like=None):
cur = self.raw_sql('SHOW DATABASES')
databases = self._get_list(cur)
Expand Down
13 changes: 10 additions & 3 deletions ibis/backends/impala/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ def isproperty(obj):


class IbisTestEnv:
def __init__(self):
if options.impala is None:
ibis.backends.impala.Backend.register_options()

def items(self):
return [
(name, getattr(self, name))
Expand Down Expand Up @@ -90,9 +94,12 @@ def tmp_db(self):
)
return tmp_db

options.impala.temp_hdfs_path = tmp_dir = os.environ.get(
'IBIS_TEST_TMP_HDFS_DIR', f'/tmp/__ibis_test_{util.guid()}'
)
@property
def tmp_dir(self):
options.impala.temp_hdfs_path = tmp_dir = os.environ.get(
'IBIS_TEST_TMP_HDFS_DIR', f'/tmp/__ibis_test_{util.guid()}'
)
return tmp_dir

@property
def test_data_db(self):
Expand Down
17 changes: 8 additions & 9 deletions ibis/backends/pandas/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from typing import Any, MutableMapping

import pandas as pd
from pydantic import Field

import ibis.common.exceptions as com
import ibis.config
Expand All @@ -18,8 +19,15 @@ class BasePandasBackend(BaseBackend):
Base class for backends based on pandas.
"""

name = "pandas"
backend_table_type = pd.DataFrame

class Options(ibis.config.BaseModel):
enable_trace: bool = Field(
default=False,
description="Enable tracing for execution.",
)

def do_connect(
self,
dictionary: MutableMapping[str, pd.DataFrame],
Expand Down Expand Up @@ -60,15 +68,6 @@ def from_dataframe(self, df, name='df', client=None):
client.dictionary[name] = df
return client.table(name)

def register_options(self):
ibis.config.register_option(
'enable_trace',
False,
f'Whether enable tracing for {self.name} execution. '
'See ibis.{self.name}.trace for details.',
validator=ibis.config.is_bool,
)

@property
def version(self) -> str:
return pd.__version__
Expand Down
14 changes: 14 additions & 0 deletions ibis/common/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,20 @@ class BackendConversionError(IbisError):
"""A backend cannot convert an input to its native type."""


class BackendConfigurationNotRegistered(IbisError):
"""A backend has options but isn't regsitered in ibis/config.py"""

def __init__(self, backend_name: str) -> None:
super().__init__(backend_name)
self.backend_name = backend_name

def __str__(self) -> str:
return (
f"Please register options for the `{self.backend_name}` "
"backend in ibis/config.py"
)


def mark_as_unsupported(f: Callable) -> Callable:
"""Decorate an unsupported method.
Expand Down
Loading

0 comments on commit 4bb96d1

Please sign in to comment.