Skip to content

Commit 45ce9b2

Browse files
committed
feat(ir): rewrite and speed up expression repr
The expression `__repr__` is rewritten to be less noisy and more performant. See https://gist.github.com/gforsyth/1d41488a8680d2dd43bff8a07f1f7931 for details.
1 parent 63f1382 commit 45ce9b2

24 files changed

+1020
-511
lines changed

ibis/backends/base/sql/compiler/query_builder.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ def get_result(self):
178178
return buf.getvalue()
179179

180180

181-
class Select(DML, util.CachedEqMixin):
181+
class Select(DML, util.EqMixin):
182182

183183
"""
184184
A SELECT statement which, after execution, might yield back to the user a

ibis/backends/dask/execution/window.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def execute_window_op(
6969
[
7070
window.preceding is None,
7171
window.following is None,
72-
window._order_by == [],
72+
not window._order_by,
7373
]
7474
):
7575
raise NotImplementedError(

ibis/config.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from typing import Any, Callable, Iterator, Optional, Tuple
44

55
from pydantic import BaseModel as PydanticBaseModel
6-
from pydantic import BaseSettings, Field
6+
from pydantic import BaseSettings, Field, validator
77

88
__all__ = [
99
"option_context",
@@ -37,13 +37,56 @@ class SQL(BaseModel):
3737
)
3838

3939

40+
class Repr(BaseModel):
41+
"""Options controlling expression printing."""
42+
43+
depth: Optional[int] = Field(
44+
default=None,
45+
description="The maximum number of expression nodes to print when repring.", # noqa: E501
46+
)
47+
table_columns: Optional[int] = Field(
48+
default=None,
49+
description="The number of columns to show in leaf table expressions.",
50+
)
51+
query_text_length: int = Field(
52+
default=80,
53+
description="The maximum number of characters to show in the `query` field repr of SQLQueryResult operations.", # noqa: E501
54+
)
55+
show_types: bool = Field(
56+
default=False,
57+
description="Show the inferred type of value expressions in the repr.",
58+
)
59+
60+
@validator("depth")
61+
def depth_gt_zero_or_none(cls, depth: Optional[int]) -> Optional[int]:
62+
if depth is not None and depth <= 0:
63+
raise ValueError("must be None or greater than 0")
64+
return depth
65+
66+
@validator("table_columns")
67+
def table_columns_gt_zero_or_none(
68+
cls,
69+
table_columns: Optional[int],
70+
) -> Optional[int]:
71+
if table_columns is not None and table_columns <= 0:
72+
raise ValueError("must be None or greater than 0")
73+
return table_columns
74+
75+
@validator("query_text_length")
76+
def query_text_length_ge_zero(cls, query_text_length: int) -> int:
77+
if query_text_length < 0:
78+
raise ValueError("must be non-negative")
79+
return query_text_length
80+
81+
4082
class Options(BaseSettings):
4183
"""Ibis configuration options."""
4284

4385
interactive: bool = Field(
4486
default=False,
4587
description="Show the first few rows of computing an expression when in a repl.", # noqa: E501
4688
)
89+
repr: Repr = Field(default=Repr(), description=Repr.__doc__)
4790
verbose: bool = Field(
4891
default=False,
4992
description="Run in verbose mode if [`True`][True]",

ibis/expr/datatypes/__init__.py

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def _not_constructible(cls: type[DataType]) -> type[DataType]:
6262

6363
@public
6464
@_not_constructible
65-
class DataType(util.CachedEqMixin):
65+
class DataType(util.EqMixin):
6666
"""Base class for all data types.
6767
6868
[`DataType`][ibis.expr.datatypes.DataType] instances are
@@ -123,24 +123,16 @@ def __repr__(self) -> str:
123123

124124
@cached_property
125125
def _hash(self) -> int:
126-
custom_parts = (getattr(self, slot) for slot in self._fields)
127-
return hash((self.__class__, *custom_parts, self.nullable))
126+
return hash(
127+
(
128+
self.__class__,
129+
*(getattr(self, slot) for slot in self._fields),
130+
)
131+
)
128132

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

132-
def equals(
133-
self,
134-
other: typing.Any,
135-
cache: MutableMapping[Hashable, bool] | None = None,
136-
) -> bool:
137-
if not isinstance(other, DataType):
138-
raise TypeError(
139-
'Comparing datatypes to other types is not allowed. Convert '
140-
f'{other!r} to the equivalent DataType instance.'
141-
)
142-
return super().equals(other, cache=cache)
143-
144136
def __component_eq__(
145137
self,
146138
other: DataType,
@@ -698,7 +690,6 @@ def __init__(
698690
types
699691
Types of the fields of the struct
700692
"""
701-
702693
names = tuple(names)
703694
if not names:
704695
raise ValueError("names must not be empty")
@@ -731,7 +722,7 @@ def from_tuples(
731722
Struct data type instance
732723
"""
733724
names, types = zip(*pairs)
734-
return cls(list(names), list(map(dtype, types)), nullable=nullable)
725+
return cls(names, types, nullable=nullable)
735726

736727
@classmethod
737728
def from_dict(
@@ -752,7 +743,7 @@ def from_dict(
752743
Struct data type instance
753744
"""
754745
names, types = pairs.keys(), pairs.values()
755-
return cls(list(names), list(map(dtype, types)), nullable=nullable)
746+
return cls(names, types, nullable=nullable)
756747

757748
@property
758749
def pairs(self) -> Mapping[str, DataType]:

0 commit comments

Comments
 (0)