Skip to content

Commit 8e58090

Browse files
committed
- wip cleanup after removal of __call__
1 parent 9a2f4e7 commit 8e58090

File tree

5 files changed

+35
-32
lines changed

5 files changed

+35
-32
lines changed

ibis/common/annotations.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -302,11 +302,13 @@ class Signature(inspect.Signature):
302302
Primarily used in the implementation of `ibis.common.grounds.Annotable`.
303303
"""
304304

305-
__slots__ = ('_cached_params')
305+
__slots__ = ('_patterns', '_dataclass')
306306

307307
def __init__(self, *args, **kwargs):
308308
super().__init__(*args, **kwargs)
309-
self._cached_params = dict(self.parameters) # avoids access via MappingProxyType which is slow compared to dict
309+
# prebuild dict of patterns to avoid slow retrieval via property&MappingProxyType
310+
self._patterns = {k: param.annotation.pattern for k, param in self.parameters.items() if hasattr(param.annotation, 'pattern')}
311+
self._dataclass = self.to_dataclass()
310312

311313
@classmethod
312314
def merge(cls, *signatures, **annotations):
@@ -514,27 +516,27 @@ def validate(self, func, args, kwargs):
514516

515517
return this
516518

517-
def validate_nobind_using_dataclass(self, cls, *args, **kwargs):
519+
def validate_fast(self, func, args, kwargs):
520+
"""Faster validation using internal dataclass to bind args/kwargs to names instead of Signature.bind."""
518521
try:
519-
instance = cls.__dataclass__(*args, **kwargs)
522+
instance = self._dataclass(*args, **kwargs)
520523
except TypeError as err:
521524
raise SignatureValidationError(
522525
"{call} {cause}\n\nExpected signature: {sig}",
523526
sig=self,
524-
func=cls,
527+
func=func,
525528
args=args,
526529
kwargs=kwargs,
527530
) from err
528531

529-
return self.validate_nobind(cls, instance.__dict__)
532+
return self.validate_nobind(func, instance.__dict__)
530533

531534
def validate_nobind(self, func, kwargs):
532535
"""Validate the arguments against the signature without binding."""
533536
this, errors = {}, []
534-
for name, param in self._cached_params.items():
537+
for name, pattern in self._patterns.items():
535538
value = kwargs[name]
536539

537-
pattern = param.annotation.pattern
538540
result = pattern.match(value, this)
539541
if result is NoMatch:
540542
errors.append((name, value, pattern))
@@ -581,12 +583,12 @@ def validate_return(self, func, value):
581583
)
582584

583585
return result
584-
585-
def to_dataclass(self, cls_name: str) -> type:
586+
587+
def to_dataclass(self, cls_name: str = 'SignatureDataclass') -> type:
586588
"""Create a dataclass from this signature.
587589
588590
Later, instantiating a dataclass from arg+kwargs and accessing the resulting __dict__
589-
is much faster (~100x) than using Signature.bind
591+
is much faster (~10-20x) than using Signature.bind
590592
"""
591593
fields = []
592594
for k, v in self.parameters.items():

ibis/common/grounds.py

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -81,24 +81,22 @@ def __new__(metacls, clsname, bases, dct, **kwargs):
8181
signature = Signature.merge(*signatures, **arguments)
8282
argnames = tuple(signature.parameters.keys())
8383

84-
# convert the signature to a dataclass so it can be used later instead of Signature.bind
85-
data_cls = signature.to_dataclass(f"{clsname}DataClass")
86-
8784
namespace.update(
8885
__module__=module,
8986
__qualname__=qualname,
9087
__argnames__=argnames,
9188
__attributes__=attributes,
9289
__match_args__=argnames,
9390
__signature__=signature,
94-
__dataclass__=data_cls,
9591
__slots__=tuple(slots),
9692
)
9793
return super().__new__(metacls, clsname, bases, namespace, **kwargs)
9894

9995
def __or__(self, other):
10096
# required to support `dt.Numeric | dt.Floating` annotation for python<3.10
10197
return Union[self, other]
98+
99+
__call__ = type.__call__
102100

103101

104102
@dataclass_transform()
@@ -107,9 +105,6 @@ class Annotable(Abstract, metaclass=AnnotableMeta):
107105

108106
__signature__: ClassVar[Signature]
109107
"""Signature of the class, containing the Argument annotations."""
110-
111-
__dataclass__: ClassVar[type]
112-
"""Dataclass with identical signature to this class. Used as a faster alternative to Signature.bind"""
113108

114109
__attributes__: ClassVar[FrozenDict[str, Annotation]]
115110
"""Mapping of the Attribute annotations."""
@@ -120,21 +115,22 @@ class Annotable(Abstract, metaclass=AnnotableMeta):
120115
__match_args__: ClassVar[tuple[str, ...]]
121116
"""Names of the arguments to be used for pattern matching."""
122117

123-
@classmethod
124-
def __create__(cls, *args: Any, **kwargs: Any) -> Self:
125-
# construct the instance by passing only validated keyword arguments
126-
validated_kwargs = cls.__signature__.validate_nobind_using_dataclass(cls, *args, **kwargs)
127-
return super().__create__(**validated_kwargs)
118+
#@classmethod
119+
#def __create__(cls, *args: Any, **kwargs: Any) -> Self:
120+
# # construct the instance by passing only validated keyword arguments
121+
# validated_kwargs = cls.__signature__.validate_fast(cls, args, kwargs)
122+
# return super().__create__(**validated_kwargs)
128123

129124
@classmethod
130125
def __recreate__(cls, kwargs: Any) -> Self:
131126
# bypass signature binding by requiring keyword arguments only
132127
kwargs = cls.__signature__.validate_nobind(cls, kwargs)
133128
return super().__create__(**kwargs)
134129

135-
def __init__(self, **kwargs: Any) -> None:
130+
def __init__(self, *args, **kwargs: Any) -> None:
131+
validated_kwargs = self.__signature__.validate_fast(self.__class__, args, kwargs)
136132
# set the already validated arguments
137-
for name, value in kwargs.items():
133+
for name, value in validated_kwargs.items():
138134
object.__setattr__(self, name, value)
139135
# initialize the remaining attributes
140136
for name, field in self.__attributes__.items():
@@ -199,11 +195,12 @@ class Concrete(Immutable, Comparable, Annotable):
199195

200196
__slots__ = ("__args__", "__precomputed_hash__")
201197

202-
def __init__(self, **kwargs: Any) -> None:
198+
def __init__(self, *args, **kwargs: Any) -> None:
199+
validated_kwargs = self.__signature__.validate_fast(self.__class__, args, kwargs)
203200
# collect and set the arguments in a single pass
204201
args = []
205202
for name in self.__argnames__:
206-
value = kwargs[name]
203+
value = validated_kwargs[name]
207204
args.append(value)
208205
object.__setattr__(self, name, value)
209206

ibis/expr/operations/generic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ class ScalarParameter(Scalar):
166166

167167
shape = ds.scalar
168168

169-
def __init__(self, dtype, counter):
169+
def __init__(self, dtype, counter=None):
170170
if counter is None:
171171
counter = next(self._counter)
172172
super().__init__(dtype=dtype, counter=counter)

ibis/expr/operations/relations.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,13 @@ class Field(Value):
9191
shape = ds.columnar
9292

9393
def __init__(self, rel, name):
94-
if name not in rel.schema:
95-
columns_formatted = ", ".join(map(repr, rel.schema.names))
94+
super().__init__(rel=rel, name=name)
95+
if self.name not in self.rel.schema:
96+
columns_formatted = ", ".join(map(repr, self.rel.schema.names))
9697
raise IbisTypeError(
97-
f"Column {name!r} is not found in table. "
98+
f"Column {self.name!r} is not found in table. "
9899
f"Existing columns: {columns_formatted}."
99100
)
100-
super().__init__(rel=rel, name=name)
101101

102102
@attribute
103103
def dtype(self):

ibis/expr/types/generic.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ def type(self) -> dt.DataType:
120120
"""
121121
return self.op().dtype
122122

123+
@property
124+
def dtype(self) -> dt.DataType:
125+
return self.type()
126+
123127
def hash(self) -> ir.IntegerValue:
124128
"""Compute an integer hash value.
125129

0 commit comments

Comments
 (0)