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 VisitUnionType(self, union): if len(union.type_list) > self.max_length: return pytd.NamedType("object") elif pytd.NamedType("object") in union.type_list: return pytd.NamedType("object") else: return union
def testNamed(self): m = type_match.TypeMatch({}) eq = m.match_type_against_type(pytd.NamedType("A"), pytd.NamedType("A"), {}) self.assertEquals(eq, booleq.TRUE) eq = m.match_type_against_type(pytd.NamedType("A"), pytd.NamedType("B"), {}) self.assertNotEquals(eq, booleq.TRUE)
def ClassAsType(cls): """Converts a pytd.Class to an instance of pytd.Type.""" params = tuple(item.type_param for item in cls.template) if not params: return pytd.NamedType(cls.name) elif len(params) == 1: return pytd.HomogeneousContainerType(pytd.NamedType(cls.name), params) else: # len(cls.template) >= 2 return pytd.GenericType(pytd.NamedType(cls.name), params)
def p_type_homogeneous(self, p): """type : NAME LBRACKET parameters RBRACKET""" if len(p[3]) == 1: element_type, = p[3] p[0] = pytd.HomogeneousContainerType(base_type=pytd.NamedType( p[1]), parameters=(element_type, )) else: p[0] = pytd.GenericType(base_type=pytd.NamedType(p[1]), parameters=p[3])
def _HasSubClassInSet(self, cls, known): """Queries whether a subclass of a type is present in a given set.""" # object is an implicit superclass of all types. So if we're being asked # whether object has a subclass in the set, we just need to find any # class that's not object itself. if (cls == pytd.NamedType("object") and known and any(k != pytd.NamedType("object") for k in known)): return True return any( pytd.NamedType(sub) in known for sub in self._subclasses[str(cls)])
def testTokens(self): """Test various token forms (int, float, n"...", etc.).""" # TODO: a test with '"' or "'" in a string data = textwrap.dedent(""" def `interface`(abcde: "xyz", foo: 'a"b', b: -1.0, c: 666) -> int """) result = self.Parse(data) f1 = result.Lookup("interface") f2 = pytd.Function( name="interface", signatures=(pytd.Signature( params=( pytd.Parameter(name="abcde", type=pytd.Scalar(value="xyz")), pytd.Parameter(name="foo", type=pytd.Scalar(value='a"b')), pytd.Parameter(name="b", type=pytd.Scalar(value=-1.0)), pytd.Parameter(name="c", type=pytd.Scalar(value=666))), return_type=pytd.NamedType("int"), exceptions=(), template=(), has_optional=False),)) self.assertEqual(f1, f2)
def testJoinNothingType(self): """Test that JoinTypes() removes or collapses 'nothing'.""" a = pytd.NamedType("a") nothing = pytd.NothingType() self.assertEquals(utils.JoinTypes([a, nothing]), a) self.assertEquals(utils.JoinTypes([nothing]), nothing) self.assertEquals(utils.JoinTypes([nothing, nothing]), nothing)
def __init__(self, replace_unknown=False, force=False): self.class_types = [] # allow nested classes self.force = force self.replaced_self_types = (pytd.NamedType("object"), pytd.ClassType("object")) if replace_unknown: self.replaced_self_types += (pytd.AnythingType(), )
def p_classdef(self, p): """classdef : CLASS NAME template parents COLON INDENT class_funcs DEDENT""" # 1 2 3 4 5 6 funcdefs = [x for x in p[7] if isinstance(x, NameAndSig)] constants = [x for x in p[7] if isinstance(x, pytd.Constant)] if (set(f.name for f in funcdefs) | set(c.name for c in constants) != set(d.name for d in p[7])): # TODO: raise a syntax error right when the identifier is defined. raise make_syntax_error(self, 'Duplicate identifier(s)', p) # Check that template parameter names are unique: template_names = {t.name for t in p[3]} for _, sig in funcdefs: for t in sig.template: if t.name in template_names: raise make_syntax_error( self, 'Duplicate template parameter %s' % t.name, p) if p[4] == [pytd.NothingType()]: bases = () else: # Everything implicitly subclasses "object" bases = tuple(p[4]) or (pytd.NamedType('object'), ) cls = pytd.Class(name=p[2], parents=bases, methods=tuple(MergeSignatures(funcdefs)), constants=tuple(constants), template=tuple(p[3])) p[0] = cls.Visit(visitors.AdjustSelf())
def VisitClassType(self, node): """Converts a class type to a named type. Args: node: The ClassType. Returns: A NamedType. """ return pytd.NamedType(node.name)
def testJoinTypes(self): """Test that JoinTypes() does recursive flattening.""" n1, n2, n3, n4, n5, n6 = [pytd.NamedType("n%d" % i) for i in xrange(6)] # n1 or (n2 or (n3)) nested1 = pytd.UnionType( (n1, pytd.UnionType((n2, pytd.UnionType((n3, )))))) # ((n4) or n5) or n6 nested2 = pytd.UnionType((pytd.UnionType((pytd.UnionType( (n4, )), n5)), n6)) joined = optimize.JoinTypes([nested1, nested2]) self.assertEquals(joined.type_list, (n1, n2, n3, n4, n5, n6))
def _CollectSuperclasses(self, node, collect): """Recursively collect super classes for a type. Arguments: node: A type node. collect: A set(), modified to contain all superclasses. """ collect.add(node) superclasses = [ pytd.NamedType(name) for name in self._superclasses.get(str(node), []) ] # The superclasses might have superclasses of their own, so recurse. for superclass in superclasses: self._CollectSuperclasses(superclass, collect) if node != pytd.NamedType("object"): # Everything but object itself subclasses object. This is not explicitly # specified in _superclasses, so we add object manually. collect.add(pytd.NamedType("object"))
def testReplaceType(self): src = """ class A: def a(self, a: A or B) -> A or B raises A, B """ expected = """ class A: def a(self, a: A2 or B) -> A2 or B raises A2, B """ tree = self.Parse(src) new_tree = tree.Visit(visitors.ReplaceType({"A": pytd.NamedType("A2")})) self.AssertSourceEquals(new_tree, expected)
def testReturnTypes(self): src = textwrap.dedent(""" def a() -> ? # TODO: remove "-> ?" if we allow implicit result def b() -> ? def c() -> object def d() -> None def e() -> a or b def f() -> a<x> def g() -> a<x,> def h() -> a<x,y> def i() -> nothing # never returns """) result = self.Parse(src) ret = {f.name: f.signatures[0].return_type for f in result.functions} self.assertIsInstance(ret["a"], pytd.AnythingType) self.assertIsInstance(ret["b"], pytd.AnythingType) self.assertEquals(ret["c"], pytd.NamedType("object")) self.assertEquals(ret["d"], pytd.NamedType("None")) self.assertIsInstance(ret["e"], pytd.UnionType) self.assertIsInstance(ret["f"], pytd.HomogeneousContainerType) self.assertIsInstance(ret["g"], pytd.GenericType) self.assertIsInstance(ret["h"], pytd.GenericType) self.assertIsInstance(ret["i"], pytd.NothingType)
def _CollectSuperclasses(self, node, collect): """Recursively collect super classes for a type. Arguments: node: A type node. collect: A set(), modified to contain all superclasses. """ collect.add(node) superclasses = [ pytd.NamedType(name) for name in self._superclasses.get(str(node), []) ] # The superclasses might have superclasses of their own, so recurse. for superclass in superclasses: self._CollectSuperclasses(superclass, collect)
def VisitGenericType(self, node): """Converts a parameter-based template type (e.g. dict<str,int>) to a class. This works by looking up the actual Class (using the lookup table passed when initializing the visitor) and substituting the parameters of the template everywhere in its definition. The new class is appended to the list of classes of this module. (Later on, also all templates are removed.) Args: node: An instance of GenericType. Returns: A new NamedType pointing to an instantiation of the class. """ name = pytd.Print(node) if name not in self.classes_to_instantiate: self.classes_to_instantiate[name] = node return pytd.NamedType(name)
def VisitHomogeneousContainerType(self, node): """Converts a template type (container type) to a concrete class. This works by looking up the actual Class (using the lookup table passed when initializing the visitor) and substituting the parameters of the template everywhere in its definition. The new class is appended to the list of classes of this module. (Later on, the template we used is removed.) Args: node: An instance of HomogeneousContainerType Returns: A new NamedType pointing to an instantiation of the class. """ name = pytd.Print(node) if name not in self.classes_to_instantiate: self.classes_to_instantiate[name] = node return pytd.NamedType(name)
def testOrder(self): # pytd types' primary sort key is the class name, second sort key is # the contents when interpreted as a (named)tuple. nodes = [ pytd.AnythingType(), pytd.GenericType(self.list, (self.int, )), pytd.NamedType("int"), pytd.NothingType(), pytd.UnionType(self.float), pytd.UnionType(self.int) ] for n1, n2 in zip(nodes[:-1], nodes[1:]): self.assertLess(n1, n2) self.assertLessEqual(n1, n2) self.assertGreater(n2, n1) self.assertGreaterEqual(n2, n1) for p in itertools.permutations(nodes): self.assertEquals(list(sorted(p)), nodes)
def testSelf(self): """Test handling of self.""" data = textwrap.dedent(""" class MyClass<U, V>: def f1(self) -> ? """) result = self.Parse(data) myclass = result.Lookup("MyClass") self.assertEquals([t.name for t in myclass.template], ["U", "V"]) f = myclass.Lookup("f1").signatures[0] self_param = f.params[0] self.assertEquals(self_param.name, "self") u, v = myclass.template self.assertEquals(self_param.type, pytd.GenericType(pytd.NamedType("MyClass"), (u.type_param, v.type_param)))
def p_funcdef(self, p): """funcdef : DEF NAME template LPAREN params RPAREN return raises signature maybe_body""" # 1 2 3 4 5 6 7 8 9 10 # TODO: Output a warning if we already encountered a signature # with these types (but potentially different argument names) if p[2] == '__init__' and isinstance(p[7], pytd.AnythingType): # for __init__, the default return value is None ret = pytd.NamedType('NoneType') else: ret = p[7] signature = pytd.Signature(params=tuple(p[5].required), return_type=ret, exceptions=tuple(p[8]), template=tuple(p[3]), has_optional=p[5].has_optional) for mutator in p[10]: signature = signature.Visit(mutator) if not mutator.successful: make_syntax_error(self, 'No parameter named %s' % mutator.name, p) p[0] = NameAndSig(name=p[2], signature=signature)
def testDecorator(self): decorator = decorate.Decorator() # Change pytd.NamedType to also have a method called "Test1" @decorator # pylint: disable=unused-variable class NamedType(pytd.NamedType): def Test1(self): pass # Change pytd.Scalar to also have a method called "Test2" @decorator # pylint: disable=unused-variable class Scalar(pytd.Scalar): def Test2(self): pass tree = pytd.Scalar(pytd.NamedType("test")) tree = decorator.Visit(tree) # test that we now have the "test2" method on pytd.Scalar tree.Test2() # test that we now have the "test1" method on pytd.NamedType tree.value.Test1()
def testNamedAgainstGeneric(self): m = type_match.TypeMatch({}) eq = m.match_type_against_type( pytd.GenericType(pytd.NamedType("A"), []), pytd.NamedType("A"), {}) self.assertEquals(eq, booleq.TRUE)
def p_type_name(self, p): """type : NAME""" p[0] = pytd.NamedType(p[1])
def p_type_generic_1(self, p): """type : NAME LBRACKET parameters COMMA RBRACKET""" p[0] = pytd.GenericType(base_type=pytd.NamedType(p[1]), parameters=p[3])
def p_param(self, p): """param : NAME""" # type is optional and defaults to "object" p[0] = pytd.Parameter(p[1], pytd.NamedType('object'))
def p_template_item(self, p): """template_item : NAME""" p[0] = pytd.TemplateItem(pytd.TypeParameter(p[1]), pytd.NamedType('object'))
def p_template_item(self, p): """template_item : NAME""" p[0] = pytd.TemplateItem(p[1], pytd.NamedType('object'), 0)
def testNothing(self): m = type_match.TypeMatch({}) eq = m.match_type_against_type(pytd.NothingType(), pytd.NamedType("A"), {}) self.assertEquals(eq, booleq.FALSE)
def testHasObjectSuperClass(self): cls = self.builtins.Lookup("int") self.assertEquals(cls.parents, (pytd.NamedType("object"), )) cls = self.builtins.Lookup("object") self.assertEquals(cls.parents, ())