Skip to content

Commit

Permalink
feat(ir): rewrite and speed up expression repr
Browse files Browse the repository at this point in the history
The expression `__repr__` is rewritten to be less noisy and more performant.
See https://gist.github.com/gforsyth/1d41488a8680d2dd43bff8a07f1f7931 for details.
  • Loading branch information
cpcloud committed Mar 23, 2022
1 parent 63f1382 commit 45ce9b2
Show file tree
Hide file tree
Showing 24 changed files with 1,020 additions and 511 deletions.
2 changes: 1 addition & 1 deletion ibis/backends/base/sql/compiler/query_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ def get_result(self):
return buf.getvalue()


class Select(DML, util.CachedEqMixin):
class Select(DML, util.EqMixin):

"""
A SELECT statement which, after execution, might yield back to the user a
Expand Down
2 changes: 1 addition & 1 deletion ibis/backends/dask/execution/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def execute_window_op(
[
window.preceding is None,
window.following is None,
window._order_by == [],
not window._order_by,
]
):
raise NotImplementedError(
Expand Down
45 changes: 44 additions & 1 deletion ibis/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from typing import Any, Callable, Iterator, Optional, Tuple

from pydantic import BaseModel as PydanticBaseModel
from pydantic import BaseSettings, Field
from pydantic import BaseSettings, Field, validator

__all__ = [
"option_context",
Expand Down Expand Up @@ -37,13 +37,56 @@ class SQL(BaseModel):
)


class Repr(BaseModel):
"""Options controlling expression printing."""

depth: Optional[int] = Field(
default=None,
description="The maximum number of expression nodes to print when repring.", # noqa: E501
)
table_columns: Optional[int] = Field(
default=None,
description="The number of columns to show in leaf table expressions.",
)
query_text_length: int = Field(
default=80,
description="The maximum number of characters to show in the `query` field repr of SQLQueryResult operations.", # noqa: E501
)
show_types: bool = Field(
default=False,
description="Show the inferred type of value expressions in the repr.",
)

@validator("depth")
def depth_gt_zero_or_none(cls, depth: Optional[int]) -> Optional[int]:
if depth is not None and depth <= 0:
raise ValueError("must be None or greater than 0")
return depth

@validator("table_columns")
def table_columns_gt_zero_or_none(
cls,
table_columns: Optional[int],
) -> Optional[int]:
if table_columns is not None and table_columns <= 0:
raise ValueError("must be None or greater than 0")
return table_columns

@validator("query_text_length")
def query_text_length_ge_zero(cls, query_text_length: int) -> int:
if query_text_length < 0:
raise ValueError("must be non-negative")
return query_text_length


class Options(BaseSettings):
"""Ibis configuration options."""

interactive: bool = Field(
default=False,
description="Show the first few rows of computing an expression when in a repl.", # noqa: E501
)
repr: Repr = Field(default=Repr(), description=Repr.__doc__)
verbose: bool = Field(
default=False,
description="Run in verbose mode if [`True`][True]",
Expand Down
27 changes: 9 additions & 18 deletions ibis/expr/datatypes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def _not_constructible(cls: type[DataType]) -> type[DataType]:

@public
@_not_constructible
class DataType(util.CachedEqMixin):
class DataType(util.EqMixin):
"""Base class for all data types.
[`DataType`][ibis.expr.datatypes.DataType] instances are
Expand Down Expand Up @@ -123,24 +123,16 @@ def __repr__(self) -> str:

@cached_property
def _hash(self) -> int:
custom_parts = (getattr(self, slot) for slot in self._fields)
return hash((self.__class__, *custom_parts, self.nullable))
return hash(
(
self.__class__,
*(getattr(self, slot) for slot in self._fields),
)
)

def __hash__(self) -> int:
return self._hash

def equals(
self,
other: typing.Any,
cache: MutableMapping[Hashable, bool] | None = None,
) -> bool:
if not isinstance(other, DataType):
raise TypeError(
'Comparing datatypes to other types is not allowed. Convert '
f'{other!r} to the equivalent DataType instance.'
)
return super().equals(other, cache=cache)

def __component_eq__(
self,
other: DataType,
Expand Down Expand Up @@ -698,7 +690,6 @@ def __init__(
types
Types of the fields of the struct
"""

names = tuple(names)
if not names:
raise ValueError("names must not be empty")
Expand Down Expand Up @@ -731,7 +722,7 @@ def from_tuples(
Struct data type instance
"""
names, types = zip(*pairs)
return cls(list(names), list(map(dtype, types)), nullable=nullable)
return cls(names, types, nullable=nullable)

@classmethod
def from_dict(
Expand All @@ -752,7 +743,7 @@ def from_dict(
Struct data type instance
"""
names, types = pairs.keys(), pairs.values()
return cls(list(names), list(map(dtype, types)), nullable=nullable)
return cls(names, types, nullable=nullable)

@property
def pairs(self) -> Mapping[str, DataType]:
Expand Down
Loading

0 comments on commit 45ce9b2

Please sign in to comment.