Skip to content

[mypyc] Fix decorated __new__ in native classes - #22192

Merged
p-sawicki merged 2 commits into
python:masterfrom
rheard:fix-mypyc-1249
Oct 14, 2026
Merged

p-sawicki merged 2 commits into
python:masterfrom
rheard:fix-mypyc-1249

Conversation

@rheard

@rheard rheard commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Fixes mypyc/mypyc#1249.

A decorated method has no entry in ClassIR.method_decls, but the function below the decorators is in ClassIR.methods. For __new__ the code generator used both, and the result didn't work for any caller:

  • The constructor and tp_new called the function below the decorators directly, so a native call skipped the decorators.
  • The decorators were applied to the built-in __new__ function that calls tp_new. Setting the result on the class makes Python replace tp_new, which fails the safety check of that built-in function. Every call that isn't a native constructor call raised TypeError: C.__new__(C) is not safe, use object.__new__().
  • If __new__ takes other arguments than __init__, the generated C didn't compile (too many arguments to function 'CPyDef_C_____new__').
  • In a class with a built-in base class, __new__ in the type dict came from the method table, whose flags don't match the wrapper function of __new__. Instantiating the class crashed.

Comment thread mypyc/irbuild/prepare.py Outdated
Comment on lines +788 to +791
if isinstance(node, Decorator):
# A decorated method has no method declaration. Other methods only lack one
# while their class isn't prepared yet, which can happen in an import cycle.
return "__new__" not in class_ir.method_decls

@p-sawicki p-sawicki Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

looks like there's an edge case where method_decls of the base class doesn't contain __new__ when it's decorated with @staticmethod and the subclass is prepared first.

when compiling the subclass it's assumed that __new__ is decorated but not when compiling the base since @staticmethod is removed so there's an inconsistency.

# multimodule test
[case testDecoratedNewInBaseClassPreparedAfterSubclass]
from other import Base

class Child(Base):
    def __init__(self) -> None:
        raise ValueError("expected init failure")

def construct() -> Child:
    return Child()

[file other.py]
from typing import Any

class Base:
    @staticmethod
    def __new__(cls) -> Any:
        return super().__new__(cls)

    def __init__(self) -> None:
        pass

[file driver.py]
from native import construct
from testutil import assertRaises

with assertRaises(ValueError):
    construct()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch. I just pushed up a commit with a fix for this, though its worth noting this PR is going to conflict with a few of the others I've made. I'll go back and fix real conflicts once once of them gets merged in. Thanks!

@p-sawicki p-sawicki left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

thanks!

@p-sawicki
p-sawicki merged commit bc95983 into python:master Oct 14, 2026
18 checks passed
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.

Decorated __new__ of a native class is called without its decorator, or raises TypeError

2 participants