You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
isinstance accepts subtypes of tuples as second argument #83731
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee=Noneclosed_at=Nonecreated_at=<Date2020-02-04.18:30:00.188>labels= ['type-bug', '3.9']
title='isinstance accepts subtypes of tuples as second argument'updated_at=<Date2020-02-04.21:31:43.493>user='https://github.com/ewjoachim'
(Not really sure it is a bug, but better informed people might find it worthy still)
isinstance can accept, as second argument, a type or a potentially nested tuple of types. Only tuples are accepted, as opposed to generic iterables. The reasoning behind using a tuple was recently added through a small refactoring from Victor Stinner: 850a4bd
The idea being that it's impossible to make a self referencing tuple nest, and thus the function, which is recursive, doesn't have to deal with infinite recursion.
It's possible to use a tuple subclass, though, and while it doesn't break the function because it reads , the tuple is not explored through the __iter__ interface:
This is the expected result if checking what the tuple contains, but not if iterating the tuple. For me, there's nothing absolutely wrong with the current behaviour, but it feels like we're walking on a fine line, and if for any reason, the isinstance tuple iteration were to start using __iter__ in the future, this example may crash. Solutions could be handling any iterable but explicitely checking for recursion or, as suggested by Victor Stinner, forbidding subclasses of tuple.
Guido van Rossum suggested opening an issue so here it is.
Serhiy: I think at least a test for this particular corner case should be added, so that no implementations of isinstance that use the CPython test suite hit an infinite recursion in that event, I guess?
Though I think it's maybe an open question as to what the correct behavior is. Should we throw on any tuple subclass because there's no reason to support tuple subclasses? Should we switch to using __iter__ when it's defined because there are other cases where the custom behavior of the subclass is defined by its __iter__? Should we make it a guarantee that __iter__ is *never* called?
I can't really think of a reason why defining __iter__ on a tuple subclass would be anything other than a weird hack, so I would probably say either ban tuple subclasses or add a test like so:
deftestIsinstanceIterNeverCalled(self):
"""Guarantee that __iter__ is never called when isinstance is invoked"""classNoIterTuple(tuple):
def__iter__(self): # pragma: nocoverraiseNotImplemented("Cannot call __iter__ on this.")
self.assertTrue(isinstance(1, NoIterTuple((int,))))
It's possible to use a tuple subclass, though, and while it doesn't break the function because it reads , the tuple is not explored through the __iter__ interface: (...)
I don't think that the example from the first message is an issue in isinstance(). Defining __iter__() does not override __getitem__() nor __len__():
---
But isinstance() doesn't call overriden __getitem__() and __len__() methods, only tuple.__getitem__() and tuple.__len__() original methods.
Joachim:
Solutions could be handling any iterable but explicitely checking for recursion or, as suggested by Victor Stinner, forbidding subclasses of tuple.
isinstance() should use PyTuple_CheckExact(): only accept the exact tuple type, and reject any other type.
We can start by deprecating tuple subclasses in Python 3.9.
Paul:
Should we switch to using __iter__ when it's defined because there are other cases where the custom behavior of the subclass is defined by its __iter__?
I just restored an old comment from Guido van Rossum which was lost in a previous refactoring, commit 850a4bd:
if (PyTuple_Check(cls)) {
/* Not a general sequence -- that opens up the road to recursion and stack overflow. */
...
}
--
By the way, nested tuples are accepted:
>>> isinstance(1, ((((int,),),),))
True
I am -0 on nested tuples. The implementation is not so crazy (simple recursive code), so it is maybe ok to keep it.
I've opened the PR with the regression test @pganssle suggested, to make sure a future change to the tuple handling can't start calling __iter__(), __getitem__() or __len__() by accident.
@vasiliyk Please avoid opening PRs for issues that have been stalled for a long time because decisions may change. It is better to first revive the discussion.
Activity
ewjoachim commentedon Feb 4, 2020
(Not really sure it is a bug, but better informed people might find it worthy still)
isinstance can accept, as second argument, a type or a potentially nested tuple of types. Only tuples are accepted, as opposed to generic iterables. The reasoning behind using a tuple was recently added through a small refactoring from Victor Stinner:
850a4bd
The idea being that it's impossible to make a self referencing tuple nest, and thus the function, which is recursive, doesn't have to deal with infinite recursion.
It's possible to use a tuple subclass, though, and while it doesn't break the function because it reads , the tuple is not explored through the __iter__ interface:
This is the expected result if checking what the tuple contains, but not if iterating the tuple. For me, there's nothing absolutely wrong with the current behaviour, but it feels like we're walking on a fine line, and if for any reason, the isinstance tuple iteration were to start using __iter__ in the future, this example may crash. Solutions could be handling any iterable but explicitely checking for recursion or, as suggested by Victor Stinner, forbidding subclasses of tuple.
Guido van Rossum suggested opening an issue so here it is.
A link to the discussion that prompted this:
https://twitter.com/VictorStinner/status/1224744606421655554
serhiy-storchaka commentedon Feb 4, 2020
I do not understand what the problem is.
If the isinstance tuple iteration were to start using __iter__ in the future, it should start to handle all corner cases. But what is wrong now?
pganssle commentedon Feb 4, 2020
Serhiy: I think at least a test for this particular corner case should be added, so that no implementations of
isinstancethat use the CPython test suite hit an infinite recursion in that event, I guess?Though I think it's maybe an open question as to what the correct behavior is. Should we throw on any tuple subclass because there's no reason to support tuple subclasses? Should we switch to using __iter__ when it's defined because there are other cases where the custom behavior of the subclass is defined by its __iter__? Should we make it a guarantee that __iter__ is *never* called?
I can't really think of a reason why defining __iter__ on a tuple subclass would be anything other than a weird hack, so I would probably say either ban tuple subclasses or add a test like so:
vstinner commentedon Feb 4, 2020
Joachim:
I don't think that the example from the first message is an issue in isinstance(). Defining __iter__() does not override __getitem__() nor __len__():
---
Output:
---
2
a
b
---
But isinstance() doesn't call overriden __getitem__() and __len__() methods, only tuple.__getitem__() and tuple.__len__() original methods.
Joachim:
isinstance() should use PyTuple_CheckExact(): only accept the exact tuple type, and reject any other type.
We can start by deprecating tuple subclasses in Python 3.9.
Paul:
I just restored an old comment from Guido van Rossum which was lost in a previous refactoring, commit 850a4bd:
--
By the way, nested tuples are accepted:
I am -0 on nested tuples. The implementation is not so crazy (simple recursive code), so it is maybe ok to keep it.
vasiliyk commentedon Sep 23, 2026
The behaviour is still the same on
main.I've opened the PR with the regression test @pganssle suggested, to make sure a future change to the tuple handling can't start calling
__iter__(),__getitem__()or__len__()by accident.picnixz commentedon Sep 24, 2026
I do not think there is anything to do. We have now recursion guards and this can be detected (and I think we already have them?).
picnixz commentedon Sep 24, 2026
@vasiliyk Please avoid opening PRs for issues that have been stalled for a long time because decisions may change. It is better to first revive the discussion.
picnixz commentedon Sep 24, 2026
(Though in our case, I believe it is ok if it is just about testsl
gh-83731: Test that isinstance() ignores tuple subclass methods (#158000
vasiliyk commentedon Oct 17, 2026
The regression test was added in #158000. methane noted it should not be backported as it only guards against future breakage.
The original behaviour is intended, and recursion guards cover deeply nested tuples.
I think this can be closed. @picnixz