|
3 | 3 | from __future__ import annotations
|
4 | 4 |
|
5 | 5 | from typing import Iterable, List, cast
|
6 |
| -from typing_extensions import Final |
| 6 | +from typing_extensions import Final, Literal |
7 | 7 |
|
8 | 8 | import mypy.plugin # To avoid circular imports.
|
9 | 9 | from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type
|
@@ -756,20 +756,28 @@ def _add_init(
|
756 | 756 | ctx: mypy.plugin.ClassDefContext,
|
757 | 757 | attributes: list[Attribute],
|
758 | 758 | adder: MethodAdder,
|
759 |
| - method_name: str, |
| 759 | + method_name: Literal["__init__", "__attrs_init__"], |
760 | 760 | ) -> None:
|
761 | 761 | """Generate an __init__ method for the attributes and add it to the class."""
|
762 |
| - # Convert attributes to arguments with kw_only arguments at the end of |
| 762 | + # Convert attributes to arguments with kw_only arguments at the end of |
763 | 763 | # the argument list
|
764 | 764 | pos_args = []
|
765 | 765 | kw_only_args = []
|
| 766 | + sym_table = ctx.cls.info.names |
766 | 767 | for attribute in attributes:
|
767 | 768 | if not attribute.init:
|
768 | 769 | continue
|
769 | 770 | if attribute.kw_only:
|
770 | 771 | kw_only_args.append(attribute.argument(ctx))
|
771 | 772 | else:
|
772 | 773 | pos_args.append(attribute.argument(ctx))
|
| 774 | + |
| 775 | + # If the attribute is Final, present in `__init__` and has |
| 776 | + # no default, make sure it doesn't error later. |
| 777 | + if not attribute.has_default and attribute.name in sym_table: |
| 778 | + sym_node = sym_table[attribute.name].node |
| 779 | + if isinstance(sym_node, Var) and sym_node.is_final: |
| 780 | + sym_node.final_set_in_init = True |
773 | 781 | args = pos_args + kw_only_args
|
774 | 782 | if all(
|
775 | 783 | # We use getattr rather than instance checks because the variable.type
|
|
0 commit comments