Skip to content

BUG: is_list_like was False for an Enum class so it could not be used as columns - #68035

Open
HuzaifaChaudary wants to merge 1 commit into
pandas-dev:mainfrom
HuzaifaChaudary:fix/is-list-like-enum-class
Open

HuzaifaChaudary wants to merge 1 commit into
pandas-dev:mainfrom
HuzaifaChaudary:fix/is-list-like-enum-class

Conversation

@HuzaifaChaudary

@HuzaifaChaudary HuzaifaChaudary commented Sep 4, 2026 •

Copy link
Copy Markdown

closes #54386

an enum subclass is iterable , list(Color) gives you the members , but is_list_like said
false for it so DataFrame(columns=Color) raised .

the class check in c_is_list_like is there for a good reason , list.__iter__ exists while
iter(list) raises . but that only holds when the metaclass has no __iter__ of its own . an
enum has one through EnumType , which is why iter(Color) works . so it asks the metaclass
now instead of turning down every class .

worth flagging , the shorter version of this does not work . checking type(obj) looks right
but getattr on a type still walks the metaclass , so Color.RED comes back list like , and
a member is a scalar not a collection . there is a test pinning that .

i compared the old check , the new one and what iter() really does over 45 objects . four
answers change and all four were wrong before , enum classes and anything else with an
iterable metaclass . nothing else moves .

89905 tests pass across dtypes , frame , series , indexes , reshape , groupby , apply ,
indexing , tools and base . both new tests fail with the change taken out .


disclosure , as the automated contributions policy asks for . tool is claude code , model
claude opus 5 . i used it to work through the cause , to write the patch and the tests , and
to help word this description . i went over the result myself and every number quoted above
comes from a run on my own machine . sorry it was not in here when i opened it , i had not
read the policy at that point .

… as columns

an Enum subclass is iterable, list(Color) works, but is_list_like said False and
DataFrame(columns=Color) raised TypeError.

the class check was there because list.__iter__ exists while iter(list) raises. that
is only true when the metaclass has no __iter__ of its own, which an Enum does have,
so the check now asks the metaclass instead of refusing every class.

enum members are unaffected, they stay scalar.
Copilot AI lite review requested due to automatic review settings September 4, 2026 21:29

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The behavioral change is narrowly targeted, matches Python iteration semantics for classes, and is covered by regression tests plus a whatsnew entry.

Pull request overview

Fixes a regression where Enum subclasses (which are iterable via their metaclass) were incorrectly treated as non-list-like, causing DataFrame(columns=MyEnum) / DataFrame(index=MyEnum) to raise TypeError. The change updates the low-level is_list_like implementation to treat classes as list-like only when their metaclass provides __iter__, matching Python’s actual iter() behavior for class objects.

Changes:

  • Adjust c_is_list_like to allow iterable classes when their metaclass defines __iter__ (e.g., EnumType).
  • Add regression tests ensuring Enum classes are list-like but Enum members remain non-list-like scalars.
  • Document the fix in the v3.1.0 whatsnew notes.
File summaries
File Description
pandas/tests/frame/test_constructors.py Adds a regression test that DataFrame(columns=EnumSubclass) constructs with the expected enum-member columns.
pandas/tests/dtypes/test_inference.py Adds focused tests for is_list_like behavior on enum classes vs enum members and on regular classes.
pandas/_libs/lib.pyx Updates core list-likeness detection to consult the metaclass __iter__ when the object is a class.
doc/source/whatsnew/v3.1.0.rst Adds a whatsnew entry describing the bug fix and linking to the issue.
Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread pandas/_libs/lib.pyx
# a class only iterates if its metaclass says so. `list.__iter__` exists but
# `iter(list)` fails, while an Enum subclass is iterable through EnumType
and not (isinstance(obj, type)
and getattr(type(obj), "__iter__", None) is None)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how does this impact perf in regular cases? would it be simpler just to check directly for Enum?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

measured both, on this branch built from source against the same tree with only this hunk
reverted. lib.is_list_like, min of 7 runs of 2,000,000 calls, ns per call:

input base this branch delta same-build noise
[1, 2, 3] 9.32 9.26 -0.06 +0.25
(1, 2, 3) 54.69 56.02 +1.33 -1.49
{"a": 1} 53.51 54.98 +1.47 +0.04
{1, 2} 53.92 54.77 +0.85 +1.01
np.arange(10) 8.78 8.51 -0.27 -0.12
Series 107.80 108.33 +0.53 +0.89
Index 84.34 84.53 +0.19 +0.44
"abc" 30.48 31.18 +0.70 +0.49
3 15.31 15.00 -0.31 +0.03
None 15.47 15.05 -0.42 -0.39
np.float64(1.5) 15.31 15.38 +0.07 -0.01
int (a class) 15.35 15.63 +0.28 +0.40

the last column is the same binary measured twice with nothing changed, and it spans -1.49 to
+1.01, so every delta in the middle column is inside the noise. that matches what the
expression does: for anything that is not a class, isinstance(obj, type) is False and the
added getattr never runs, and that isinstance call was already there on the base. the only
input that does more work than before is a class that has __iter__, which is the int row.

on checking for Enum directly , i built that too:

and not (isinstance(obj, type) and not isinstance(obj, EnumMeta))

it needs from enum import EnumMeta in lib.pyx and it is the same speed. the difference is
what it says about anything else with an iterating metaclass:

class IterMeta(type):
    def __iter__(cls): return iter((1, 2))
class Registry(metaclass=IterMeta): pass

list(Registry)          # [1, 2]
is_list_like(Registry)  # EnumMeta version: False.  this branch: True

Enum is not special here, EnumType.__iter__ is. so the direct check fixes the reported
case and still calls a class that iterates a scalar, which is the same shape as the original
bug. the metaclass version is why list and dict stay False: list.__iter__ exists but
type(list) has no __iter__, so iter(list) raises , which is what the
not isinstance(obj, type) guard was protecting against in the first place.

happy to switch to the EnumMeta form if you would rather keep the rule narrow, it is a
two line change and the tests for list/dict/Plain pass either way. 8099 passed, 113
skipped, 6 xfailed across tests/dtypes/, tests/frame/test_constructors.py and
tests/indexes/test_base.py on the version in the diff.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Awaiting Review PR in the maintainers' court; exempt from staleness

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG (regression): Enum (and subclasses) no longer acceptable parameter type for columns

3 participants