# Buggy code
def collect_attributes(self) -> List[DataclassAttribute]:
    """Collect all attributes declared in the dataclass and its parents.

    All assignments of the form

      a: SomeType
      b: SomeOtherType = ...

    are collected.
    """
    # First, collect attributes belonging to the current class.
    ctx = self._ctx
    cls = self._ctx.cls
    attrs = []  # type: List[DataclassAttribute]
    known_attrs = set()  # type: Set[str]
    for stmt in cls.defs.body:
        # Any assignment that doesn't use the new type declaration
        # syntax can be ignored out of hand.
        if not (isinstance(stmt, AssignmentStmt) and stmt.new_syntax):
            continue

        # a: int, b: str = 1, 'foo' is not supported syntax so we
        # don't have to worry about it.
        lhs = stmt.lvalues[0]
        if not isinstance(lhs, NameExpr):
            continue

        node = cls.info.names[lhs.name].node
        if isinstance(node, PlaceholderNode):
            # This node is not ready yet.
            continue
        assert isinstance(node, Var)

        # x: ClassVar[int] is ignored by dataclasses.
        if node.is_classvar:
            continue

        # x: InitVar[int] is turned into x: int and is removed from the class.
        is_init_var = False
        if (
            isinstance(node.type, Instance)
            and node.type.type.fullname() == "dataclasses.InitVar"
        ):
            is_init_var = True
            node.type = node.type.args[0]

        has_field_call, field_args = _collect_field_args(stmt.rvalue)

        is_in_init_param = field_args.get("init")
        if is_in_init_param is None:
            is_in_init = True
        else:
            is_in_init = bool(ctx.api.parse_bool(is_in_init_param))

        has_default = False
        # Ensure that something like x: int = field() is rejected
        # after an attribute with a default.
        if has_field_call:
            has_default = "default" in field_args or "default_factory" in field_args

        # All other assignments are already type checked.
        elif not isinstance(stmt.rvalue, TempNode):
            has_default = True

        known_attrs.add(lhs.name)
        attrs.append(
            DataclassAttribute(
                name=lhs.name,
                is_in_init=is_in_init,
                is_init_var=is_init_var,
                has_default=has_default,
                line=stmt.line,
                column=stmt.column,
            )
        )

    # Next, collect attributes belonging to any class in the MRO
    # as long as those attributes weren't already collected.  This
    # makes it possible to overwrite attributes in subclasses.
    # copy() because we potentially modify all_attrs below and if this code requires debugging
    # we'll have unmodified attrs laying around.
    all_attrs = attrs.copy()
    init_method = cls.info.get_method("__init__")
    for info in cls.info.mro[1:-1]:
        if "dataclass" not in info.metadata:
            continue

        super_attrs = []
        # Each class depends on the set of attributes in its dataclass ancestors.
        ctx.api.add_plugin_dependency(make_wildcard_trigger(info.fullname()))

        for name, data in info.metadata["dataclass"]["attributes"].items():
            if name not in known_attrs:
                attr = DataclassAttribute.deserialize(info, data)
                if attr.is_init_var and isinstance(init_method, FuncDef):
                    # InitVars are removed from classes so, in order for them to be inherited
                    # properly, we need to re-inject them into subclasses' sym tables here.
                    # To do that, we look 'em up from the parents' __init__.  These variables
                    # are subsequently removed from the sym table at the end of
                    # DataclassTransformer.transform.
                    for arg, arg_name in zip(
                        init_method.arguments, init_method.arg_names
                    ):
                        if arg_name == attr.name:
                            cls.info.names[attr.name] = SymbolTableNode(
                                MDEF, arg.variable
                            )

                known_attrs.add(name)
                super_attrs.append(attr)
            else:
                # How early in the attribute list an attribute appears is determined by the
                # reverse MRO, not simply MRO.
                # See https://docs.python.org/3/library/dataclasses.html#inheritance for
                # details.
                (attr,) = [a for a in all_attrs if a.name == name]
                all_attrs.remove(attr)
                super_attrs.append(attr)
        all_attrs = super_attrs + all_attrs

    # Ensure that arguments without a default don't follow
    # arguments that have a default.
    found_default = False
    for attr in all_attrs:
        # If we find any attribute that is_in_init but that
        # doesn't have a default after one that does have one,
        # then that's an error.
        if found_default and attr.is_in_init and not attr.has_default:
            ctx.api.fail(
                "Attributes without a default cannot follow attributes with one",
                Context(line=attr.line, column=attr.column),
            )

        found_default = found_default or (attr.has_default and attr.is_in_init)

    return all_attrs


# Traceback (KeyError)
/Users/skreft/.virtualenvs/zapship/lib/python3.6/site-packages/aiohttp/client.py:49: error: INTERNAL ERROR -- Please try using mypy master on Github:
https://mypy.rtfd.io/en/latest/common_issues.html#using-a-development-mypy-build
Please report a bug at https://github.com/python/mypy/issues
version: 0.720+dev.262fe3dc4d5d14492c6fd4009d91c555c406ac04
Traceback (most recent call last):
File "/Users/skreft/.virtualenvs/zapship/bin/mypy", line 10, in <module>
sys.exit(console_entry())
File "/Users/skreft/.virtualenvs/zapship/lib/python3.6/site-packages/mypy/__main__.py", line 8, in console_entry
main(None, sys.stdout, sys.stderr)
File "/Users/skreft/.virtualenvs/zapship/lib/python3.6/site-packages/mypy/main.py", line 83, in main
res = build.build(sources, options, None, flush_errors, fscache, stdout, stderr)
File "/Users/skreft/.virtualenvs/zapship/lib/python3.6/site-packages/mypy/build.py", line 164, in build
result = _build(sources, options, alt_lib_path, flush_errors, fscache, stdout, stderr)
File "/Users/skreft/.virtualenvs/zapship/lib/python3.6/site-packages/mypy/build.py", line 224, in _build
graph = dispatch(sources, manager, stdout)
File "/Users/skreft/.virtualenvs/zapship/lib/python3.6/site-packages/mypy/build.py", line 2567, in dispatch
process_graph(graph, manager)
File "/Users/skreft/.virtualenvs/zapship/lib/python3.6/site-packages/mypy/build.py", line 2880, in process_graph
process_stale_scc(graph, scc, manager)
File "/Users/skreft/.virtualenvs/zapship/lib/python3.6/site-packages/mypy/build.py", line 2978, in process_stale_scc
semantic_analysis_for_scc(graph, scc, manager.errors)
File "/Users/skreft/.virtualenvs/zapship/lib/python3.6/site-packages/mypy/newsemanal/semanal_main.py", line 74, in semantic_analysis_for_scc
process_top_levels(graph, scc, patches)
File "/Users/skreft/.virtualenvs/zapship/lib/python3.6/site-packages/mypy/newsemanal/semanal_main.py", line 193, in process_top_levels
patches)
File "/Users/skreft/.virtualenvs/zapship/lib/python3.6/site-packages/mypy/newsemanal/semanal_main.py", line 315, in semantic_analyze_target
active_type=active_type)
File "/Users/skreft/.virtualenvs/zapship/lib/python3.6/site-packages/mypy/newsemanal/semanal.py", line 360, in refresh_partial
self.refresh_top_level(node)
File "/Users/skreft/.virtualenvs/zapship/lib/python3.6/site-packages/mypy/newsemanal/semanal.py", line 371, in refresh_top_level
self.accept(d)
File "/Users/skreft/.virtualenvs/zapship/lib/python3.6/site-packages/mypy/newsemanal/semanal.py", line 4435, in accept
node.accept(self)
File "/Users/skreft/.virtualenvs/zapship/lib/python3.6/site-packages/mypy/nodes.py", line 923, in accept
return visitor.visit_class_def(self)
File "/Users/skreft/.virtualenvs/zapship/lib/python3.6/site-packages/mypy/newsemanal/semanal.py", line 978, in visit_class_def
self.analyze_class(defn)
File "/Users/skreft/.virtualenvs/zapship/lib/python3.6/site-packages/mypy/newsemanal/semanal.py", line 1049, in analyze_class
self.analyze_class_body_common(defn)
File "/Users/skreft/.virtualenvs/zapship/lib/python3.6/site-packages/mypy/newsemanal/semanal.py", line 1058, in analyze_class_body_common
self.apply_class_plugin_hooks(defn)
File "/Users/skreft/.virtualenvs/zapship/lib/python3.6/site-packages/mypy/newsemanal/semanal.py", line 1103, in apply_class_plugin_hooks
hook(ClassDefContext(defn, decorator, self))
File "/Users/skreft/.virtualenvs/zapship/lib/python3.6/site-packages/mypy/plugins/attrs.py", line 214, in attr_class_maker_callback
if info[attr.name].type is None and not ctx.api.final_iteration:
File "/Users/skreft/.virtualenvs/zapship/lib/python3.6/site-packages/mypy/nodes.py", line 2413, in __getitem__
raise KeyError(name)
KeyError: 'total'
/Users/skreft/.virtualenvs/zapship/lib/python3.6/site-packages/aiohttp/client.py:49: : note: use --pdb to drop into pdb

# Fixed code
def collect_attributes(self) -> List[DataclassAttribute]:
    """Collect all attributes declared in the dataclass and its parents.

    All assignments of the form

      a: SomeType
      b: SomeOtherType = ...

    are collected.
    """
    # First, collect attributes belonging to the current class.
    ctx = self._ctx
    cls = self._ctx.cls
    attrs = []  # type: List[DataclassAttribute]
    known_attrs = set()  # type: Set[str]
    for stmt in cls.defs.body:
        # Any assignment that doesn't use the new type declaration
        # syntax can be ignored out of hand.
        if not (isinstance(stmt, AssignmentStmt) and stmt.new_syntax):
            continue

        # a: int, b: str = 1, 'foo' is not supported syntax so we
        # don't have to worry about it.
        lhs = stmt.lvalues[0]
        if not isinstance(lhs, NameExpr):
            continue

        sym = cls.info.names.get(lhs.name)
        if sym is None:
            # This name is likely blocked by a star import. We don't need to defer because
            # defer() is already called by mark_incomplete().
            assert ctx.api.options.new_semantic_analyzer
            continue

        node = sym.node
        if isinstance(node, PlaceholderNode):
            # This node is not ready yet.
            continue
        assert isinstance(node, Var)

        # x: ClassVar[int] is ignored by dataclasses.
        if node.is_classvar:
            continue

        # x: InitVar[int] is turned into x: int and is removed from the class.
        is_init_var = False
        if (
            isinstance(node.type, Instance)
            and node.type.type.fullname() == "dataclasses.InitVar"
        ):
            is_init_var = True
            node.type = node.type.args[0]

        has_field_call, field_args = _collect_field_args(stmt.rvalue)

        is_in_init_param = field_args.get("init")
        if is_in_init_param is None:
            is_in_init = True
        else:
            is_in_init = bool(ctx.api.parse_bool(is_in_init_param))

        has_default = False
        # Ensure that something like x: int = field() is rejected
        # after an attribute with a default.
        if has_field_call:
            has_default = "default" in field_args or "default_factory" in field_args

        # All other assignments are already type checked.
        elif not isinstance(stmt.rvalue, TempNode):
            has_default = True

        known_attrs.add(lhs.name)
        attrs.append(
            DataclassAttribute(
                name=lhs.name,
                is_in_init=is_in_init,
                is_init_var=is_init_var,
                has_default=has_default,
                line=stmt.line,
                column=stmt.column,
            )
        )

    # Next, collect attributes belonging to any class in the MRO
    # as long as those attributes weren't already collected.  This
    # makes it possible to overwrite attributes in subclasses.
    # copy() because we potentially modify all_attrs below and if this code requires debugging
    # we'll have unmodified attrs laying around.
    all_attrs = attrs.copy()
    init_method = cls.info.get_method("__init__")
    for info in cls.info.mro[1:-1]:
        if "dataclass" not in info.metadata:
            continue

        super_attrs = []
        # Each class depends on the set of attributes in its dataclass ancestors.
        ctx.api.add_plugin_dependency(make_wildcard_trigger(info.fullname()))

        for name, data in info.metadata["dataclass"]["attributes"].items():
            if name not in known_attrs:
                attr = DataclassAttribute.deserialize(info, data)
                if attr.is_init_var and isinstance(init_method, FuncDef):
                    # InitVars are removed from classes so, in order for them to be inherited
                    # properly, we need to re-inject them into subclasses' sym tables here.
                    # To do that, we look 'em up from the parents' __init__.  These variables
                    # are subsequently removed from the sym table at the end of
                    # DataclassTransformer.transform.
                    for arg, arg_name in zip(
                        init_method.arguments, init_method.arg_names
                    ):
                        if arg_name == attr.name:
                            cls.info.names[attr.name] = SymbolTableNode(
                                MDEF, arg.variable
                            )

                known_attrs.add(name)
                super_attrs.append(attr)
            else:
                # How early in the attribute list an attribute appears is determined by the
                # reverse MRO, not simply MRO.
                # See https://docs.python.org/3/library/dataclasses.html#inheritance for
                # details.
                (attr,) = [a for a in all_attrs if a.name == name]
                all_attrs.remove(attr)
                super_attrs.append(attr)
        all_attrs = super_attrs + all_attrs

    # Ensure that arguments without a default don't follow
    # arguments that have a default.
    found_default = False
    for attr in all_attrs:
        # If we find any attribute that is_in_init but that
        # doesn't have a default after one that does have one,
        # then that's an error.
        if found_default and attr.is_in_init and not attr.has_default:
            ctx.api.fail(
                "Attributes without a default cannot follow attributes with one",
                Context(line=attr.line, column=attr.column),
            )

        found_default = found_default or (attr.has_default and attr.is_in_init)

    return all_attrs