def _load_late_type(self, late_type): """Resolve a late type, possibly by loading a module.""" if late_type.name not in self._resolved_late_types: module, dot, _ = late_type.name.rpartition(".") assert dot ast = self.vm.loader.import_name(module) if ast is not None: try: cls = ast.Lookup(late_type.name) except KeyError: if "__getattr__" not in ast: log.warning("Couldn't resolve %s", late_type.name) t = pytd.AnythingType() else: t = pytd.ToType(cls, allow_functions=True) else: # A pickle file refers to a module that went away in the mean time. log.error("During dependency resolution, couldn't import %r", module) t = pytd.AnythingType() self._resolved_late_types[late_type.name] = t return self._resolved_late_types[late_type.name]
def convert_string_type_list(types_as_string, unknown, mapping, global_lookup, depth=0): """Like convert_string_type, but operate on a list.""" if not types_as_string or booleq.Solver.ANY_VALUE in types_as_string: # If we didn't find a solution for a type (the list of matches is empty) # then report it as "?", not as "nothing", because the latter is confusing. return pytd.AnythingType() return pytd_utils.JoinTypes( convert_string_type(type_as_string, unknown, mapping, global_lookup, depth) for type_as_string in types_as_string)
def testLookupConstant(self): src1 = textwrap.dedent(""" Foo = ... # type: type """) src2 = textwrap.dedent(""" class Bar(object): bar = ... # type: foo.Foo """) ast1 = self.Parse(src1, name="foo") ast2 = self.Parse(src2, name="bar") ast2 = ast2.Visit(visitors.LookupExternalTypes({"foo": ast1, "bar": ast2})) self.assertEqual(ast2.Lookup("bar.Bar").constants[0], pytd.Constant(name="bar", type=pytd.AnythingType()))
def _function_call_to_return_type(self, node, v, seen_return, num_returns): """Get a function call's pytd return type.""" if v.signature.has_return_annotation: ret = v.signature.annotations["return"].get_instance_type(node) else: ret = seen_return.data.to_type(node) if isinstance(ret, pytd.NothingType) and num_returns == 1: if isinstance(seen_return.data, abstract.Empty): ret = pytd.AnythingType() else: assert isinstance(seen_return.data, typing_overlay.NoReturn) return ret
def to_pytd(self) -> pytd.Parameter: """Return a pytd.Parameter object for a normal argument.""" if self.default is not None: default_type = self.default if self.type is None and default_type != pytd.NamedType( "NoneType"): self.type = default_type if self.type is None: self.type = pytd.AnythingType() optional = self.default is not None return pytd.Parameter(self.name, self.type, self.kwonly, optional, None)
def _namedtuple_init(self): """Build an __init__ method for a namedtuple. Builds a dummy __init__ that accepts any arguments. Needed because our model of __builtin__.tuple uses __init__. Returns: A _NameAndSig object for an __init__ method. """ args = [(name, pytd.AnythingType(), None) for name in ("self", "*args", "**kwargs")] ret = pytd.NamedType("NoneType") return self.new_function((), "__init__", args, ret, ())
def _make_pytd_function(self, params, name="f"): pytd_params = [] for i, p in enumerate(params): p_type = pytd.ClassType(p.name) p_type.cls = p pytd_params.append( pytd.Parameter(function.argname(i), p_type, pytd.ParameterKind.REGULAR, False, None)) pytd_sig = pytd.Signature( tuple(pytd_params), None, None, pytd.AnythingType(), (), ()) sig = abstract.PyTDSignature(name, pytd_sig, self._ctx) return abstract.PyTDFunction(name, (sig,), pytd.MethodKind.METHOD, self._ctx)
def merge_method_signatures( signatures: List[NameAndSig]) -> List[pytd.Function]: """Group the signatures by name, turning each group into a function.""" name_to_signatures = collections.OrderedDict() name_to_decorator = {} name_to_is_abstract = {} name_to_is_coroutine = {} for sig in signatures: if sig.name not in name_to_signatures: name_to_signatures[sig.name] = [] name_to_decorator[sig.name] = sig.decorator old_decorator = name_to_decorator[sig.name] check = _check_decorator_overload(sig.name, old_decorator, sig.decorator) if check == _MERGE: name_to_signatures[sig.name].append(sig.signature) elif check == _REPLACE: name_to_signatures[sig.name] = [sig.signature] name_to_decorator[sig.name] = sig.decorator _add_flag_overload(name_to_is_abstract, sig.name, sig.is_abstract, "abstractmethod") _add_flag_overload(name_to_is_coroutine, sig.name, sig.is_coroutine, "coroutine") methods = [] for name, sigs in name_to_signatures.items(): decorator = name_to_decorator[name] is_abstract = name_to_is_abstract[name] is_coroutine = name_to_is_coroutine[name] if name == "__new__" or decorator == "staticmethod": kind = pytd.STATICMETHOD elif name == "__init_subclass__" or decorator == "classmethod": kind = pytd.CLASSMETHOD elif _is_property(name, decorator, sigs[0]): kind = pytd.PROPERTY # If we have only setters and/or deleters, replace them with a single # method foo(...) -> Any, so that we infer a constant `foo: Any` even if # the original method signatures are all `foo(...) -> None`. (If we have a # getter we use its return type, but in the absence of a getter we want to # fall back on Any since we cannot say anything about what the setter sets # the type of foo to.) if decorator.endswith(".setter") or decorator.endswith(".deleter"): sigs = [sigs[0].Replace(return_type=pytd.AnythingType())] else: kind = pytd.METHOD flags = 0 if is_abstract: flags |= pytd.Function.IS_ABSTRACT if is_coroutine: flags |= pytd.Function.IS_COROUTINE methods.append(pytd.Function(name, tuple(sigs), kind, flags)) return methods
def _parameterized_type(self, base_type, parameters): """Return a parameterized type.""" if self._is_any(base_type): return pytd.AnythingType() elif len(parameters) == 2 and parameters[-1] is self.ELLIPSIS and ( not self._is_callable_base_type(base_type)): element_type = parameters[0] if element_type is self.ELLIPSIS: raise ParseError("[..., ...] not supported") return pytd.GenericType(base_type=base_type, parameters=(element_type,)) else: parameters = tuple(pytd.AnythingType() if p is self.ELLIPSIS else p for p in parameters) if self._is_tuple_base_type(base_type): return self._heterogeneous_tuple(base_type, parameters) elif (self._is_callable_base_type(base_type) and self._is_heterogeneous_tuple(parameters[0])): if len(parameters) > 2: raise ParseError( "Expected 2 parameters to Callable, got %d" % len(parameters)) if len(parameters) == 1: # We're usually happy to treat omitted parameters as "Any", but we # need a return type for CallableType, or we wouldn't know whether the # last parameter is an argument or return type. parameters += (pytd.AnythingType(),) if self._is_empty_tuple(parameters[0]): parameters = parameters[1:] else: parameters = parameters[0].parameters + parameters[1:] return pytd.CallableType(base_type=base_type, parameters=parameters) else: assert parameters if (self._is_callable_base_type(base_type) and not self._is_any(parameters[0])): raise ParseError( "First argument to Callable must be a list of argument types") return pytd.GenericType(base_type=base_type, parameters=parameters)
def _value_to_parameter_types(self, node, v, instance, template, seen, view): """Get PyTD types for the parameters of an instance of an abstract value.""" if isinstance(v, abstract.CallableClass): assert template == (abstract_utils.ARGS, abstract_utils.RET), template template = list(moves.range(v.num_args)) + [template[1]] if self._is_tuple(v, instance): if isinstance(v, abstract.TupleClass): new_template = range(v.tuple_length) else: new_template = range(instance.tuple_length) if template: assert len(template) == 1 and template[0] == abstract_utils.T, template else: # We have a recursive type. By erasing the instance and value # information, we'll return Any for all of the tuple elements. v = instance = None template = new_template if instance is None and isinstance(v, abstract.ParameterizedClass): return [self.value_instance_to_pytd_type( node, v.get_formal_type_parameter(t), None, seen, view) for t in template] elif isinstance(instance, abstract.SimpleAbstractValue): type_arguments = [] for t in template: if isinstance(instance, abstract.Tuple): param_values = self._get_values(node, instance.pyval[t], view) elif instance.has_instance_type_parameter(t): param_values = self._get_values( node, instance.get_instance_type_parameter(t), view) elif isinstance(v, abstract.CallableClass): param_values = v.get_formal_type_parameter(t).instantiate( node or self.vm.root_cfg_node).data else: param_values = [self.vm.convert.unsolvable] if (param_values == [self.vm.convert.unsolvable] and isinstance(v, abstract.ParameterizedClass) and not self.vm.annotations_util.get_type_parameters( v.get_formal_type_parameter(t))): # When the instance's parameter value is unsolvable, we can get a # more precise type from the class. Note that we need to be careful # not to introduce unbound type parameters. arg = self.value_instance_to_pytd_type( node, v.get_formal_type_parameter(t), None, seen, view) else: arg = pytd_utils.JoinTypes(self.value_to_pytd_type( node, p, seen, view) for p in param_values) type_arguments.append(arg) return type_arguments else: return [pytd.AnythingType() for _ in template]
def test_signature_annotations(self): # def f(self: Any, *args: Any) self_param = pytd.Parameter("self", pytd.AnythingType(), False, False, None) # Imitate the parser's conversion of '*args: Any' to '*args: Tuple[Any]'. tup = pytd.ClassType("__builtin__.tuple") tup.cls = self._vm.convert.tuple_type.pytd_cls any_tuple = pytd.GenericType(tup, (pytd.AnythingType(), )) args_param = pytd.Parameter("args", any_tuple, False, True, None) sig = function.Signature.from_pytd( self._vm, "f", pytd.Signature((self_param, ), args_param, None, pytd.AnythingType(), (), ())) self.assertEqual(repr(sig), "def f(self: Any, *args: Tuple[Any, ...]) -> Any") self.assertIs(sig.annotations["self"], self._vm.convert.unsolvable) args_type = sig.annotations["args"] self.assertIsInstance(args_type, abstract.ParameterizedClass) self.assertIs(args_type.base_cls, self._vm.convert.tuple_type) self.assertListEqual(args_type.type_parameters.items(), [(abstract.T, self._vm.convert.unsolvable)]) self.assertIs(sig.drop_first_parameter().annotations["args"], args_type)
def _normal_param(name, param_type, default, kwonly): """Return a pytd.Parameter object for a normal argument.""" if default is not None: default_type = _type_for_default(default) if default_type == pytd.NamedType("NoneType"): if param_type is not None: param_type = pytd.UnionType((param_type, default_type)) elif param_type is None: param_type = default_type if param_type is None: param_type = pytd.AnythingType() optional = default is not None return pytd.Parameter(name, param_type, kwonly, optional, None)
def testPython3Builtins(self): # Test that we read python3 builtins from builtin.pytd if we pass a (3, 6) # version to the loader. with utils.Tempdir() as d: d.create_file("a.pyi", """\ from typing import AsyncGenerator class A(AsyncGenerator[str]): ...""") loader = load_pytd.Loader("base", python_version=self.PYTHON_VERSION, pythonpath=[d.path]) a = loader.import_name("a") cls = a.Lookup("a.A") # New python3 builtins are currently aliases for Any. self.assertIn(pytd.AnythingType(), cls.parents)
def pytd_for_types(self, defs): data = [] pytd_convert = self.convert.pytd_convert annots = abstract_utils.get_annotations_dict(defs) for name, t in pytd_convert.uninitialized_annotations_to_instance_types( self.exitpoint, annots, defs): data.append(pytd.Constant(name, t)) for name, var in defs.items(): if name in output.TOP_LEVEL_IGNORE or self._is_builtin(name, var.data): continue options = var.FilteredData(self.exitpoint, strict=False) if (len(options) > 1 and not all(isinstance(o, abstract.FUNCTION_TYPES) for o in options)): # It's ambiguous whether this is a type, a function or something # else, so encode it as a constant. combined_types = pytd_utils.JoinTypes(t.to_type(self.exitpoint) for t in options) data.append(pytd.Constant(name, combined_types)) elif options: for option in options: try: d = option.to_pytd_def(self.exitpoint, name) # Deep definition except NotImplementedError: d = option.to_type(self.exitpoint) # Type only if isinstance(d, pytd.NothingType): if isinstance(option, abstract.Empty): d = pytd.AnythingType() else: assert isinstance(option, typing_overlay.NoReturn) if isinstance(d, pytd.Type) and not isinstance(d, pytd.TypeParameter): data.append(pytd.Constant(name, d)) else: data.append(d) else: log.error("No visible options for %s", name) data.append(pytd.Constant(name, pytd.AnythingType())) return pytd_utils.WrapTypeDeclUnit("inferred", data)
def _function_to_def(self, node, v, function_name): """Convert an InterpreterFunction to a PyTD definition.""" signatures = [] combinations = tuple(v.get_call_combinations()) if not combinations: # Fallback: Generate a PyTD signature only from the definition of the # method, not the way it's being used. param = v.vm.convert.primitive_class_instances[object].to_variable(node) ret = v.vm.convert.create_new_unsolvable(node) combinations = ((node, collections.defaultdict(lambda: param.bindings[0]), ret.bindings[0]),) for node_after, combination, return_value in combinations: params = [] for i, (name, kwonly, optional) in enumerate(v.get_parameters()): if i < v.nonstararg_count and name in v.signature.annotations: t = v.signature.annotations[name].get_instance_type(node_after) else: t = combination[name].data.to_type(node_after) # Python uses ".0" etc. for the names of parameters that are tuples, # like e.g. in: "def f((x, y), z)". params.append( pytd.Parameter(name.replace(".", "_"), t, kwonly, optional, None)) if "return" in v.signature.annotations: ret = v.signature.annotations["return"].get_instance_type(node_after) else: ret = return_value.data.to_type(node_after) if isinstance(ret, pytd.NothingType) and len(combinations) == 1: assert isinstance(return_value.data, abstract.Empty) ret = pytd.AnythingType() if v.has_varargs(): starargs = pytd.Parameter(v.signature.varargs_name, pytd.NamedType("__builtin__.tuple"), False, True, None) else: starargs = None if v.has_kwargs(): starstarargs = pytd.Parameter(v.signature.kwargs_name, pytd.NamedType("__builtin__.dict"), False, True, None) else: starstarargs = None signatures.append(pytd.Signature( params=tuple(params), starargs=starargs, starstarargs=starstarargs, return_type=ret, exceptions=(), # TODO(kramm): record exceptions template=())) return pytd.Function(function_name, tuple(signatures), pytd.METHOD)
def _class_to_def(self, node, v, class_name): """Convert an InterpreterClass to a PyTD definition.""" methods = {} constants = collections.defaultdict(pytd_utils.TypeBuilder) # class-level attributes for name, member in v.members.items(): if name not in CLASS_LEVEL_IGNORE: for value in member.FilteredData(v.vm.exitpoint): if isinstance(value, abstract.Function): val = self.value_to_pytd_def(node, value, name) if isinstance(val, pytd.Function): methods[name] = val elif isinstance(v, pytd.TYPE): constants[name].add_type(val) else: raise AssertionError(str(type(val))) else: constants[name].add_type(value.to_type(node)) # instance-level attributes for instance in v.instances: for name, member in instance.members.items(): if name not in CLASS_LEVEL_IGNORE: for value in member.FilteredData(v.vm.exitpoint): constants[name].add_type(value.to_type(node)) for name in list(methods): if name in constants: # If something is both a constant and a method, it means that the class # is, at some point, overwriting its own methods with an attribute. del methods[name] constants[name].add_type(pytd.AnythingType()) bases = [pytd_utils.JoinTypes(b.get_instance_type(node) for b in basevar.data) for basevar in v.bases() if basevar is not v.vm.convert.oldstyleclass_type] constants = [pytd.Constant(name, builder.build()) for name, builder in constants.items() if builder] metaclass = v.metaclass(node) if metaclass is not None: metaclass = metaclass.get_instance_type(node) return pytd.Class(name=class_name, metaclass=metaclass, parents=tuple(bases), methods=tuple(methods.values()), constants=tuple(constants), template=())
def _load_late_type(self, late_type): """Resolve a late type, possibly by loading a module.""" if late_type.name not in self._resolved_late_types: ast = self.ctx.loader.import_name(late_type.name) if ast: t = pytd.Module(name=late_type.name, module_name=late_type.name) else: ast, attr_name = self._load_late_type_module(late_type) if ast is None: log.error( "During dependency resolution, couldn't resolve late type %r", late_type.name) t = pytd.AnythingType() else: try: cls = pytd.LookupItemRecursive(ast, attr_name) except KeyError: if "__getattr__" not in ast: log.warning("Couldn't resolve %s", late_type.name) t = pytd.AnythingType() else: t = pytd.ToType(cls, allow_functions=True) self._resolved_late_types[late_type.name] = t return self._resolved_late_types[late_type.name]
def _function_to_return_types(self, node, fvar): """Convert a function variable to a list of PyTD return types.""" options = fvar.FilteredData(self.vm.exitpoint, strict=False) if not all(isinstance(o, abstract.Function) for o in options): return [pytd.AnythingType()] types = [] for val in options: if isinstance(val, abstract.InterpreterFunction): combinations = val.get_call_combinations(node) for sig, node_after, _, return_value in combinations: types.append(self._function_call_to_return_type( sig, node_after, val, return_value, len(combinations))) elif isinstance(val, abstract.PyTDFunction): types.extend(sig.pytd_sig.return_type for sig in val.signatures) else: types.append(pytd.AnythingType()) safe_types = [] # types without type parameters for t in types: collector = visitors.CollectTypeParameters() t.Visit(collector) t = t.Visit(visitors.ReplaceTypeParameters( {p: p.upper_value for p in collector.params})) safe_types.append(t) return safe_types
def _Convert(self, t): module, name = self._GetModuleAndName(t) if not module and name == "None": # PEP 484 allows "None" as an abbreviation of "NoneType". return pytd.NamedType("NoneType") elif self._IsTyping(module): if name in PEP484_CAPITALIZED: return pytd.NamedType(name.lower()) # "typing.List" -> "list" etc. elif name == "Any": return pytd.AnythingType() else: # IO, Callable, etc. (I.e., names in typing we leave alone) return t else: return t
def _get_parameters(self, t1, t2): if isinstance(t1, pytd.TupleType) and isinstance(t2, pytd.TupleType): # No change needed; the parameters will be compared element-wise. return t1.parameters, t2.parameters elif isinstance(t2, pytd.TupleType): # Since we call _get_parameters after confirming that t1 and t2 have # compatible base types, t1 is a homogeneous tuple here. return (t1.element_type, ) * len(t2.parameters), t2.parameters elif isinstance(t1, pytd.TupleType): return (pytd.UnionType(type_list=t1.parameters), ), t2.parameters elif (isinstance(t1, pytd.CallableType) and isinstance(t2, pytd.CallableType)): # Flip the arguments, since argument types are contravariant. return t2.args + (t1.ret, ), t1.args + (t2.ret, ) elif (t1.base_type.cls.name == "__builtin__.type" and t2.base_type.cls.name == "typing.Callable"): # We'll only check the return type, since getting the argument types for # initializing a class is tricky. return t1.parameters, (t2.parameters[-1], ) elif (t1.base_type.cls.name == "typing.Callable" and t2.base_type.cls.name == "__builtin__.type"): return (t1.parameters[-1], ), t2.parameters elif isinstance(t1, pytd.CallableType): # We're matching against GenericType(Callable, (Any, _RET)), so we don't # need the argument types. return (pytd.AnythingType(), t1.ret), t2.parameters elif isinstance(t2, pytd.CallableType): return t1.parameters, (pytd.AnythingType(), t2.ret) else: num_extra_params = len(t1.parameters) - len(t2.parameters) # Matching, e.g., Dict[str, int] against Iterable[K] is legitimate. assert num_extra_params >= 0, (t1.base_type.cls.name, t2.base_type.cls.name) t2_parameters = t2.parameters + ( pytd.AnythingType(), ) * num_extra_params return t1.parameters, t2_parameters
def testAliasPrinting(self): a = pytd.Alias("MyList", pytd.GenericType( pytd.NamedType("typing.List"), (pytd.AnythingType(),))) ty = pytd.TypeDeclUnit( name="test", constants=(), type_params=(), classes=(), functions=(), aliases=(a,)) expected = textwrap.dedent(""" from typing import Any, List MyList = List[Any]""") self.assertMultiLineEqual(expected.strip(), pytd.Print(ty).strip())
def DummyMethod(name, *params): """Create a simple method using only "Any"s as types. Arguments: name: The name of the method *params: The parameter names. Returns: A pytd.Function. """ sig = pytd.Signature(tuple( pytd.Parameter(param, type=pytd.AnythingType(), kwonly=False, optional=False, mutated_type=None) for param in params), starargs=None, starstarargs=None, return_type=pytd.AnythingType(), exceptions=(), template=()) return pytd.Function(name=name, signatures=(sig, ), kind=pytd.METHOD, flags=0)
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 _make_init(self) -> function.NameAndSig: """Build an __init__ method for a namedtuple. Builds a dummy __init__ that accepts any arguments. Needed because our model of builtins.tuple uses __init__. Returns: A function.NameAndSig object for an __init__ method. """ self_arg = function.Param("self", pytd.AnythingType()).to_pytd() ret = pytd.NamedType("NoneType") sig = pytd.Signature(params=(self_arg,), return_type=ret, starargs=function.pytd_default_star_param(), starstarargs=function.pytd_default_starstar_param(), exceptions=(), template=()) return function.NameAndSig("__init__", sig)
def _value_to_parameter_types(self, node, v, instance, template, seen, view): """Get PyTD types for the parameters of an instance of an abstract value.""" if isinstance(v, abstract.Callable): assert template == (abstract.ARGS, abstract.RET), template template = range(v.num_args) + [template[1]] if self._is_tuple(v, instance): if isinstance(v, abstract.TupleClass): new_template = range(v.tuple_length) else: new_template = range(instance.tuple_length) if template: assert len( template) == 1 and template[0] == abstract.T, template else: # We have a recursive type. By erasing the instance and value # information, we'll return Any for all of the tuple elements. v = instance = None template = new_template if instance is None and isinstance(v, abstract.ParameterizedClass): return [ self.value_instance_to_pytd_type(node, v.type_parameters[t], None, seen, view) for t in template ] elif isinstance(instance, abstract.SimpleAbstractValue): type_arguments = [] for t in template: if isinstance(instance, abstract.Tuple): param_values = self._get_values(node, instance.pyval[t], view) elif t in instance.type_parameters: param_values = self._get_values( node, instance.type_parameters[t], view) elif isinstance(v, abstract.Callable): param_values = v.type_parameters[t].instantiate( node or v.vm.root_cfg_node).data else: param_values = [v.vm.convert.unsolvable] type_arguments.append( pytd_utils.JoinTypes( self.value_to_pytd_type(node, p, seen, view) for p in param_values)) return type_arguments else: return [pytd.AnythingType() for _ in template]
def _FormatContainerContents(self, node): """Print out the last type parameter of a container. Used for *args/**kw.""" assert isinstance(node, pytd.Parameter) if isinstance(node.type, pytd.GenericType): container_name = node.type.base_type.name.rpartition(".")[2] assert container_name in ("tuple", "dict") self._typing_import_counts[container_name.capitalize()] -= 1 # If the type is "Any", e.g. `**kwargs: Any`, decrement Any to avoid an # extraneous import of typing.Any. Any was visited before this function # transformed **kwargs, so it was incremented at least once already. if isinstance(node.type.parameters[-1], pytd.AnythingType): self._typing_import_counts["Any"] -= 1 return node.Replace(type=node.type.parameters[-1], optional=False).Visit(PrintVisitor()) else: return node.Replace(type=pytd.AnythingType(), optional=False).Visit(PrintVisitor())
def match_Function_against_Class(self, f1, cls2, subst, cache): cls2_methods = cache.get(id(cls2)) if cls2_methods is None: cls2_methods = cache[id(cls2)] = {f.name: f for f in cls2.methods} if f1.name not in cls2_methods: # The class itself doesn't have this method, but base classes might. # TODO(kramm): This should do MRO order, not depth-first. for base in cls2.parents: if isinstance(base, pytd.AnythingType): # AnythingType can contain any method. However, that would mean that # a class that inherits from AnythingType contains any method # imaginable, and hence is a match for anything. To prevent the bad # results caused by that, return FALSE here. return booleq.FALSE elif isinstance(base, (pytd.ClassType, pytd.GenericType)): if isinstance(base, pytd.ClassType): cls = base.cls values = tuple(pytd.AnythingType() for _ in cls.template) elif isinstance(base, pytd.TupleType): cls = base.base_type.cls values = (pytd.UnionType(type_list=base.parameters), ) else: cls = base.base_type.cls values = base.parameters if values: subst = subst.copy() for param, value in zip(cls.template, values): subst[param.type_param] = value implication = self.match_Function_against_Class( f1, cls, subst, cache) if implication is not booleq.FALSE: return implication else: # Funky types like UnionType are hard to match against (and shouldn't # appear as a base class) so we treat them as catch-all. log.warning("Assuming that %s has method %s", pytd_utils.Print(base), f1.name) return booleq.TRUE return booleq.FALSE else: f2 = cls2_methods[f1.name] return self.match_Function_against_Function(f1, f2, subst, skip_self=True)
def p_type_homogeneous(self, p): """type : named_or_external_type LBRACKET parameters RBRACKET""" _, base_type, _, parameters, _ = p if p[1] == pytd.NamedType('Union'): p[0] = pytd.UnionType(parameters) elif p[1] == pytd.NamedType('Optional'): p[0] = pytd.UnionType(parameters[0], pytd.NamedType('None')) elif len(parameters) == 2 and parameters[-1] is Ellipsis: element_type, _ = parameters if element_type is Ellipsis: make_syntax_error(self, '[..., ...] not supported', p) p[0] = pytd.HomogeneousContainerType(base_type=base_type, parameters=(element_type, )) else: parameters = tuple(pytd.AnythingType() if p is Ellipsis else p for p in parameters) p[0] = pytd.GenericType(base_type=base_type, parameters=parameters)
def VisitNamedType(self, node): name = node.name for lookup in self._lookup_list: try: cls = lookup.Lookup(name) if isinstance(cls, pytd.Class): return node except KeyError: pass if "." in node.name: logging.warning("Marking %s as external", name) module_name, base_name = name.rsplit(".", 1) return pytd.ExternalType(base_name, module_name) else: if (self._do_not_log_prefix is None or not name.startswith(self._do_not_log_prefix)): logging.error("Setting %s to ?", name) return pytd.AnythingType()
def new_new_type(self, name, typ): """Returns a type for a NewType.""" args = [("self", pytd.AnythingType()), ("val", typ)] ret = pytd.NamedType("NoneType") methods = function.merge_method_signatures( [function.NameAndSig.make("__init__", args, ret)]) cls_name = escape.pack_newtype_base_class( name, len(self.generated_classes[name])) cls = pytd.Class(name=cls_name, metaclass=None, parents=(typ, ), methods=tuple(methods), constants=(), decorators=(), classes=(), slots=None, template=()) self.generated_classes[name].append(cls) return pytd.NamedType(cls_name)