Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

typing.Protocol implementation means thatisinstance([], collections.abc.Mapping) can sometimes evaluate toTrue #105280

Closed
Assignees
AlexWaygood
Labels
3.12only security fixes3.13bugs and security fixesstdlibPython modules in the Lib dirtopic-typingtype-bugAn unexpected behavior, bug, or error
@AlexWaygood

Description

@AlexWaygood

(In very specific circumstances)

Bug report

Due to some changes that have been made to the implementation oftyping.Protocol in Python 3.12, whether or notisinstance([], collections.abc.Mapping) evaluates toTrue orFalse now depends on precisely when and if garbage collection happens.

Minimal repro:

>>>import gc, collections.abc, typing>>> gc.disable()>>>try:...classFoo(collections.abc.Mapping,typing.Protocol):pass...exceptTypeError:...pass...>>>isinstance([], collections.abc.Mapping)True

Why does this happen?! It's becauseFoo is an illegal class, soTypeError is raised during class initialisation here:

cpython/Lib/typing.py

Lines 1905 to 1912 ince558e6

# ... otherwise check consistency of bases, and prohibit instantiation.
forbaseincls.__bases__:
ifnot (basein (object,Generic)or
base.__module__in_PROTO_ALLOWLISTand
base.__name__in_PROTO_ALLOWLIST[base.__module__]or
issubclass(base,Generic)andgetattr(base,'_is_protocol',False)):
raiseTypeError('Protocols can only inherit from other'
' protocols, got %r'%base)

TypeError being raised here means thatFoo never has__protocol_attrs__ set on it.But, the error is raisedafter class construction, meaning that if garbage collection happens at the wrong time, a reference toFoo is still retained incollections.abc.Mapping.__subclasses__(). This then creates problems in theisinstance() check, because of some behaviour in theabc module where it iterates through all of the subclasses ofcollections.abc.Mapping in order to determine whether an object is an instance ofcollections.abc.Mapping.

>>>import gc, collections.abc, typing>>> gc.disable()>>>try:...classFoo(collections.abc.Mapping,typing.Protocol):pass...exceptTypeError:...pass...>>> x= collections.abc.Mapping.__subclasses__()>>> x[<class 'collections.abc.MutableMapping'>, <class '__main__.Foo'>]>>> x[-1].__protocol_attrs__set()

The fix is to raiseTypeError before the class has actually been created, rather than during class initialisation.

Why does this matter?

This might seem like an absurdly specific bug report, but it would be good to see it fixed. It was causing bizarre test failures in thetyping_extensions backport. The issue was that we did this in one test method:

deftest_protocols_bad_subscripts(self):T=TypeVar('T')S=TypeVar('S')withself.assertRaises(TypeError):classP(typing.Mapping[T,S],Protocol[T]):pass

...And that finalP classwasn't being cleaned up by the garbage collector immediately after that test having been run. That then caused an unrelated test elsewhere to fail due to the fact thatisinstance([], collections.abc.Mapping) was evaluating toTrue, and it wasvery hard to figure out why.

Linked PRs

Metadata

Metadata

Assignees

Labels

3.12only security fixes3.13bugs and security fixesstdlibPython modules in the Lib dirtopic-typingtype-bugAn unexpected behavior, bug, or error

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions


    [8]ページ先頭

    ©2009-2025 Movatter.jp