Find initializers of attributes in a class body. If provided, the skip arg should be a callable which will return whether to skip generating a default for an attribute. It will be passed the name of the attribute and the corresponding AssignmentStmt.
(
builder: IRBuilder, cdef: ClassDef, skip: Callable[[str, AssignmentStmt], bool] | None = None
)
| 741 | |
| 742 | |
| 743 | def find_attr_initializers( |
| 744 | builder: IRBuilder, cdef: ClassDef, skip: Callable[[str, AssignmentStmt], bool] | None = None |
| 745 | ) -> tuple[set[str], list[tuple[AssignmentStmt, str]]]: |
| 746 | """Find initializers of attributes in a class body. |
| 747 | |
| 748 | If provided, the skip arg should be a callable which will return whether |
| 749 | to skip generating a default for an attribute. It will be passed the name of |
| 750 | the attribute and the corresponding AssignmentStmt. |
| 751 | """ |
| 752 | cls = builder.mapper.type_to_ir[cdef.info] |
| 753 | if cls.builtin_base: |
| 754 | return set(), [] |
| 755 | |
| 756 | attrs_with_defaults = set() |
| 757 | |
| 758 | # Pull out all assignments in classes in the mro so we can initialize them |
| 759 | # TODO: Support nested statements |
| 760 | default_assignments: list[tuple[AssignmentStmt, str]] = [] |
| 761 | for info in reversed(cdef.info.mro): |
| 762 | if info not in builder.mapper.type_to_ir: |
| 763 | continue |
| 764 | for stmt in info.defn.defs.body: |
| 765 | if ( |
| 766 | isinstance(stmt, AssignmentStmt) |
| 767 | and isinstance(stmt.lvalues[0], NameExpr) |
| 768 | and not is_class_var(stmt.lvalues[0]) |
| 769 | and not isinstance(stmt.rvalue, TempNode) |
| 770 | ): |
| 771 | name = stmt.lvalues[0].name |
| 772 | if name == "__slots__": |
| 773 | continue |
| 774 | |
| 775 | if name == "__deletable__": |
| 776 | check_deletable_declaration(builder, cls, stmt.line) |
| 777 | continue |
| 778 | |
| 779 | if skip is not None and skip(name, stmt): |
| 780 | continue |
| 781 | |
| 782 | attr_type = cls.attr_type(name) |
| 783 | |
| 784 | # If the attribute is initialized to None and type isn't optional, |
| 785 | # doesn't initialize it to anything (special case for "# type:" comments). |
| 786 | if isinstance(stmt.rvalue, RefExpr) and stmt.rvalue.fullname == "builtins.None": |
| 787 | if ( |
| 788 | not is_optional_type(attr_type) |
| 789 | and not is_object_rprimitive(attr_type) |
| 790 | and not is_none_rprimitive(attr_type) |
| 791 | ): |
| 792 | continue |
| 793 | |
| 794 | attrs_with_defaults.add(name) |
| 795 | default_assignments.append((stmt, info.module_name)) |
| 796 | |
| 797 | return attrs_with_defaults, default_assignments |
| 798 | |
| 799 | |
| 800 | def generate_attr_defaults_init( |
no test coverage detected
searching dependent graphs…