Parse and validate fields in named tuple class definition. Return a four tuple: * field names * field types * field default values * valid statements or None, if any of the types are not ready.
(
self, defn: ClassDef, is_stub_file: bool
)
| 140 | return False, None |
| 141 | |
| 142 | def check_namedtuple_classdef( |
| 143 | self, defn: ClassDef, is_stub_file: bool |
| 144 | ) -> tuple[list[str], list[Type], dict[str, Expression], list[Statement]] | None: |
| 145 | """Parse and validate fields in named tuple class definition. |
| 146 | |
| 147 | Return a four tuple: |
| 148 | * field names |
| 149 | * field types |
| 150 | * field default values |
| 151 | * valid statements |
| 152 | or None, if any of the types are not ready. |
| 153 | """ |
| 154 | if len(defn.base_type_exprs) > 1: |
| 155 | self.fail("NamedTuple should be a single base", defn) |
| 156 | items: list[str] = [] |
| 157 | types: list[Type] = [] |
| 158 | default_items: dict[str, Expression] = {} |
| 159 | statements: list[Statement] = [] |
| 160 | for stmt in defn.defs.body: |
| 161 | statements.append(stmt) |
| 162 | if not isinstance(stmt, AssignmentStmt): |
| 163 | # Still allow pass or ... (for empty namedtuples). |
| 164 | if isinstance(stmt, PassStmt) or ( |
| 165 | isinstance(stmt, ExpressionStmt) and isinstance(stmt.expr, EllipsisExpr) |
| 166 | ): |
| 167 | continue |
| 168 | # Also allow methods, including decorated ones. |
| 169 | if isinstance(stmt, (Decorator, FuncBase)): |
| 170 | continue |
| 171 | # And docstrings. |
| 172 | if isinstance(stmt, ExpressionStmt) and isinstance(stmt.expr, StrExpr): |
| 173 | continue |
| 174 | statements.pop() |
| 175 | defn.removed_statements.append(stmt) |
| 176 | self.fail(NAMEDTUP_CLASS_ERROR, stmt) |
| 177 | elif len(stmt.lvalues) > 1 or not isinstance(stmt.lvalues[0], NameExpr): |
| 178 | # An assignment, but an invalid one. |
| 179 | statements.pop() |
| 180 | defn.removed_statements.append(stmt) |
| 181 | self.fail(NAMEDTUP_CLASS_ERROR, stmt) |
| 182 | else: |
| 183 | # Append name and type in this case... |
| 184 | name = stmt.lvalues[0].name |
| 185 | items.append(name) |
| 186 | if stmt.type is None: |
| 187 | types.append(AnyType(TypeOfAny.unannotated)) |
| 188 | else: |
| 189 | # We never allow recursive types at function scope. Although it is |
| 190 | # possible to support this for named tuples, it is still tricky, and |
| 191 | # it would be inconsistent with type aliases. |
| 192 | analyzed = self.api.anal_type( |
| 193 | stmt.type, |
| 194 | allow_placeholder=not self.api.is_func_scope(), |
| 195 | prohibit_self_type="NamedTuple item type", |
| 196 | prohibit_special_class_field_types="NamedTuple", |
| 197 | ) |
| 198 | if analyzed is None: |
| 199 | # Something is incomplete. We need to defer this named tuple. |
no test coverage detected