Skip to content

Commit cca795d

Browse files
feat(python): When reporting unexpected types in errors, module-qualify the typename (#22390)
1 parent 8306883 commit cca795d

File tree

25 files changed

+120
-53
lines changed

25 files changed

+120
-53
lines changed

py-polars/polars/_utils/getitem.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import polars.functions as F
88
from polars._utils.constants import U32_MAX
99
from polars._utils.slice import PolarsSlice
10-
from polars._utils.various import range_to_slice
10+
from polars._utils.various import qualified_type_name, range_to_slice
1111
from polars.datatypes.classes import (
1212
Boolean,
1313
Int8,
@@ -72,7 +72,7 @@ def get_series_item_by_key(
7272
try:
7373
indices = pl.Series("", key, dtype=Int64)
7474
except TypeError:
75-
msg = f"cannot select elements using Sequence with elements of type {type(first).__name__!r}"
75+
msg = f"cannot select elements using Sequence with elements of type {qualified_type_name(first)!r}"
7676
raise TypeError(msg) from None
7777

7878
indices = _convert_series_to_indices(indices, s.len())
@@ -86,7 +86,7 @@ def get_series_item_by_key(
8686
indices = _convert_np_ndarray_to_indices(key, s.len())
8787
return _select_elements_by_index(s, indices)
8888

89-
msg = f"cannot select elements using key of type {type(key).__name__!r}: {key!r}"
89+
msg = f"cannot select elements using key of type {qualified_type_name(key)!r}: {key!r}"
9090
raise TypeError(msg)
9191

9292

@@ -216,7 +216,7 @@ def _select_columns(
216216
elif isinstance(first, str):
217217
return _select_columns_by_name(df, key) # type: ignore[arg-type]
218218
else:
219-
msg = f"cannot select columns using Sequence with elements of type {type(first).__name__!r}"
219+
msg = f"cannot select columns using Sequence with elements of type {qualified_type_name(first)!r}"
220220
raise TypeError(msg)
221221

222222
elif isinstance(key, pl.Series):
@@ -254,7 +254,9 @@ def _select_columns(
254254
msg = f"cannot select columns using NumPy array of type {key.dtype}"
255255
raise TypeError(msg)
256256

257-
msg = f"cannot select columns using key of type {type(key).__name__!r}: {key!r}"
257+
msg = (
258+
f"cannot select columns using key of type {qualified_type_name(key)!r}: {key!r}"
259+
)
258260
raise TypeError(msg)
259261

260262

@@ -322,7 +324,7 @@ def _select_rows(
322324
return _select_rows_by_index(df, indices)
323325

324326
else:
325-
msg = f"cannot select rows using key of type {type(key).__name__!r}: {key!r}"
327+
msg = f"cannot select rows using key of type {qualified_type_name(key)!r}: {key!r}"
326328
raise TypeError(msg)
327329

328330

py-polars/polars/_utils/various.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,3 +702,31 @@ def display_dot_graph(
702702
plt.imshow(img)
703703
plt.show()
704704
return None
705+
706+
707+
def qualified_type_name(obj: Any, *, qualify_polars: bool = False) -> str:
708+
"""
709+
Return the module-qualified name of the given object as a string.
710+
711+
Parameters
712+
----------
713+
obj
714+
The object to get the qualified name for.
715+
qualify_polars
716+
If False (default), omit the module path for our own (Polars) objects.
717+
"""
718+
if isinstance(obj, type):
719+
module = obj.__module__
720+
name = obj.__name__
721+
else:
722+
module = obj.__class__.__module__
723+
name = obj.__class__.__name__
724+
725+
if (
726+
not module
727+
or module == "builtins"
728+
or (not qualify_polars and module.startswith("polars."))
729+
):
730+
return name
731+
732+
return f"{module}.{name}"

py-polars/polars/convert/general.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@
2121
issue_deprecation_warning,
2222
)
2323
from polars._utils.pycapsule import is_pycapsule, pycapsule_to_frame
24-
from polars._utils.various import _cast_repr_strings_with_schema, issue_warning
24+
from polars._utils.various import (
25+
_cast_repr_strings_with_schema,
26+
issue_warning,
27+
qualified_type_name,
28+
)
2529
from polars._utils.wrap import wrap_df, wrap_s
2630
from polars.datatypes import N_INFER_DEFAULT, Categorical, String
2731
from polars.dependencies import _check_for_pyarrow
@@ -493,7 +497,7 @@ def from_arrow(
493497
)
494498
)
495499

496-
msg = f"expected PyArrow Table, Array, or one or more RecordBatches; got {type(data).__name__!r}"
500+
msg = f"expected PyArrow Table, Array, or one or more RecordBatches; got {qualified_type_name(data)!r}"
497501
raise TypeError(msg)
498502

499503

@@ -616,7 +620,7 @@ def from_pandas(
616620
)
617621
)
618622
else:
619-
msg = f"expected pandas DataFrame or Series, got {type(data).__name__!r}"
623+
msg = f"expected pandas DataFrame or Series, got {qualified_type_name(data)!r}"
620624
raise TypeError(msg)
621625

622626

py-polars/polars/dataframe/frame.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
no_default,
5757
normalize_filepath,
5858
parse_version,
59+
qualified_type_name,
5960
scale_bytes,
6061
warn_null_comparison,
6162
)
@@ -4935,7 +4936,7 @@ def insert_column(self, index: int, column: IntoExprColumn) -> DataFrame:
49354936
cols.insert(index, column) # type: ignore[arg-type]
49364937
self._df = self.select(cols)._df
49374938
else:
4938-
msg = f"column must be a Series or Expr, got {column!r} (type={type(column)})"
4939+
msg = f"column must be a Series or Expr, got {column!r} (type={qualified_type_name(column)})"
49394940
raise TypeError(msg)
49404941
return self
49414942

@@ -7505,19 +7506,21 @@ def join_asof(
75057506
└─────────────┴────────────┴────────────┴──────┘
75067507
"""
75077508
if not isinstance(other, DataFrame):
7508-
msg = f"expected `other` join table to be a DataFrame, got {type(other).__name__!r}"
7509+
msg = f"expected `other` join table to be a DataFrame, not {qualified_type_name(other)!r}"
75097510
raise TypeError(msg)
75107511

75117512
if on is not None:
75127513
if not isinstance(on, (str, pl.Expr)):
7513-
msg = f"expected `on` to be str or Expr, got {type(on).__name__!r}"
7514+
msg = (
7515+
f"expected `on` to be str or Expr, got {qualified_type_name(on)!r}"
7516+
)
75147517
raise TypeError(msg)
75157518
else:
75167519
if not isinstance(left_on, (str, pl.Expr)):
7517-
msg = f"expected `left_on` to be str or Expr, got {type(left_on).__name__!r}"
7520+
msg = f"expected `left_on` to be str or Expr, got {qualified_type_name(left_on)!r}"
75187521
raise TypeError(msg)
75197522
elif not isinstance(right_on, (str, pl.Expr)):
7520-
msg = f"expected `right_on` to be str or Expr, got {type(right_on).__name__!r}"
7523+
msg = f"expected `right_on` to be str or Expr, got {qualified_type_name(right_on)!r}"
75217524
raise TypeError(msg)
75227525

75237526
return (
@@ -7751,7 +7754,7 @@ def join(
77517754
For joining on columns with categorical data, see :class:`polars.StringCache`.
77527755
"""
77537756
if not isinstance(other, DataFrame):
7754-
msg = f"expected `other` join table to be a DataFrame, got {type(other).__name__!r}"
7757+
msg = f"expected `other` join table to be a DataFrame, not {qualified_type_name(other)!r}"
77557758
raise TypeError(msg)
77567759

77577760
return (
@@ -7840,7 +7843,7 @@ def join_where(
78407843
└─────┴─────┴─────┴───────┴──────┴──────┴──────┴─────────────┘
78417844
"""
78427845
if not isinstance(other, DataFrame):
7843-
msg = f"expected `other` join table to be a DataFrame, got {type(other).__name__!r}"
7846+
msg = f"expected `other` join table to be a DataFrame, not {qualified_type_name(other)!r}"
78447847
raise TypeError(msg)
78457848

78467849
return (
@@ -10864,7 +10867,7 @@ def row(
1086410867

1086510868
elif by_predicate is not None:
1086610869
if not isinstance(by_predicate, pl.Expr):
10867-
msg = f"expected `by_predicate` to be an expression, got {type(by_predicate).__name__!r}"
10870+
msg = f"expected `by_predicate` to be an expression, got {qualified_type_name(by_predicate)!r}"
1086810871
raise TypeError(msg)
1086910872
rows = self.filter(by_predicate).rows()
1087010873
n_rows = len(rows)

py-polars/polars/datatypes/convert.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,8 @@ def maybe_cast(el: Any, dtype: PolarsDataType) -> Any:
355355
try:
356356
el = py_type(el) # type: ignore[call-arg]
357357
except Exception:
358-
msg = f"cannot convert Python type {type(el).__name__!r} to {dtype!r}"
358+
from polars._utils.various import qualified_type_name
359+
360+
msg = f"cannot convert Python type {qualified_type_name(el)!r} to {dtype!r}"
359361
raise TypeError(msg) from None
360362
return el

py-polars/polars/datatypes/group.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,11 @@ def __new__(
6262
"""
6363
for it in items:
6464
if not isinstance(it, (DataType, DataTypeClass)):
65-
msg = f"DataTypeGroup items must be dtypes; found {type(it).__name__!r}"
65+
from polars._utils.various import qualified_type_name
66+
67+
msg = f"DataTypeGroup items must be dtypes; found {qualified_type_name(it)!r}"
6668
raise TypeError(msg)
69+
6770
dtype_group = super().__new__(cls, items)
6871
dtype_group._match_base_type = match_base_type
6972
return dtype_group

py-polars/polars/expr/categorical.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from typing import TYPE_CHECKING
44

5+
from polars._utils.various import qualified_type_name
56
from polars._utils.wrap import wrap_expr
67

78
if TYPE_CHECKING:
@@ -181,7 +182,7 @@ def starts_with(self, prefix: str) -> Expr:
181182
└────────┘
182183
"""
183184
if not isinstance(prefix, str):
184-
msg = f"'prefix' must be a string; found {type(prefix)!r}"
185+
msg = f"'prefix' must be a string; found {qualified_type_name(prefix)!r}"
185186
raise TypeError(msg)
186187
return wrap_expr(self._pyexpr.cat_starts_with(prefix))
187188

@@ -234,7 +235,7 @@ def ends_with(self, suffix: str) -> Expr:
234235
└────────┘
235236
"""
236237
if not isinstance(suffix, str):
237-
msg = f"'suffix' must be a string; found {type(suffix)!r}"
238+
msg = f"'suffix' must be a string; found {qualified_type_name(suffix)!r}"
238239
raise TypeError(msg)
239240
return wrap_expr(self._pyexpr.cat_ends_with(suffix))
240241

py-polars/polars/expr/datetime.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from polars._utils.deprecation import deprecate_function, deprecate_nonkeyword_arguments
1010
from polars._utils.parse import parse_into_expression, parse_into_list_of_expressions
1111
from polars._utils.unstable import unstable
12+
from polars._utils.various import qualified_type_name
1213
from polars._utils.wrap import wrap_expr
1314
from polars.datatypes import DTYPE_TEMPORAL_UNITS, Date, Int32
1415

@@ -539,7 +540,7 @@ def combine(self, time: dt.time | Expr, time_unit: TimeUnit = "us") -> Expr:
539540
└─────────────────────────┴─────────────────────────┴─────────────────────┘
540541
"""
541542
if not isinstance(time, (dt.time, pl.Expr)):
542-
msg = f"expected 'time' to be a Python time or Polars expression, found {type(time).__name__!r}"
543+
msg = f"expected 'time' to be a Python time or Polars expression, found {qualified_type_name(time)!r}"
543544
raise TypeError(msg)
544545
time = parse_into_expression(time)
545546
return wrap_expr(self._pyexpr.dt_combine(time, time_unit))

py-polars/polars/expr/string.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
)
1313
from polars._utils.parse import parse_into_expression
1414
from polars._utils.unstable import unstable
15-
from polars._utils.various import find_stacklevel, no_default
15+
from polars._utils.various import find_stacklevel, no_default, qualified_type_name
1616
from polars._utils.wrap import wrap_expr
1717
from polars.datatypes import Date, Datetime, Time, parse_into_dtype
1818
from polars.datatypes.constants import N_INFER_DEFAULT
@@ -847,7 +847,9 @@ def pad_start(self, length: int, fill_char: str = " ") -> Expr:
847847
└──────────────┴──────────────┘
848848
"""
849849
if not isinstance(fill_char, str):
850-
msg = f"pad_start expects a `str`, given a `{type(fill_char)}`"
850+
msg = (
851+
f"pad_start expects a `str`, given a {qualified_type_name(fill_char)!r}"
852+
)
851853
raise TypeError(msg)
852854
return wrap_expr(self._pyexpr.str_pad_start(length, fill_char))
853855

@@ -884,7 +886,7 @@ def pad_end(self, length: int, fill_char: str = " ") -> Expr:
884886
└──────────────┴──────────────┘
885887
"""
886888
if not isinstance(fill_char, str):
887-
msg = f"pad_end expects a `str`, given a `{type(fill_char)}`"
889+
msg = f"pad_end expects a `str`, given a {qualified_type_name(fill_char)!r}"
888890
raise TypeError(msg)
889891
return wrap_expr(self._pyexpr.str_pad_end(length, fill_char))
890892

@@ -1640,7 +1642,7 @@ def extract_groups(self, pattern: str) -> Expr:
16401642
└─────────────────────────────────┴───────────────────────┴──────────┘
16411643
"""
16421644
if not isinstance(pattern, str):
1643-
msg = f"extract_groups expects a `str`, given a `{type(pattern)}`"
1645+
msg = f"extract_groups expects a `str`, given a {qualified_type_name(pattern)!r}"
16441646
raise TypeError(msg)
16451647
return wrap_expr(self._pyexpr.str_extract_groups(pattern))
16461648

py-polars/polars/expr/struct.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from typing import TYPE_CHECKING
55

66
from polars._utils.parse import parse_into_list_of_expressions
7+
from polars._utils.various import qualified_type_name
78
from polars._utils.wrap import wrap_expr
89

910
if TYPE_CHECKING:
@@ -27,7 +28,7 @@ def __getitem__(self, item: str | int) -> Expr:
2728
elif isinstance(item, int):
2829
return wrap_expr(self._pyexpr.struct_field_by_index(item))
2930
else:
30-
msg = f"expected type 'int | str', got {type(item).__name__!r} ({item!r})"
31+
msg = f"expected type 'int | str', got {qualified_type_name(item)!r} ({item!r})"
3132
raise TypeError(msg)
3233

3334
def field(self, name: str | list[str], *more_names: str) -> Expr:

0 commit comments

Comments
 (0)