Right, thanks!
Sure, here’s an example of it coming from pandas:
In [32]: df = pd.DataFrame({'a': [1,2,3], 'b': [4,5,6]})
In [33]: df.rename(columns={'a': 'c', 'b': 'd'})
Out[33]:
c d
0 1 4
1 2 5
2 3 6
In [34]: df.rename(columns={'a': 'c', 'b': ['d']})
Out[34]: ---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
File ~/scratch/.venv/lib/python3.12/site-packages/IPython/core/formatters.py:770, in PlainTextFormatter.__call__(self, obj)
763 stream = StringIO()
764 printer = pretty.RepresentationPrinter(stream, self.verbose,
765 self.max_width, self.newline,
766 max_seq_length=self.max_seq_length,
767 singleton_pprinters=self.singleton_printers,
768 type_pprinters=self.type_printers,
769 deferred_pprinters=self.deferred_printers)
--> 770 printer.pretty(obj)
771 printer.flush()
772 return stream.getvalue()
File ~/scratch/.venv/lib/python3.12/site-packages/IPython/lib/pretty.py:411, in RepresentationPrinter.pretty(self, obj)
400 return meth(obj, self, cycle)
401 if (
402 cls is not object
403 # check if cls defines __repr__
(...) 409 and callable(_safe_getattr(cls, "__repr__", None))
410 ):
--> 411 return _repr_pprint(obj, self, cycle)
413 return _default_pprint(obj, self, cycle)
414 finally:
File ~/scratch/.venv/lib/python3.12/site-packages/IPython/lib/pretty.py:786, in _repr_pprint(obj, p, cycle)
784 """A pprint that just redirects to the normal repr function."""
785 # Find newlines and replace them with p.break_()
--> 786 output = repr(obj)
787 lines = output.splitlines()
788 with p.group():
File ~/scratch/.venv/lib/python3.12/site-packages/pandas/core/frame.py:1214, in DataFrame.__repr__(self)
1211 return buf.getvalue()
1213 repr_params = fmt.get_dataframe_repr_params()
-> 1214 return self.to_string(**repr_params)
File ~/scratch/.venv/lib/python3.12/site-packages/pandas/util/_decorators.py:333, in deprecate_nonkeyword_arguments.<locals>.decorate.<locals>.wrapper(*args, **kwargs)
327 if len(args) > num_allow_args:
328 warnings.warn(
329 msg.format(arguments=_format_argument_list(allow_args)),
330 FutureWarning,
331 stacklevel=find_stack_level(),
332 )
--> 333 return func(*args, **kwargs)
File ~/scratch/.venv/lib/python3.12/site-packages/pandas/core/frame.py:1394, in DataFrame.to_string(self, buf, columns, col_space, header, index, na_rep, formatters, float_format, sparsify, index_names, justify, max_rows, max_cols, show_dimensions, decimal, line_width, min_rows, max_colwidth, encoding)
1375 with option_context("display.max_colwidth", max_colwidth):
1376 formatter = fmt.DataFrameFormatter(
1377 self,
1378 columns=columns,
(...) 1392 decimal=decimal,
1393 )
-> 1394 return fmt.DataFrameRenderer(formatter).to_string(
1395 buf=buf,
1396 encoding=encoding,
1397 line_width=line_width,
1398 )
File ~/scratch/.venv/lib/python3.12/site-packages/pandas/io/formats/format.py:962, in DataFrameRenderer.to_string(self, buf, encoding, line_width)
959 from pandas.io.formats.string import StringFormatter
961 string_formatter = StringFormatter(self.fmt, line_width=line_width)
--> 962 string = string_formatter.to_string()
963 return save_to_buffer(string, buf=buf, encoding=encoding)
File ~/scratch/.venv/lib/python3.12/site-packages/pandas/io/formats/string.py:29, in StringFormatter.to_string(self)
28 def to_string(self) -> str:
---> 29 text = self._get_string_representation()
30 if self.fmt.should_show_dimensions:
31 text = f"{text}{self.fmt.dimensions_info}"
File ~/scratch/.venv/lib/python3.12/site-packages/pandas/io/formats/string.py:44, in StringFormatter._get_string_representation(self)
41 if self.fmt.frame.empty:
42 return self._empty_info_line
---> 44 strcols = self._get_strcols()
46 if self.line_width is None:
47 # no need to wrap around just print the whole frame
48 return self.adj.adjoin(1, *strcols)
File ~/scratch/.venv/lib/python3.12/site-packages/pandas/io/formats/string.py:35, in StringFormatter._get_strcols(self)
34 def _get_strcols(self) -> list[list[str]]:
---> 35 strcols = self.fmt.get_strcols()
36 if self.fmt.is_truncated:
37 strcols = self._insert_dot_separators(strcols)
File ~/scratch/.venv/lib/python3.12/site-packages/pandas/io/formats/format.py:476, in DataFrameFormatter.get_strcols(self)
472 def get_strcols(self) -> list[list[str]]:
473 """
474 Render a DataFrame to a list of columns (as lists of strings).
475 """
--> 476 strcols = self._get_strcols_without_index()
478 if self.index:
479 str_index = self._get_formatted_index(self.tr_frame)
File ~/scratch/.venv/lib/python3.12/site-packages/pandas/io/formats/format.py:729, in DataFrameFormatter._get_strcols_without_index(self)
727 str_columns = [[label] for label in self.header]
728 else:
--> 729 str_columns = self._get_formatted_column_labels(self.tr_frame)
731 if self.show_row_idx_names:
732 for x in str_columns:
File ~/scratch/.venv/lib/python3.12/site-packages/pandas/io/formats/format.py:811, in DataFrameFormatter._get_formatted_column_labels(self, frame)
808 dtypes = self.frame.dtypes
809 need_leadsp = dict(zip(fmt_columns, map(is_numeric_dtype, dtypes)))
810 str_columns = [
--> 811 [" " + x if not self._get_formatter(i) and need_leadsp[x] else x]
812 for i, x in enumerate(fmt_columns)
813 ]
814 # self.str_columns = str_columns
815 return str_columns
File ~/scratch/.venv/lib/python3.12/site-packages/pandas/io/formats/format.py:774, in DataFrameFormatter._get_formatter(self, i)
772 if is_integer(i) and i not in self.columns:
773 i = self.columns[i]
--> 774 return self.formatters.get(i, None)
TypeError: unhashable type: 'list'
Anyway, I think the intention is that the type annotation for column names, even in the stubs, is meant to be accurate
What the pandas stubs often do is to not type deprecated or discouraged behaviour