diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 1a76372d4731..d5d1f862a9d9 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -308,18 +308,21 @@ def report_missing_attribute( def analyze_instance_member_access( name: str, typ: Instance, mx: MemberContext, override_info: TypeInfo | None ) -> Type: - if name == "__init__" and not mx.is_super: - # Accessing __init__ in statically typed code would compromise - # type safety unless used via super(). - mx.fail(message_registry.CANNOT_ACCESS_INIT) - return AnyType(TypeOfAny.from_error) - - # The base object has an instance type. - info = typ.type if override_info: info = override_info + method = info.get_method(name) + + if name == "__init__" and not mx.is_super and not info.is_final: + if not method or not method.is_final: + # Accessing __init__ in statically typed code would compromise + # type safety unless used via super() or the method/class is final. + mx.fail(message_registry.CANNOT_ACCESS_INIT) + return AnyType(TypeOfAny.from_error) + + # The base object has an instance type. + if ( state.find_occurrences and info.name == state.find_occurrences[0] @@ -329,7 +332,6 @@ def analyze_instance_member_access( mx.msg.note("Occurrence of '{}.{}'".format(*state.find_occurrences), mx.context) # Look up the member. First look up the method dictionary. - method = info.get_method(name) if method and not isinstance(method, Decorator): if mx.is_super and not mx.suppress_errors: validate_super_call(method, mx) diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test index ce68b265a3c3..4b0bab45d16c 100644 --- a/test-data/unit/check-final.test +++ b/test-data/unit/check-final.test @@ -1229,3 +1229,24 @@ reveal_type(B() and 42) # N: Revealed type is "Literal[42]?" reveal_type(C() and 42) # N: Revealed type is "__main__.C" [builtins fixtures/bool.pyi] + +[case testCanAccessFinalClassInit] +from typing import final + +@final +class FinalClass: + pass + +def check_final_class() -> None: + new_instance = FinalClass() + new_instance.__init__() + +class FinalInit: + @final + def __init__(self) -> None: + pass + +def check_final_init() -> None: + new_instance = FinalInit() + new_instance.__init__() +[builtins fixtures/tuple.pyi]