Skip to content

Commit 015d1ee

Browse files
committed
- cleanup and cache patterns
1 parent 9a2f4e7 commit 015d1ee

File tree

2 files changed

+14
-19
lines changed

2 files changed

+14
-19
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: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,17 +81,13 @@ 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)
@@ -107,9 +103,6 @@ class Annotable(Abstract, metaclass=AnnotableMeta):
107103

108104
__signature__: ClassVar[Signature]
109105
"""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"""
113106

114107
__attributes__: ClassVar[FrozenDict[str, Annotation]]
115108
"""Mapping of the Attribute annotations."""
@@ -123,7 +116,7 @@ class Annotable(Abstract, metaclass=AnnotableMeta):
123116
@classmethod
124117
def __create__(cls, *args: Any, **kwargs: Any) -> Self:
125118
# construct the instance by passing only validated keyword arguments
126-
validated_kwargs = cls.__signature__.validate_nobind_using_dataclass(cls, *args, **kwargs)
119+
validated_kwargs = cls.__signature__.validate_fast(cls, args, kwargs)
127120
return super().__create__(**validated_kwargs)
128121

129122
@classmethod

0 commit comments

Comments
 (0)