def p_type_and(self, p): """type : type AND type""" # TODO: Unless we bring interfaces back, it's not clear when # "type1 and type2" would be useful for anything. We # should remove it. # This rule depends on precedence specification if (isinstance(p[1], pytd.IntersectionType) and isinstance(p[3], pytd.NamedType)): p[0] = pytd.IntersectionType(p[1].type_list + (p[3], )) elif (isinstance(p[1], pytd.NamedType) and isinstance(p[3], pytd.IntersectionType)): # associative p[0] = pytd.IntersectionType(((p[1], ) + p[3].type_list)) else: p[0] = pytd.IntersectionType((p[1], p[3]))
def testComplexCombinedType(self): """Test parsing a type with both union and intersection.""" data1 = r"def foo(a: Foo or Bar and Zot) -> object" data2 = r"def foo(a: Foo or (Bar and Zot)) -> object" result1 = self.Parse(data1) result2 = self.Parse(data2) f = pytd.Function( name="foo", signatures=(pytd.Signature( params=( pytd.Parameter( name="a", type=pytd.UnionType( type_list=( pytd.NamedType("Foo"), pytd.IntersectionType( type_list=( pytd.NamedType("Bar"), pytd.NamedType("Zot")))) ) ),), return_type=pytd.NamedType("object"), template=(), has_optional=False, exceptions=()),)) self.assertEqual(f, result1.Lookup("foo")) self.assertEqual(f, result2.Lookup("foo"))
def p_type_and(self, p): """type : type AND type""" # TODO: Unless we bring interfaces back, it's not clear when # "type1 and type2" would be useful for anything. We # should remove it. # This rule depends on precedence specification # IntersectionType flattens any contained IntersectinType's p[0] = pytd.IntersectionType((p[1], p[3]))
def testIntersectionError(self): """Typechecking fct with intersection types (error). """ with self.assertRaises(checker.CheckTypeAnnotationError) as context: union.DoSomeIOStuff( union.Readable()) # we want Readable & Writable expected = checker.ParamTypeErrorMsg( "DoSomeIOStuff", "f", union.Readable, pytd.IntersectionType([union.Readable, union.Writable])) [actual] = context.exception.args[0] self.assertEquals(expected, actual)
def ConvertToType(module, type_node): """Helper for converting a type node to a valid Python type. Args: module: The module to look up symbols/types type_node: A type node to convert into a python type Returns: A valid Python type. Note that None is considered a type in the declaration language, but a value in Python. So a string None is converted to a NoneType. We use the module object to look up potential type definitions defined inside that module. Raises: TypeError: if the type node passed is not supported/unknown """ # TODO: Convert this to a visitor. # clean up str if isinstance(type_node, pytd.NamedType): if type_node.name == "None": return types.NoneType elif type_node.name == "generator": return types.GeneratorType else: res = _EvalWithModuleContext(type_node.name, module) assert isinstance(res, type), (type_node.name, repr(res)) return res elif isinstance(type_node, pytd.UnionType): return pytd.UnionType([ConvertToType(module, t) for t in type_node.type_list]) elif isinstance(type_node, pytd.IntersectionType): return pytd.IntersectionType([ConvertToType(module, t) for t in type_node.type_list]) elif isinstance(type_node, pytd.GenericType): return pytd.GenericType(ConvertToType(module, type_node.base_type), type_node.parameters) elif isinstance(type_node, pytd.HomogeneousContainerType): return pytd.HomogeneousContainerType(ConvertToType(module, type_node.base_type), ConvertToType(module, type_node.element_type)) else: raise TypeError("Unknown type of type_node: {!r}".format(type_node))
def VisitIntersectionType(self, node): return pytd.IntersectionType(tuple(sorted(node.type_list)))