def visit_typeddict_type(self, left: TypedDictType) -> bool: right = self.right if isinstance(right, Instance): return is_subtype(left.fallback, right, self.check_type_parameter) elif isinstance(right, TypedDictType): if not left.names_are_wider_than(right): return False for name, l, r in left.zip(right): if not is_equivalent(l, r, self.check_type_parameter): return False # Non-required key is not compatible with a required key since # indexing may fail unexpectedly if a required key is missing. # Required key is not compatible with a non-required key since # the prior doesn't support 'del' but the latter should support # it. # # NOTE: 'del' support is currently not implemented (#3550). We # don't want to have to change subtyping after 'del' support # lands so here we are anticipating that change. if (name in left.required_keys) != (name in right.required_keys): return False # (NOTE: Fallbacks don't matter.) return True else: return False
def visit_typeddict_type(self, left: TypedDictType) -> bool: right = self.right if isinstance(right, Instance): return is_subtype(left.fallback, right, self.check_type_parameter) elif isinstance(right, TypedDictType): if not left.names_are_wider_than(right): return False for (_, l, r) in left.zip(right): if not is_equivalent(l, r, self.check_type_parameter): return False # (NOTE: Fallbacks don't matter.) return True else: return False
def copy_modified(self, *, fallback: Optional[Instance] = None, item_types: Optional[List[Type]] = None, required_keys: Optional[Set[str]] = None) -> 'TypedDictType': if fallback is None: fallback = self.fallback if item_types is None: items = self.items else: items = OrderedDict(zip(self.items, item_types)) if required_keys is None: required_keys = self.required_keys new_type = TypedDictType(items, required_keys, fallback, self.line, self.column) new_type.allow_extra = getattr(self, 'allow_extra', False) return new_type
def get_typed_dict_type( ctx: FunctionContext, handler_arg_type: ProperType, oas_type: oas_model.OASObjectType, ) -> TypedDictType: if isinstance(handler_arg_type, UnionType): td_type_fallback = next( try_getting_instance_fallback(get_proper_type(x)) for x in handler_arg_type.relevant_items()) else: td_type_fallback = try_getting_instance_fallback(handler_arg_type) assert td_type_fallback is not None return TypedDictType( items=OrderedDict({ oas_prop_name: transform_oas_type(oas_prop_type, handler_arg_type, ctx) for oas_prop_name, oas_prop_type in oas_type.properties.items() }), required_keys=oas_type.required, fallback=td_type_fallback, line=td_type_fallback.line, column=td_type_fallback.column, )
def make_typeddict(api: TypeChecker, fields: 'OrderedDict[str, Type]', required_keys: typing.Set[str]) -> Type: object_type = api.named_generic_type('mypy_extensions._TypedDict', []) typed_dict_type = TypedDictType(fields, required_keys=required_keys, fallback=object_type) return typed_dict_type
def make_typeddict(api: CheckerPluginInterface, fields: "OrderedDict[str, MypyType]", required_keys: Set[str]) -> TypedDictType: object_type = api.named_generic_type("mypy_extensions._TypedDict", []) typed_dict_type = TypedDictType(fields, required_keys=required_keys, fallback=object_type) return typed_dict_type
def visit_typeddict_type(self, left: TypedDictType) -> bool: if isinstance(self.right, TypedDictType): if left.items.keys() != self.right.items.keys(): return False for (_, left_item_type, right_item_type) in left.zip(self.right): if not is_identical_type(left_item_type, right_item_type): return False return True return False
def visit_typeddict_type(self, t: TypedDictType) -> Type: if self.check_recursion(t): return AnyType(TypeOfAny.from_error) items = OrderedDict([(item_name, item_type.accept(self)) for (item_name, item_type) in t.items.items()]) fallback = self.visit_instance(t.fallback, from_fallback=True) assert isinstance(fallback, Instance) return TypedDictType(items, t.required_keys, fallback, t.line, t.column)
def build_typeddict_typeinfo(self, name: str, items: List[str], types: List[Type], required_keys: Set[str]) -> TypeInfo: fallback = self.api.named_type_or_none('mypy_extensions._TypedDict', []) assert fallback is not None info = self.api.basic_new_typeinfo(name, fallback) info.typeddict_type = TypedDictType(OrderedDict(zip(items, types)), required_keys, fallback) return info
def visit_typeddict_type(self, t: TypedDictType) -> Type: items = OrderedDict([(item_name, item_type.accept(self)) for (item_name, item_type) in t.items.items()]) return TypedDictType( items, t.required_keys, # TODO: This appears to be unsafe. cast(Any, t.fallback.accept(self)), t.line, t.column)
def build_typeddict_typeinfo(self, name: str, items: List[str], types: List[Type], required_keys: Set[str]) -> TypeInfo: fallback = (self.api.named_type_or_none('typing.Mapping', [ self.api.named_type('__builtins__.str'), self.api.named_type('__builtins__.object') ]) or self.api.named_type('__builtins__.object')) info = self.api.basic_new_typeinfo(name, fallback) info.typeddict_type = TypedDictType(OrderedDict(zip(items, types)), required_keys, fallback) return info
def build_typeddict_typeinfo(self, name: str, items: List[str], types: List[Type], required_keys: Set[str], line: int) -> TypeInfo: # Prefer typing then typing_extensions if available. fallback = ( self.api.named_type_or_none('typing._TypedDict', []) or self.api.named_type_or_none('typing_extensions._TypedDict', []) or self.api.named_type_or_none('mypy_extensions._TypedDict', [])) assert fallback is not None info = self.api.basic_new_typeinfo(name, fallback, line) info.typeddict_type = TypedDictType(OrderedDict(zip(items, types)), required_keys, fallback) return info
def visit_typeddict_type(self, t: TypedDictType) -> Type: if isinstance(self.s, TypedDictType): for (_, l, r) in self.s.zip(t): if not is_equivalent(l, r): return self.default(self.s) items = OrderedDict([(item_name, s_item_type or t_item_type) for (item_name, s_item_type, t_item_type) in self.s.zipall(t)]) mapping_value_type = join_type_list(list(items.values())) fallback = self.s.create_anonymous_fallback( value_type=mapping_value_type) return TypedDictType(items, fallback) else: return self.default(self.s)
def object( self, ctx: AnalyzeTypeContext, schema: Dict[str, Any], outer: bool = False, **kwargs, ) -> Type: """Generate an annotation for an object, usually a TypedDict.""" properties = schema.get("properties") if properties is None: return named_builtin_type(ctx, "dict") try: fallback = ctx.api.named_type("mypy_extensions._TypedDict", []) except AssertionError: fallback = named_builtin_type(ctx, "dict", []) items, types = zip(*filter( lambda o: o[1] is not None, [ (prop, self.get_type(ctx, subschema)) for prop, subschema in properties.items() if prop not in ["default", "const"] # These are reserved names, # not properties. ], )) required_keys = set(schema.get("required", [])) if outer: # We want to name the outer Type, so that we can support nested # references. Note that this may not be fully supported in mypy # at this time. info = self._build_typeddict_typeinfo(ctx, self.outer_name, list(items), list(types), required_keys) instance = Instance(info, []) td = info.typeddict_type assert td is not None typing_type = td.copy_modified(item_types=list(td.items.values()), fallback=instance) # # Resolve any forward (nested) references to this Type. # if self.forward_refs: # # for fw in self.forward_refs: # fw.resolve(typing_type) return typing_type struct = OrderedDict(zip(items, types)) return TypedDictType(struct, required_keys, fallback)
def visit_typeddict_type(self, template: TypedDictType) -> List[Constraint]: actual = self.actual if isinstance(actual, TypedDictType): res = [] # type: List[Constraint] # NOTE: Non-matching keys are ignored. Compatibility is checked # elsewhere so this shouldn't be unsafe. for (item_name, template_item_type, actual_item_type) in template.zip(actual): res.extend(infer_constraints(template_item_type, actual_item_type, self.direction)) return res elif isinstance(actual, AnyType): return self.infer_against_any(template.items.values()) else: return []
def visit_typeddict_type(self, template: TypedDictType) -> List[Constraint]: actual = self.actual if isinstance(actual, TypedDictType): res = [] # type: List[Constraint] # NOTE: Non-matching keys are ignored. Compatibility is checked # elsewhere so this shouldn't be unsafe. for (item_name, template_item_type, actual_item_type) in template.zip(actual): res.extend(infer_constraints(template_item_type, actual_item_type, self.direction)) return res elif isinstance(actual, AnyType): return self.infer_against_any(template.items.values(), actual) else: return []
def write_hook(ctx: MethodSigContext) -> CallableType: if not isinstance(ctx.type, Instance): return ctx.default_signature if ctx.type.type.name == "BaseModel": return ctx.default_signature vals = _build_vals_dict(ctx.type, ctx.api) fallback = ctx.api.named_type("typing._TypedDict") # type: ignore vals_type = TypedDictType(vals, set(), fallback) return CallableType( [vals_type], [ARG_POS], ["vals"], ctx.default_signature.ret_type, ctx.default_signature.fallback, )
def _build_typeddict_typeinfo(self, ctx: AnalyzeTypeContext, name: str, items: List[str], types: List[Type], required_keys: Set[str]) -> TypeInfo: """ Build a :class:`.TypeInfo` for a TypedDict. This was basically lifted from ``mypy.semanal_typeddict``. """ try: fallback = ctx.api.named_type('mypy_extensions._TypedDict', []) except AssertionError: fallback = named_builtin_type(ctx, 'dict') info = self._basic_new_typeinfo(ctx, name, fallback) info.typeddict_type = TypedDictType(OrderedDict(zip(items, types)), required_keys, fallback) return info
def visit_typeddict_type(self, t: TypedDictType) -> Type: if isinstance(self.s, TypedDictType): items = OrderedDict([ (item_name, s_item_type) for (item_name, s_item_type, t_item_type) in self.s.zip(t) if (is_equivalent(s_item_type, t_item_type) and (item_name in t.required_keys) == (item_name in self.s.required_keys)) ]) mapping_value_type = join_type_list(list(items.values())) fallback = self.s.create_anonymous_fallback(value_type=mapping_value_type) # We need to filter by items.keys() since some required keys present in both t and # self.s might be missing from the join if the types are incompatible. required_keys = set(items.keys()) & t.required_keys & self.s.required_keys return TypedDictType(items, required_keys, fallback) elif isinstance(self.s, Instance): return join_types(self.s, t.fallback) else: return self.default(self.s)
def visit_typeddict_type(self, t: TypedDictType) -> Type: if isinstance(self.s, TypedDictType): for (_, l, r) in self.s.zip(t): if not is_equivalent(l, r): return self.default(self.s) item_list = [] # type: List[Tuple[str, Type]] for (item_name, s_item_type, t_item_type) in self.s.zipall(t): if s_item_type is not None: item_list.append((item_name, s_item_type)) else: # at least one of s_item_type and t_item_type is not None assert t_item_type is not None item_list.append((item_name, t_item_type)) items = OrderedDict(item_list) mapping_value_type = join_type_list(list(items.values())) fallback = self.s.create_anonymous_fallback( value_type=mapping_value_type) return TypedDictType(items, fallback) else: return self.default(self.s)
def build_typeddict_typeinfo(self, name: str, items: List[str], types: List[Type], required_keys: Set[str]) -> TypeInfo: fallback = (self.api.named_type_or_none('typing.Mapping', [self.api.named_type('__builtins__.str'), self.api.named_type('__builtins__.object')]) or self.api.named_type('__builtins__.object')) info = self.api.basic_new_typeinfo(name, fallback) info.typeddict_type = TypedDictType(OrderedDict(zip(items, types)), required_keys, fallback) def patch() -> None: # Calculate the correct value type for the fallback Mapping. assert info.typeddict_type, "TypedDict type deleted before calling the patch" fallback.args[1] = join.join_type_list(list(info.typeddict_type.items.values())) # We can't calculate the complete fallback type until after semantic # analysis, since otherwise MROs might be incomplete. Postpone a callback # function that patches the fallback. self.api.schedule_patch(PRIORITY_FALLBACKS, patch) return info
def add_tag_callback(ctx: MethodContext) -> Type: """Callback for the ``add_tag`` method of ``FixedMapping``. """ (key, ), (value, ) = ctx.arg_types if not isinstance(key, Instance ) or not isinstance(key.last_known_value, LiteralType): ctx.api.fail( 'The key to FixedMapping.add_tag should be a literal', ctx.context, ) return ctx.default_return_type dict_type = value if isinstance(value, Instance ) and isinstance(value.last_known_value, LiteralType): dict_type = value.last_known_value key_value = key.last_known_value.value if not isinstance(key_value, str): # pragma: no cover return ctx.default_return_type assert isinstance(ctx.default_return_type, Instance) typeddict = ctx.default_return_type.args[0] assert isinstance(typeddict, TypedDictType) items = OrderedDict(typeddict.items.items()) items[key_value] = dict_type required = set([*typeddict.required_keys, key_value]) args = [ TypedDictType( items=items, required_keys=required, fallback=typeddict.fallback, line=typeddict.line, column=typeddict.column ) ] return ctx.default_return_type.copy_modified(args=args)
def visit_typeddict_type(self, t: TypedDictType) -> ProperType: if isinstance(self.s, TypedDictType): for (name, l, r) in self.s.zip(t): if (not is_equivalent(l, r) or (name in t.required_keys) != (name in self.s.required_keys)): return self.default(self.s) item_list: List[Tuple[str, Type]] = [] for (item_name, s_item_type, t_item_type) in self.s.zipall(t): if s_item_type is not None: item_list.append((item_name, s_item_type)) else: # at least one of s_item_type and t_item_type is not None assert t_item_type is not None item_list.append((item_name, t_item_type)) items = OrderedDict(item_list) fallback = self.s.create_anonymous_fallback() required_keys = t.required_keys | self.s.required_keys return TypedDictType(items, required_keys, fallback) elif isinstance(self.s, Instance) and is_subtype(t, self.s): return t else: return self.default(self.s)
def combine_callback(ctx: MethodContext) -> Type: """Callback for the ``combine`` method on ``FixedMapping``. """ assert isinstance(ctx.type, Instance) own_typeddict, = ctx.type.args (other, ), = ctx.arg_types assert isinstance(other, Instance) other_typeddict, = other.args assert isinstance(other_typeddict, TypedDictType) assert isinstance(own_typeddict, TypedDictType) for new_key in other_typeddict.items.keys(): if new_key in own_typeddict.items: ctx.api.fail( 'Cannot combine typeddict, got overlapping key {!r}'. format(new_key), ctx.context ) return ctx.default_return_type items = list( itertools.chain( own_typeddict.items.items(), other_typeddict.items.items(), ) ) new_typeddict = TypedDictType( items=OrderedDict(items), required_keys={ *own_typeddict.required_keys, *other_typeddict.required_keys }, fallback=own_typeddict.fallback, line=own_typeddict.line, column=own_typeddict.column ) assert isinstance(ctx.default_return_type, Instance) return ctx.default_return_type.copy_modified(args=[new_typeddict])
def visit_typeddict_type(self, t: TypedDictType) -> Type: return t.copy_modified(item_types=self.expand_types(t.items.values()))
def visit_typeddict_type(self, t: TypedDictType) -> Type: items = OrderedDict([(item_name, self.anal_type(item_type)) for (item_name, item_type) in t.items.items()]) return TypedDictType(items, set(t.required_keys), t.fallback)
def visit_typeddict_type(self, t: TypedDictType) -> ProperType: return self.copy_common( t, TypedDictType(t.items, t.required_keys, t.fallback))
def object(self, ctx: AnalyzeTypeContext, schema: Dict[str, Any], outer: bool = False, **kwargs) -> Type: """Generate an annotation for an object, usually a TypedDict.""" properties = schema.get('properties') pattern_properties = schema.get('patternProperties') if pattern_properties is not None: if properties is not None: raise NotImplementedError( 'using `properties` in combination with `patternProperties`' ' is not supported') # If we have pattern properties, we want Dict[str, Union[...]] # where ... is the types of all patternProperties subschemas return named_builtin_type( ctx, 'dict', [ named_builtin_type(ctx, 'str'), UnionType([ self.get_type(ctx, subschema) for subschema in pattern_properties.values() ]), ], ) if properties is None: return named_builtin_type(ctx, 'dict') try: fallback = ctx.api.named_type('mypy_extensions._TypedDict', []) except AssertionError: fallback = named_builtin_type(ctx, 'dict', []) items, types = zip(*filter( lambda o: o[1] is not None, [ (prop, self.get_type(ctx, subschema)) for prop, subschema in properties.items() if prop not in ['default', 'const'] # These are reserved names, # not properties. ])) required_keys = set(schema.get('required', [])) if outer: # We want to name the outer Type, so that we can support nested # references. Note that this may not be fully supported in mypy # at this time. info = self._build_typeddict_typeinfo(ctx, self.outer_name, list(items), list(types), required_keys) instance = Instance(info, []) td = info.typeddict_type typing_type = td.copy_modified(item_types=list(td.items.values()), fallback=instance) # # Resolve any forward (nested) references to this Type. # if self.forward_refs: # # for fw in self.forward_refs: # fw.resolve(typing_type) return typing_type struct = OrderedDict(zip(items, types)) return TypedDictType(struct, required_keys, fallback)
def fixed_mapping_callback(ctx: FunctionContext) -> Type: """The callback to infer a better type for ``FixedMapping``. """ fallback = ctx.api.named_generic_type('typing_extensions._TypedDict', []) required_keys = set() items = OrderedDict() for idx, arg in enumerate(ctx.arg_types[0]): if isinstance(arg, AnyType): ctx.api.fail(( 'Argument {} was an "Any" which is not allowed as an' ' argument to FixedMapping' ).format(idx + 1), ctx.context) continue if isinstance(arg, Instance): typ = arg.type.fullname else: # pragma: no cover typ = '????' if typ not in ( 'cg_request_args.RequiredArgument', 'cg_request_args.OptionalArgument' ): ctx.api.fail(( 'Argument {} provided was of wrong type, expected' ' cg_request_args._RequiredArgument or' ' cg_request_args._OptionalArgument, but got {}.' ).format(idx + 1, typ), ctx.context) continue assert isinstance(arg, Instance) key_typevar = arg.args[1] if not isinstance(key_typevar, LiteralType): ctx.api.fail(( 'Second parameter of the argument should be a literal, this' ' was not the case for argument {}' ).format(idx + 1), ctx.context) continue key = key_typevar.value if not isinstance(key, str): ctx.api.fail(( 'Key should be of type string, but was of type {} for argument' ' {}.' ).format(type(key).__name__, idx + 1), ctx.context) continue if key in items: ctx.api.fail(( 'Key {!r} was already present, but given again as argument {}.' ).format(key, idx + 1), ctx.context) continue required_keys.add(key) value_type = arg.args[0] if typ == 'cg_request_args.OptionalArgument': value_type = make_simplified_union([ ctx.api.named_generic_type( 'cg_maybe.Just', [value_type], ), ctx.api.named_generic_type( 'cg_maybe._Nothing', [value_type], ), ]) items[key] = value_type assert isinstance(ctx.default_return_type, Instance) return ctx.default_return_type.copy_modified( args=[ TypedDictType(OrderedDict(items), required_keys, fallback), ] )
def visit_typeddict_type(self, t: TypedDictType) -> Type: items = OrderedDict([(item_name, item_type.accept(self)) for (item_name, item_type) in t.items.items()]) return TypedDictType(items, t.fallback)
def translate_kind_instance(typ: Type) -> Type: # noqa: WPS, C901 """ We use this ugly hack to translate ``KindN[x, y]`` into ``x[y]``. This is required due to the fact that ``KindN`` can be nested in other types, like: ``List[KindN[...]]``. We will refactor this code after ``TypeTranslator`` is released in ``[email protected]`` version. """ typ = get_proper_type(typ) if isinstance(typ, _LEAF_TYPES): # noqa: WPS223 return typ elif isinstance(typ, Instance): last_known_value: Optional[LiteralType] = None if typ.last_known_value is not None: raw_last_known_value = translate_kind_instance( typ.last_known_value) assert isinstance(raw_last_known_value, LiteralType) last_known_value = raw_last_known_value instance = Instance( typ=typ.type, args=_translate_types(typ.args), line=typ.line, column=typ.column, last_known_value=last_known_value, ) if typ.type.fullname == TYPED_KINDN: # That's where we do the change return _process_kinded_type(instance) return instance elif isinstance(typ, CallableType): return typ.copy_modified( arg_types=_translate_types(typ.arg_types), ret_type=translate_kind_instance(typ.ret_type), ) elif isinstance(typ, TupleType): return TupleType( _translate_types(typ.items), translate_kind_instance(typ.partial_fallback), # type: ignore typ.line, typ.column, ) elif isinstance(typ, TypedDictType): dict_items = { item_name: translate_kind_instance(item_type) for item_name, item_type in typ.items.items() } return TypedDictType( dict_items, typ.required_keys, translate_kind_instance(typ.fallback), # type: ignore typ.line, typ.column, ) elif isinstance(typ, LiteralType): fallback = translate_kind_instance(typ.fallback) assert isinstance(fallback, Instance) return LiteralType( value=typ.value, fallback=fallback, line=typ.line, column=typ.column, ) elif isinstance(typ, UnionType): return UnionType(_translate_types(typ.items), typ.line, typ.column) elif isinstance(typ, Overloaded): functions: List[CallableType] = [] for func in typ.items(): new = translate_kind_instance(func) assert isinstance(new, CallableType) functions.append(new) return Overloaded(items=functions) elif isinstance(typ, TypeType): return TypeType.make_normalized( translate_kind_instance(typ.item), line=typ.line, column=typ.column, ) return typ