Repository navigation
Disallow multiple initializations of asyncio tasks and futures #142615
Description
Activity
I'm a little surprised that we can't change
__init__to__new__, but I trust there's working code that we want to support that relies on it -- somehow. (But would that code perhaps also be broken when we disallow a second__init__call?)Just make sure to make the same change in the pure Python code.
And I would love to see an example in the wild that would break by switching to
__new__.I'm a little surprised that we can't change init to new, but I trust there's working code that we want to support that relies on it -- somehow. (But would that code perhaps also be broken when we disallow a second init call?)
I did a similar change of changing
__init__to__new__for struct.Struct a while ago in #78724 but it had to reverted because it broke third party code.Bummer. I wonder what people are doing in the wild. For struct.Struct, the only clue in that issue is this:
reverted, since it broke code for users in the wild who were subclassing struct.Struct.
And reading through the reverted PR, I find #94532 (comment) which shows the problem. (IIUC, the added builtin
__new__doesn't support all possible signatures, which means subclasses would have to override__new__to match their__init__and it should call something likesuper().__new__(format).Okay, so I agree we probably shouldn't change. Sigh. I presume we have this problem with all classes that define both
__new__and__init__.- addedtype-featureA feature request or enhancementA feature request or enhancement
on Dec 12, 2025 - added a commit that references this issue
on Jan 3, 2026 The PR is merged and multiple initializations of asyncio tasks and futures is disallowed now in both pure python and C implementation.
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsDone
Problem
This is a long standing issue of the
_asyncioaccelerator module where_asyncio.Taskand_asyncio.Futuredefinetp_initslot instead oftp_newslot for creating the object. This allows malicious code to re-initialize objects while tasks and futures are executing which can crash the interpreter because internally the fields of tasks and futures are accessed using borrowed references for performance.There have been multiple reports of this where we previously changed the code to use strong references like #126080 but still there are a lot of places where borrowed references to loop is used etc.
IMO changing the code to use strong references is not the solution here, instead we should make tasks and futures immutable at C level so that this issue would not exist in the first place. Avoiding strong references is also important for performance especially in free-threading.
Proposal
Historically
_asyncio.Taskand_asyncio.Futuredefinetp_initrather thantp_new, so we cannot change it to usetp_newas it would break compatibility with subclasses. To preserve backward compatibility I propose to to instead allowtp_initto be called exactly once and if user tries to initialize it again, raise RuntimeError that task cannot be re-initialized. This would prevent all these issues while preserving backwards compatibility and performance.Linked PRs