From 177c8ee7b8166b3dcf89c034a676ef5818edbc38 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 30 Jun 2024 00:34:26 +0100 Subject: [PATCH] Fix strict optional handling in attrs plugin (#17451) Fixes https://github.com/python/mypy/issues/13794 Fix is trivial (but unlike for dataclasses, the calls to type ops are scattered, so I simply put the whole hook inside a single `with`). --- mypy/plugins/attrs.py | 17 ++++++++++++++++- test-data/unit/check-plugin-attrs.test | 20 ++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index db976385ee56..b67a285af11d 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -55,6 +55,7 @@ deserialize_and_fixup_type, ) from mypy.server.trigger import make_wildcard_trigger +from mypy.state import state from mypy.typeops import get_type_vars, make_simplified_union, map_type_from_supertype from mypy.types import ( AnyType, @@ -317,9 +318,23 @@ def attr_class_maker_callback( See https://www.attrs.org/en/stable/how-does-it-work.html for information on how attrs works. - If this returns False, some required metadata was not ready yet and we need another + If this returns False, some required metadata was not ready yet, and we need another pass. """ + with state.strict_optional_set(ctx.api.options.strict_optional): + # This hook is called during semantic analysis, but it uses a bunch of + # type-checking ops, so it needs the strict optional set properly. + return attr_class_maker_callback_impl( + ctx, auto_attribs_default, frozen_default, slots_default + ) + + +def attr_class_maker_callback_impl( + ctx: mypy.plugin.ClassDefContext, + auto_attribs_default: bool | None, + frozen_default: bool, + slots_default: bool, +) -> bool: info = ctx.cls.info init = _get_decorator_bool_argument(ctx, "init", True) diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index b96c00730a74..0c653d608187 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -2475,3 +2475,23 @@ class B: reveal_type(B.__hash__) # N: Revealed type is "None" [builtins fixtures/plugin_attrs.pyi] + +[case testAttrsStrictOptionalSetProperly] +from typing import Generic, Optional, TypeVar + +import attr + +T = TypeVar("T") + +@attr.mutable() +class Parent(Generic[T]): + run_type: Optional[int] = None + +@attr.mutable() +class Child(Parent[float]): + pass + +Parent(run_type = None) +c = Child(run_type = None) +reveal_type(c.run_type) # N: Revealed type is "Union[builtins.int, None]" +[builtins fixtures/plugin_attrs.pyi]