diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 02947385ca47..b5a577b4b016 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -240,11 +240,12 @@ def _analyze_class(ctx: 'mypy.plugin.ClassDefContext', auto_attribs: bool) -> Li # attributes for all classes have been read, because subclasses can override parents. last_default = False for attribute in attributes: - if attribute.init and not attribute.has_default and last_default: - ctx.api.fail( - "Non-default attributes not allowed after default attributes.", - attribute.context) - last_default = attribute.has_default + if attribute.init: + if not attribute.has_default and last_default: + ctx.api.fail( + "Non-default attributes not allowed after default attributes.", + attribute.context) + last_default |= attribute.has_default return attributes diff --git a/test-data/unit/check-attr.test b/test-data/unit/check-attr.test index d403848c042e..9851509dad15 100644 --- a/test-data/unit/check-attr.test +++ b/test-data/unit/check-attr.test @@ -844,3 +844,16 @@ class A: x: int = attr.ib(factory=list) # E: Incompatible types in assignment (expression has type "List[T]", variable has type "int") y: str = attr.ib(factory=my_factory) # E: Incompatible types in assignment (expression has type "int", variable has type "str") [builtins fixtures/list.pyi] + +[case testAttrsDefaultAndInit] +import attr + +@attr.s +class C: + a = attr.ib(init=False, default=42) + b = attr.ib() # Ok because previous attribute is init=False + c = attr.ib(default=44) + d = attr.ib(init=False) # Ok because this attribute is init=False + e = attr.ib() # E: Non-default attributes not allowed after default attributes. + +[builtins fixtures/bool.pyi]