def merged_type(t, s): # type: (AbstractType, AbstractType) -> Optional[AbstractType] """Return merged type if two items can be merged in to a different, more general type. Return None if merging is not possible. """ if isinstance(t, TupleType) and isinstance(s, TupleType): if len(t.items) == len(s.items): return TupleType( [combine_types([ti, si]) for ti, si in zip(t.items, s.items)]) all_items = t.items + s.items if all_items and all(item == all_items[0] for item in all_items[1:]): # Merge multiple compatible fixed-length tuples into a variable-length tuple type. return ClassType('Tuple', [all_items[0]]) elif (isinstance(t, TupleType) and isinstance(s, ClassType) and s.name == 'Tuple' and len(s.args) == 1): if all(item == s.args[0] for item in t.items): # Merge fixed-length tuple and variable-length tuple. return s elif isinstance(s, TupleType) and isinstance( t, ClassType) and t.name == 'Tuple': return merged_type(s, t) elif isinstance(s, AnyType): # This seems to be usually desirable, since Anys tend to come from unknown types. return t elif isinstance(t, AnyType): # Similar to above. return s return None
def parse_type(self): # type: () -> AbstractType t = self.next() if not isinstance(t, DottedName): self.fail() if t.text == 'Any': return AnyType() elif t.text == 'Tuple': self.expect('[') args = self.parse_type_list() self.expect(']') return TupleType(args) elif t.text == 'Union': self.expect('[') items = self.parse_type_list() self.expect(']') if len(items) == 1: return items[0] elif len(items) == 0: self.fail() else: return UnionType(items) else: if self.lookup() == '[': self.expect('[') args = self.parse_type_list() self.expect(']') if t.text == 'Optional' and len(args) == 1: return UnionType([args[0], ClassType('None')]) return ClassType(t.text, args) else: return ClassType(t.text)
def test_simplify_multiple_empty_collections(self): # type: () -> None self.assert_infer( ['() -> Tuple[List, List[x]]', '() -> Tuple[List, List]'], ([], TupleType( [ClassType('List'), ClassType('List', [ClassType('x')])])))
def test_merge_tuples_with_different_lengths(self): # type: () -> None assert merge_items([ TupleType([CT('str')]), TupleType([CT('str'), CT('str')])]) == [CT('Tuple', [CT('str')])] assert merge_items([ TupleType([]), TupleType([CT('str')]), TupleType([CT('str'), CT('str')])]) == [CT('Tuple', [CT('str')])] # Don't merge if types aren't identical assert merge_items([ TupleType([CT('str')]), TupleType([CT('str'), CT('int')])]) == [TupleType([CT('str')]), TupleType([CT('str'), CT('int')])]
def test_merge_union_of_same_length_tuples(self): # type: () -> None assert merge_items([TupleType([CT('str')]), TupleType([CT('int')])]) == [TupleType([UnionType([CT('str'), CT('int')])])] assert merge_items([TupleType([CT('str')]), TupleType([CT('Text')])]) == [TupleType([CT('Text')])]
def simplify_recursive(typ): # type: (AbstractType) -> AbstractType """Simplify all components of a type.""" if isinstance(typ, UnionType): return combine_types(typ.items) elif isinstance(typ, ClassType): simplified = ClassType(typ.name, [simplify_recursive(arg) for arg in typ.args]) args = simplified.args if (simplified.name == 'Dict' and len(args) == 2 and isinstance(args[0], ClassType) and args[0].name in ('str', 'Text') and isinstance(args[1], UnionType) and not is_optional(args[1])): # Looks like a potential case for TypedDict, which we don't properly support yet. return ClassType('Dict', [args[0], AnyType()]) return simplified elif isinstance(typ, TupleType): return TupleType([simplify_recursive(item) for item in typ.items]) return typ
def test_tuple_type_str(self): # type: () -> None assert str(TupleType([ClassType('int')])) == 'Tuple[int]' assert str(TupleType([ClassType('int'), ClassType('str')])) == 'Tuple[int, str]' assert str(TupleType([])) == 'Tuple[()]'
def tuple_arg(items): # type: (List[AbstractType]) -> Argument return Argument(TupleType(items), ARG_POS)