From fe530c83cfab705b0a6ad587f462480de07cfb4d Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Wed, 21 Oct 2026 22:36:10 -0700 Subject: [PATCH 1/2] Avoid unnecessary setattr lookups, ignore object.__setattr__ This makes mypy 10% faster on the colour-science/colour codebase --- mypy/subtypes.py | 11 ++++++++--- test-data/unit/check-protocols.test | 26 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index d9c1b47c317f..10e9ecddaf04 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -1581,7 +1581,6 @@ def get_member_flags(name: str, itype: Instance, class_obj: bool = False) -> set """ info = itype.type method = info.get_method(name) - setattr_meth = info.get_method("__setattr__") if method: if isinstance(method, Decorator): if method.var.is_staticmethod or method.var.is_classmethod: @@ -1592,7 +1591,11 @@ def get_member_flags(name: str, itype: Instance, class_obj: bool = False) -> set assert isinstance(method, OverloadedFuncDef) dec = method.items[0] assert isinstance(dec, Decorator) - if dec.var.is_settable_property or setattr_meth: + if ( + dec.var.is_settable_property + or (setattr_meth := info.get_method("__setattr__")) + and (setattr_meth.info.fullname != "builtins.object") + ): flags = {IS_VAR, IS_SETTABLE} if dec.var.setter_type is not None: flags.add(IS_EXPLICIT_SETTER) @@ -1602,7 +1605,9 @@ def get_member_flags(name: str, itype: Instance, class_obj: bool = False) -> set return set() # Just a regular method node = info.get(name) if not node: - if setattr_meth: + if (setattr_meth := info.get_method("__setattr__")) and ( + setattr_meth.info.fullname != "builtins.object" + ): return {IS_SETTABLE} if itype.extra_attrs and name in itype.extra_attrs.attrs: flags = set() diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index 75a0dfe2f5f4..1ccb664e8755 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -1228,6 +1228,32 @@ x = C(42) y = CF(42) # E: Incompatible types in assignment (expression has type "CF", variable has type "P") \ # N: Protocol member P.x expected settable variable, got read-only attribute +[case testDeletablePropertyProtocolWithSetattr] +from typing import Protocol + +class P(Protocol): + attr: int + +class Deletable: + @property + def attr(self) -> int: + return 1 + @attr.deleter + def attr(self) -> None: + pass + +class Settable(Deletable): + def __setattr__(self, name: str, value: int) -> None: + pass + +def consume(value: P) -> None: + pass + +consume(Deletable()) # E: Argument 1 to "consume" has incompatible type "Deletable"; expected "P" \ + # N: Protocol member P.attr expected settable variable, got read-only attribute +consume(Settable()) +[builtins fixtures/property.pyi] + [case testStaticAndClassMethodsInProtocols] from typing import Protocol, Type, TypeVar From 6aae55eec7b06ddb195918e182a58696362dc269 Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Thu, 29 Oct 2026 01:33:59 -0700 Subject: [PATCH 2/2] Restore inherited __setattr__ handling in protocol checks --- mypy/subtypes.py | 10 ++-------- test-data/unit/check-protocols.test | 26 -------------------------- 2 files changed, 2 insertions(+), 34 deletions(-) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 10e9ecddaf04..17b818c49c67 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -1591,11 +1591,7 @@ def get_member_flags(name: str, itype: Instance, class_obj: bool = False) -> set assert isinstance(method, OverloadedFuncDef) dec = method.items[0] assert isinstance(dec, Decorator) - if ( - dec.var.is_settable_property - or (setattr_meth := info.get_method("__setattr__")) - and (setattr_meth.info.fullname != "builtins.object") - ): + if dec.var.is_settable_property or info.get_method("__setattr__"): flags = {IS_VAR, IS_SETTABLE} if dec.var.setter_type is not None: flags.add(IS_EXPLICIT_SETTER) @@ -1605,9 +1601,7 @@ def get_member_flags(name: str, itype: Instance, class_obj: bool = False) -> set return set() # Just a regular method node = info.get(name) if not node: - if (setattr_meth := info.get_method("__setattr__")) and ( - setattr_meth.info.fullname != "builtins.object" - ): + if info.get_method("__setattr__"): return {IS_SETTABLE} if itype.extra_attrs and name in itype.extra_attrs.attrs: flags = set() diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index 1ccb664e8755..75a0dfe2f5f4 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -1228,32 +1228,6 @@ x = C(42) y = CF(42) # E: Incompatible types in assignment (expression has type "CF", variable has type "P") \ # N: Protocol member P.x expected settable variable, got read-only attribute -[case testDeletablePropertyProtocolWithSetattr] -from typing import Protocol - -class P(Protocol): - attr: int - -class Deletable: - @property - def attr(self) -> int: - return 1 - @attr.deleter - def attr(self) -> None: - pass - -class Settable(Deletable): - def __setattr__(self, name: str, value: int) -> None: - pass - -def consume(value: P) -> None: - pass - -consume(Deletable()) # E: Argument 1 to "consume" has incompatible type "Deletable"; expected "P" \ - # N: Protocol member P.attr expected settable variable, got read-only attribute -consume(Settable()) -[builtins fixtures/property.pyi] - [case testStaticAndClassMethodsInProtocols] from typing import Protocol, Type, TypeVar