def testAdjustTypeParameters(self): ast = self.Parse(""" T = TypeVar("T") T2 = TypeVar("T2") def f(x: T) -> T class A(Generic[T]): def a(self, x: T2) -> None: self = A[T or T2] """) f = ast.Lookup("f") sig, = f.signatures p_x, = sig.params self.assertEqual(sig.template, (pytd.TemplateItem(pytd.TypeParameter("T", scope="f")),)) self.assertEqual(p_x.type, pytd.TypeParameter("T", scope="f")) cls = ast.Lookup("A") f_cls, = cls.methods sig_cls, = f_cls.signatures p_self, p_x_cls = sig_cls.params self.assertEqual(cls.template, (pytd.TemplateItem(pytd.TypeParameter("T", scope="A")),)) self.assertEqual(sig_cls.template, (pytd.TemplateItem( pytd.TypeParameter("T2", scope="A.a")),)) self.assertEqual(p_self.type.parameters, (pytd.TypeParameter("T", scope="A"),)) self.assertEqual(p_x_cls.type, pytd.TypeParameter("T2", scope="A.a"))
def testAdjustTypeParametersWithDuplicates(self): ast = self.ParseWithBuiltins(""" T = TypeVar("T") class A(Dict[T, T], Generic[T]): pass """) a = ast.Lookup("A") self.assertEqual( (pytd.TemplateItem(pytd.TypeParameter("T", (), None, "A")), pytd.TemplateItem(pytd.TypeParameter("T", (), None, "A"))), a.template)
def SplitParents(parser, p, parents): """Strip the special Generic[...] class out of the base classes.""" template = () other_parents = [] for parent in parents: if (isinstance(parent, pytd.GenericType) and parent.base_type == pytd.ExternalType('Generic', 'typing')): if not all( isinstance(param, pytd.NamedType) for param in parent.parameters): make_syntax_error( parser, 'Illegal template parameter %s' % pytd.Print(parent), p) if template: make_syntax_error(parser, 'Duplicate Template base class', p) template = tuple( pytd.TemplateItem(pytd.TypeParameter(param.name)) for param in parent.parameters) all_names = [t.name for t in template] duplicates = [ name for name, count in collections.Counter(all_names).items() if count >= 2 ] if duplicates: make_syntax_error( parser, 'Duplicate template parameters' + ', '.join(duplicates), p) else: if parent != pytd.NothingType(): other_parents.append(parent) return template, tuple(other_parents)
def testAdjustTypeParametersWithBuiltins(self): ast = self.ParseWithBuiltins(""" T = TypeVar("T") K = TypeVar("K") V = TypeVar("V") class Foo(List[int]): pass class Bar(Dict[T, int]): pass class Baz(Generic[K, V]): pass class Qux(Baz[str, int]): pass """) foo = ast.Lookup("Foo") bar = ast.Lookup("Bar") qux = ast.Lookup("Qux") foo_parent, = foo.parents bar_parent, = bar.parents qux_parent, = qux.parents # Expected: # Class(Foo, parent=GenericType(List, parameters=(int,)), template=()) # Class(Bar, parent=GenericType(Dict, parameters=(T, int)), template=(T)) # Class(Qux, parent=GenericType(Baz, parameters=(str, int)), template=()) self.assertEqual((pytd.ClassType("int"),), foo_parent.parameters) self.assertEqual((), foo.template) self.assertEqual( (pytd.TypeParameter("T", scope="Bar"), pytd.ClassType("int")), bar_parent.parameters) self.assertEqual( (pytd.TemplateItem(pytd.TypeParameter("T", scope="Bar")),), bar.template) self.assertEqual((pytd.ClassType("str"), pytd.ClassType("int")), qux_parent.parameters) self.assertEqual((), qux.template)
def testVerifyHeterogeneousTuple(self): # Error: does not inherit from Generic base = pytd.ClassType("tuple") base.cls = pytd.Class("tuple", None, (), (), (), (), None, ()) t1 = pytd.TupleType(base, (pytd.NamedType("str"), pytd.NamedType("float"))) self.assertRaises(visitors.ContainerError, lambda: t1.Visit(visitors.VerifyContainers())) # Error: Generic[str, float] gen = pytd.ClassType("typing.Generic") gen.cls = pytd.Class("typing.Generic", None, (), (), (), (), None, ()) t2 = pytd.TupleType(gen, (pytd.NamedType("str"), pytd.NamedType("float"))) self.assertRaises(visitors.ContainerError, lambda: t2.Visit(visitors.VerifyContainers())) # Okay param = pytd.TypeParameter("T") parent = pytd.GenericType(gen, (param,)) base.cls = pytd.Class( "tuple", None, (parent,), (), (), (), None, (pytd.TemplateItem(param),)) t3 = pytd.TupleType(base, (pytd.NamedType("str"), pytd.NamedType("float"))) t3.Visit(visitors.VerifyContainers())
def p_funcdef(self, p): """funcdef : DEF NAME LPAREN params RPAREN return raises signature maybe_body""" _, _, name, _, params, _, return_type, raises, _, body = p # TODO(kramm): Output a warning if we already encountered a signature # with these types (but potentially different argument names) if name == '__init__' and isinstance(return_type, pytd.AnythingType): ret = pytd.NamedType('NoneType') else: ret = return_type signature = pytd.Signature(params=tuple(params.required), return_type=ret, exceptions=tuple(raises), template=(), has_optional=params.has_optional) typeparams = { name: pytd.TypeParameter(name) for name in self.context.typevars } used_typeparams = set() signature = signature.Visit( visitors.ReplaceTypes(typeparams, used_typeparams)) if used_typeparams: signature = signature.Replace(template=tuple( pytd.TemplateItem(typeparams[name]) for name in used_typeparams)) for mutator in body: try: signature = signature.Visit(mutator) except NotImplementedError as e: make_syntax_error(self, e.message, p) if not mutator.successful: make_syntax_error(self, 'No parameter named %s' % mutator.name, p) p[0] = NameAndSig(name=name, signature=signature, external_code=False)