ibis
ibis copied to clipboard
ux: don't repr expressions as part of `SignatureValidationError`
Ibis raises a SignatureValidationError when creation of a new op node fails due to an invalid call. This is a user facing error that may result from passing an invalid type or value to a user-facing ibis method.
Currently this error reprs the args to the function in a few places, which is non-ideal:
- In non-interactive mode, the repr of an expression can take up a ton of vertical space, and isn't helpful to the user diagnosing the error.
- In interactive mode, the repr executes the expression (usually several times) before repring it. This may be both expensive and messes with the formatting of the error.
Ideally our error messages provide hints as to what was expected (type and/or shape), what was received (type and/or shape), but shouldn't need to repr the actual values (except in the case of literal scalars like 1/`"foo"/etc...).
For an example, here's what the user sees when adding an int and string column together:
Non-interactive mode
In [1]: import ibis
In [2]: t = ibis.examples.diamonds.fetch()
In [3]: t.price + t.color # invalid addition
---------------------------------------------------------------------------
SignatureValidationError Traceback (most recent call last)
Cell In[3], line 1
----> 1 t.price + t.color # invalid addition
File ~/Code/ibis/ibis/expr/types/strings.py:1660, in StringValue.__radd__(self, other)
1621 def __radd__(self, other: str | StringValue) -> StringValue:
1622 """Concatenate strings.
1623
1624 Parameters
(...)
1658 └────────────────────────┘
1659 """
-> 1660 return ops.StringConcat((other, self)).to_expr()
File ~/Code/ibis/ibis/common/bases.py:72, in AbstractMeta.__call__(cls, *args, **kwargs)
52 def __call__(cls, *args, **kwargs):
53 """Create a new instance of the class.
54
55 The subclass may override the `__create__` classmethod to change the
(...)
70
71 """
---> 72 return cls.__create__(*args, **kwargs)
File ~/Code/ibis/ibis/common/grounds.py:119, in Annotable.__create__(cls, *args, **kwargs)
116 @classmethod
117 def __create__(cls, *args: Any, **kwargs: Any) -> Self:
118 # construct the instance by passing only validated keyword arguments
--> 119 kwargs = cls.__signature__.validate(cls, args, kwargs)
120 return super().__create__(**kwargs)
File ~/Code/ibis/ibis/common/annotations.py:501, in Signature.validate(self, func, args, kwargs)
498 this[name] = result
500 if errors:
--> 501 raise SignatureValidationError(
502 "{call} has failed due to the following errors:{errors}\n\nExpected signature: {sig}",
503 sig=self,
504 func=func,
505 args=args,
506 kwargs=kwargs,
507 errors=errors,
508 )
510 return this
SignatureValidationError: StringConcat((r0 := DatabaseTable: diamonds
carat float64
cut string
color string
clarity string
depth float64
table float64
price int64
x float64
y float64
z float64
price: r0.price, r0 := DatabaseTable: diamonds
carat float64
cut string
color string
clarity string
depth float64
table float64
price int64
x float64
y float64
z float64
color: r0.color)) has failed due to the following errors:
`arg`: (r0 := DatabaseTable: diamonds
carat float64
cut string
color string
clarity string
depth float64
table float64
price int64
x float64
y float64
z float64
price: r0.price, r0 := DatabaseTable: diamonds
carat float64
cut string
color string
clarity string
depth float64
table float64
price int64
x float64
y float64
z float64
color: r0.color) is not a tuple of coercibles to a Value[String, DataShape]
Expected signature: StringConcat(arg: tuple[Value[String, DataShape], ...])
Interactive Mode
In [5]: t.price + t.color # invalid addition
---------------------------------------------------------------------------
SignatureValidationError Traceback (most recent call last)
Cell In[5], line 1
----> 1 t.price + t.color # invalid addition
File ~/Code/ibis/ibis/expr/types/strings.py:1660, in StringValue.__radd__(self, other)
1621 def __radd__(self, other: str | StringValue) -> StringValue:
1622 """Concatenate strings.
1623
1624 Parameters
(...)
1658 └────────────────────────┘
1659 """
-> 1660 return ops.StringConcat((other, self)).to_expr()
File ~/Code/ibis/ibis/common/bases.py:72, in AbstractMeta.__call__(cls, *args, **kwargs)
52 def __call__(cls, *args, **kwargs):
53 """Create a new instance of the class.
54
55 The subclass may override the `__create__` classmethod to change the
(...)
70
71 """
---> 72 return cls.__create__(*args, **kwargs)
File ~/Code/ibis/ibis/common/grounds.py:119, in Annotable.__create__(cls, *args, **kwargs)
116 @classmethod
117 def __create__(cls, *args: Any, **kwargs: Any) -> Self:
118 # construct the instance by passing only validated keyword arguments
--> 119 kwargs = cls.__signature__.validate(cls, args, kwargs)
120 return super().__create__(**kwargs)
File ~/Code/ibis/ibis/common/annotations.py:501, in Signature.validate(self, func, args, kwargs)
498 this[name] = result
500 if errors:
--> 501 raise SignatureValidationError(
502 "{call} has failed due to the following errors:{errors}\n\nExpected signature: {sig}",
503 sig=self,
504 func=func,
505 args=args,
506 kwargs=kwargs,
507 errors=errors,
508 )
510 return this
SignatureValidationError: StringConcat((┏━━━━━━━┓
┃ price ┃
┡━━━━━━━┩
│ int64 │
├───────┤
│ 326 │
│ 326 │
│ 327 │
│ 334 │
│ 335 │
│ 336 │
│ 336 │
│ 337 │
│ 337 │
│ 338 │
│ … │
└───────┘, ┏━━━━━━━━┓
┃ color ┃
┡━━━━━━━━┩
│ string │
├────────┤
│ E │
│ E │
│ E │
│ I │
│ J │
│ J │
│ I │
│ H │
│ E │
│ H │
│ … │
└────────┘)) has failed due to the following errors:
`arg`: (┏━━━━━━━┓
┃ price ┃
┡━━━━━━━┩
│ int64 │
├───────┤
│ 326 │
│ 326 │
│ 327 │
│ 334 │
│ 335 │
│ 336 │
│ 336 │
│ 337 │
│ 337 │
│ 338 │
│ … │
└───────┘, ┏━━━━━━━━┓
┃ color ┃
┡━━━━━━━━┩
│ string │
├────────┤
│ E │
│ E │
│ E │
│ I │
│ J │
│ J │
│ I │
│ H │
│ E │
│ H │
│ … │
└────────┘) is not a tuple of coercibles to a Value[String, DataShape]
Expected signature: StringConcat(arg: tuple[Value[String, DataShape], ...])