Skip to content

gh-153772: Make abc isinstance() tolerate instances without __class__ - #154149

Merged
JelleZijlstra merged 1 commit into
python:mainfrom
fedonman:fix-gh-153772-abc-instancecheck-no-class
Aug 29, 2026
Merged

gh-153772: Make abc isinstance() tolerate instances without __class__#154149
JelleZijlstra merged 1 commit into
python:mainfrom
fedonman:fix-gh-153772-abc-instancecheck-no-class

Conversation

@fedonman

Copy link
Copy Markdown
Contributor

The built-in isinstance() reads an instance's __class__ with a lookup that suppresses AttributeError and falls back to the object's type, so a value whose __class__ access raises still checks cleanly:

>>> class NoClass:
...     def __getattribute__(self, name):
...         if name == "__class__":
...             raise AttributeError(name)
...         return super().__getattribute__(name)
...
>>> isinstance(NoClass(), int)
False

ABCMeta.__instancecheck__ read __class__ directly instead, so the same object leaked the AttributeError when checked against a collections.abc class:

>>> from collections.abc import Mapping
>>> isinstance(NoClass(), Mapping)
AttributeError: __class__

This makes the abstract base class machinery fall back to type(instance) when __class__ is unavailable, in both the C (Modules/_abc.c) and the pure-Python (Lib/_py_abc.py) implementations, so it matches the built-in isinstance():

>>> isinstance(NoClass(), Mapping)
False

Objects without a __class__ are unusual, but they do turn up in the wild (for example some Qt widgets, as noted in the issue).

…lass__

The built-in isinstance() reads an instance's __class__ with a lookup that
suppresses AttributeError and falls back to the object's type, so
isinstance(obj, int) returns False for an object whose __class__ access
raises. ABCMeta.__instancecheck__ read __class__ directly instead, so
isinstance(obj, Mapping) leaked that AttributeError.

Fall back to type(instance) when __class__ is unavailable, in both the C
and the pure-Python implementations, so the abstract base classes behave
like the built-in isinstance(). Such objects are unusual, but they do turn
up in the wild (for example some Qt widgets).
@python-cla-bot

python-cla-bot Bot commented Jul 19, 2026

Copy link
Copy Markdown

All commit authors signed the Contributor License Agreement.

CLA signed

@fedonman

Copy link
Copy Markdown
Contributor Author

@johnslavik @JelleZijlstra I think this can be merged.

@JelleZijlstra JelleZijlstra added needs backport to 3.13 bugs and security fixes needs backport to 3.14 bugs and security fixes needs backport to 3.15 pre-release feature fixes, bugs and security fixes labels Aug 29, 2026
@JelleZijlstra
JelleZijlstra merged commit 3e245fa into python:main Aug 29, 2026
66 checks passed
@miss-islington-app

Copy link
Copy Markdown

Thanks @fedonman for the PR, and @JelleZijlstra for merging it 🌮🎉.. I'm working now to backport this PR to: 3.13, 3.14, 3.15.
🐍🍒⛏🤖

@bedevere-app

bedevere-app Bot commented Aug 29, 2026

Copy link
Copy Markdown

GH-156541 is a backport of this pull request to the 3.15 branch.

@bedevere-app bedevere-app Bot removed the needs backport to 3.15 pre-release feature fixes, bugs and security fixes label Aug 29, 2026
@bedevere-app

bedevere-app Bot commented Aug 29, 2026

Copy link
Copy Markdown

GH-156542 is a backport of this pull request to the 3.14 branch.

@bedevere-app bedevere-app Bot removed the needs backport to 3.14 bugs and security fixes label Aug 29, 2026
@bedevere-app

bedevere-app Bot commented Aug 29, 2026

Copy link
Copy Markdown

GH-156543 is a backport of this pull request to the 3.13 branch.

@bedevere-app bedevere-app Bot removed the needs backport to 3.13 bugs and security fixes label Aug 29, 2026
JelleZijlstra pushed a commit that referenced this pull request Aug 29, 2026
…class__ (GH-154149) (#156543)

gh-153772: Make abc isinstance() tolerate instances without __class__ (GH-154149)

The built-in isinstance() reads an instance's __class__ with a lookup that
suppresses AttributeError and falls back to the object's type, so
isinstance(obj, int) returns False for an object whose __class__ access
raises. ABCMeta.__instancecheck__ read __class__ directly instead, so
isinstance(obj, Mapping) leaked that AttributeError.

Fall back to type(instance) when __class__ is unavailable, in both the C
and the pure-Python implementations, so the abstract base classes behave
like the built-in isinstance(). Such objects are unusual, but they do turn
up in the wild (for example some Qt widgets).
(cherry picked from commit 3e245fa)

Co-authored-by: Vyron Vasileiadis <hi@fedonman.com>
JelleZijlstra pushed a commit that referenced this pull request Aug 29, 2026
…class__ (GH-154149) (#156542)

gh-153772: Make abc isinstance() tolerate instances without __class__ (GH-154149)

The built-in isinstance() reads an instance's __class__ with a lookup that
suppresses AttributeError and falls back to the object's type, so
isinstance(obj, int) returns False for an object whose __class__ access
raises. ABCMeta.__instancecheck__ read __class__ directly instead, so
isinstance(obj, Mapping) leaked that AttributeError.

Fall back to type(instance) when __class__ is unavailable, in both the C
and the pure-Python implementations, so the abstract base classes behave
like the built-in isinstance(). Such objects are unusual, but they do turn
up in the wild (for example some Qt widgets).
(cherry picked from commit 3e245fa)

Co-authored-by: Vyron Vasileiadis <hi@fedonman.com>
@bedevere-bot

Copy link
Copy Markdown

⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️

Hi! The buildbot AMD64 Arch Linux Asan 3.13 (tierless) has failed when building commit 44a4628.

What do you need to do:

  1. Don't panic.
  2. Check the buildbot page in the devguide if you don't know what the buildbots are or how they work.
  3. Go to the page of the buildbot that failed (https://buildbot.python.org/#/builders/1464/builds/1221) and take a look at the build logs.
  4. Check if the failure is related to this commit (44a4628) or if it is a false positive.
  5. If the failure is related to this commit, please, reflect that on the issue and make a new Pull Request with a fix.

You can take a look at the buildbot page here:

https://buildbot.python.org/#/builders/1464/builds/1221

Failed tests:

  • test_io

Failed subtests:

  • test_daemon_threads_shutdown_stderr_deadlock - test.test_io.CMiscIOTest.test_daemon_threads_shutdown_stderr_deadlock

Summary of the results of the build (if available):

==

Click to see traceback logs
Traceback (most recent call last):
  File "/buildbot/buildarea/3.13.pablogsal-arch-x86_64.asan/build/Lib/test/test_io.py", line 4821, in test_daemon_threads_shutdown_stderr_deadlock
    self.check_daemon_threads_shutdown_deadlock('stderr')
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
  File "/buildbot/buildarea/3.13.pablogsal-arch-x86_64.asan/build/Lib/test/test_io.py", line 4811, in check_daemon_threads_shutdown_deadlock
    self.assertFalse(err.strip('.!'))
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
AssertionError: '=================================================================\n==1958593==ERROR: AddressSanitizer: heap-use-after-free on address 0x7caf3b852898 at pc 0x56125a0c5146 bp 0x7b7f395fe080 sp 0x7b7f395fe070\nREAD of size 8 at 0x7caf3b852898 thread T1\n' is not false


Traceback (most recent call last):
  File "/buildbot/buildarea/3.13.pablogsal-arch-x86_64.asan/build/Lib/test/test_io.py", line 4821, in test_daemon_threads_shutdown_stderr_deadlock
    self.check_daemon_threads_shutdown_deadlock('stderr')
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
  File "/buildbot/buildarea/3.13.pablogsal-arch-x86_64.asan/build/Lib/test/test_io.py", line 4811, in check_daemon_threads_shutdown_deadlock
    self.assertFalse(err.strip('.!'))
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
AssertionError: '=================================================================\n==1945944==ERROR: AddressSanitizer: heap-use-after-free on address 0x7c53b9c52898 at pc 0x5616ac49e146 bp 0x7b23b79fe080 sp 0x7b23b79fe070\nREAD of size 8 at 0x7c53b9c52898 thread T1\n' is not false

hugovk pushed a commit that referenced this pull request Aug 29, 2026
…class__ (GH-154149) (#156541)

gh-153772: Make abc isinstance() tolerate instances without __class__ (GH-154149)

The built-in isinstance() reads an instance's __class__ with a lookup that
suppresses AttributeError and falls back to the object's type, so
isinstance(obj, int) returns False for an object whose __class__ access
raises. ABCMeta.__instancecheck__ read __class__ directly instead, so
isinstance(obj, Mapping) leaked that AttributeError.

Fall back to type(instance) when __class__ is unavailable, in both the C
and the pure-Python implementations, so the abstract base classes behave
like the built-in isinstance(). Such objects are unusual, but they do turn
up in the wild (for example some Qt widgets).
(cherry picked from commit 3e245fa)

Co-authored-by: Vyron Vasileiadis <hi@fedonman.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants