(self, lvalue: NameExpr, rvalue: CallExpr)
| 1088 | return self.get_fullname(expr.callee) in TPDICT_NAMES |
| 1089 | |
| 1090 | def process_typeddict(self, lvalue: NameExpr, rvalue: CallExpr) -> None: |
| 1091 | if self._state == CLASS: |
| 1092 | self.add("\n") |
| 1093 | |
| 1094 | if not isinstance(rvalue.args[0], StrExpr): |
| 1095 | self.annotate_as_incomplete(lvalue) |
| 1096 | return |
| 1097 | |
| 1098 | items: list[tuple[str, Expression]] = [] |
| 1099 | total: Expression | None = None |
| 1100 | if len(rvalue.args) > 1 and rvalue.arg_kinds[1] == ARG_POS: |
| 1101 | if not isinstance(rvalue.args[1], DictExpr): |
| 1102 | self.annotate_as_incomplete(lvalue) |
| 1103 | return |
| 1104 | for attr_name, attr_type in rvalue.args[1].items: |
| 1105 | if not isinstance(attr_name, StrExpr): |
| 1106 | self.annotate_as_incomplete(lvalue) |
| 1107 | return |
| 1108 | items.append((attr_name.value, attr_type)) |
| 1109 | if len(rvalue.args) > 2: |
| 1110 | if rvalue.arg_kinds[2] != ARG_NAMED or rvalue.arg_names[2] != "total": |
| 1111 | self.annotate_as_incomplete(lvalue) |
| 1112 | return |
| 1113 | total = rvalue.args[2] |
| 1114 | else: |
| 1115 | for arg_name, arg in zip(rvalue.arg_names[1:], rvalue.args[1:]): |
| 1116 | if not isinstance(arg_name, str): |
| 1117 | self.annotate_as_incomplete(lvalue) |
| 1118 | return |
| 1119 | if arg_name == "total": |
| 1120 | total = arg |
| 1121 | else: |
| 1122 | items.append((arg_name, arg)) |
| 1123 | p = AliasPrinter(self) |
| 1124 | if any(not key.isidentifier() or keyword.iskeyword(key) for key, _ in items): |
| 1125 | # Keep the call syntax if there are non-identifier or reserved keyword keys. |
| 1126 | self.add(f"{self._indent}{lvalue.name} = {rvalue.accept(p)}\n") |
| 1127 | self._state = VAR |
| 1128 | else: |
| 1129 | bases = self.add_name("typing_extensions.TypedDict") |
| 1130 | # TODO: Add support for generic TypedDicts. Requires `Generic` as base class. |
| 1131 | if total is not None: |
| 1132 | bases += f", total={total.accept(p)}" |
| 1133 | class_def = f"{self._indent}class {lvalue.name}({bases}):" |
| 1134 | if len(items) == 0: |
| 1135 | self.add(f"{class_def} ...\n") |
| 1136 | self._state = EMPTY_CLASS |
| 1137 | else: |
| 1138 | if self._state not in (EMPTY, CLASS): |
| 1139 | self.add("\n") |
| 1140 | self.add(f"{class_def}\n") |
| 1141 | for key, key_type in items: |
| 1142 | self.add(f"{self._indent} {key}: {key_type.accept(p)}\n") |
| 1143 | self._state = CLASS |
| 1144 | |
| 1145 | def annotate_as_incomplete(self, lvalue: NameExpr) -> None: |
| 1146 | incomplete = self.add_name("_typeshed.Incomplete") |
no test coverage detected