|
10 | 10 | from mypy import errorcodes as codes, message_registry, nodes
|
11 | 11 | from mypy.errorcodes import ErrorCode
|
12 | 12 | from mypy.expandtype import expand_type
|
13 |
| -from mypy.message_registry import INVALID_PARAM_SPEC_LOCATION, INVALID_PARAM_SPEC_LOCATION_NOTE |
| 13 | +from mypy.message_registry import ( |
| 14 | + INVALID_PARAM_SPEC_LOCATION, |
| 15 | + INVALID_PARAM_SPEC_LOCATION_NOTE, |
| 16 | + TYPEDDICT_OVERRIDE_MERGE, |
| 17 | +) |
14 | 18 | from mypy.messages import (
|
15 | 19 | MessageBuilder,
|
16 | 20 | format_type,
|
|
25 | 29 | ARG_POS,
|
26 | 30 | ARG_STAR,
|
27 | 31 | ARG_STAR2,
|
| 32 | + MISSING_FALLBACK, |
28 | 33 | SYMBOL_FUNCBASE_TYPES,
|
29 | 34 | ArgKind,
|
30 | 35 | Context,
|
|
43 | 48 | check_arg_names,
|
44 | 49 | get_nongen_builtins,
|
45 | 50 | )
|
46 |
| -from mypy.options import Options |
| 51 | +from mypy.options import INLINE_TYPEDDICT, Options |
47 | 52 | from mypy.plugin import AnalyzeTypeContext, Plugin, TypeAnalyzerPluginInterface
|
48 | 53 | from mypy.semanal_shared import (
|
49 | 54 | SemanticAnalyzerCoreInterface,
|
@@ -1220,10 +1225,45 @@ def visit_tuple_type(self, t: TupleType) -> Type:
|
1220 | 1225 | return TupleType(self.anal_array(t.items, allow_unpack=True), fallback, t.line)
|
1221 | 1226 |
|
1222 | 1227 | def visit_typeddict_type(self, t: TypedDictType) -> Type:
|
1223 |
| - items = { |
1224 |
| - item_name: self.anal_type(item_type) for (item_name, item_type) in t.items.items() |
1225 |
| - } |
1226 |
| - return TypedDictType(items, set(t.required_keys), t.fallback) |
| 1228 | + req_keys = set() |
| 1229 | + items = {} |
| 1230 | + for item_name, item_type in t.items.items(): |
| 1231 | + analyzed = self.anal_type(item_type, allow_required=True) |
| 1232 | + if isinstance(analyzed, RequiredType): |
| 1233 | + if analyzed.required: |
| 1234 | + req_keys.add(item_name) |
| 1235 | + analyzed = analyzed.item |
| 1236 | + else: |
| 1237 | + # Keys are required by default. |
| 1238 | + req_keys.add(item_name) |
| 1239 | + items[item_name] = analyzed |
| 1240 | + if t.fallback.type is MISSING_FALLBACK: # anonymous/inline TypedDict |
| 1241 | + if INLINE_TYPEDDICT not in self.options.enable_incomplete_feature: |
| 1242 | + self.fail( |
| 1243 | + "Inline TypedDict is experimental," |
| 1244 | + " must be enabled with --enable-incomplete-feature=InlineTypedDict", |
| 1245 | + t, |
| 1246 | + ) |
| 1247 | + required_keys = req_keys |
| 1248 | + fallback = self.named_type("typing._TypedDict") |
| 1249 | + for typ in t.extra_items_from: |
| 1250 | + analyzed = self.analyze_type(typ) |
| 1251 | + p_analyzed = get_proper_type(analyzed) |
| 1252 | + if not isinstance(p_analyzed, TypedDictType): |
| 1253 | + if not isinstance(p_analyzed, (AnyType, PlaceholderType)): |
| 1254 | + self.fail("Can only merge-in other TypedDict", t, code=codes.VALID_TYPE) |
| 1255 | + continue |
| 1256 | + for sub_item_name, sub_item_type in p_analyzed.items.items(): |
| 1257 | + if sub_item_name in items: |
| 1258 | + self.fail(TYPEDDICT_OVERRIDE_MERGE.format(sub_item_name), t) |
| 1259 | + continue |
| 1260 | + items[sub_item_name] = sub_item_type |
| 1261 | + if sub_item_name in p_analyzed.required_keys: |
| 1262 | + req_keys.add(sub_item_name) |
| 1263 | + else: |
| 1264 | + required_keys = t.required_keys |
| 1265 | + fallback = t.fallback |
| 1266 | + return TypedDictType(items, required_keys, fallback, t.line, t.column) |
1227 | 1267 |
|
1228 | 1268 | def visit_raw_expression_type(self, t: RawExpressionType) -> Type:
|
1229 | 1269 | # We should never see a bare Literal. We synthesize these raw literals
|
@@ -1761,11 +1801,12 @@ def anal_type(
|
1761 | 1801 | allow_param_spec: bool = False,
|
1762 | 1802 | allow_unpack: bool = False,
|
1763 | 1803 | allow_ellipsis: bool = False,
|
| 1804 | + allow_required: bool = False, |
1764 | 1805 | ) -> Type:
|
1765 | 1806 | if nested:
|
1766 | 1807 | self.nesting_level += 1
|
1767 | 1808 | old_allow_required = self.allow_required
|
1768 |
| - self.allow_required = False |
| 1809 | + self.allow_required = allow_required |
1769 | 1810 | old_allow_ellipsis = self.allow_ellipsis
|
1770 | 1811 | self.allow_ellipsis = allow_ellipsis
|
1771 | 1812 | old_allow_unpack = self.allow_unpack
|
|
0 commit comments