def EnterSignature(self, signature): """Parse a signature, and update self.operators. This will add the reverse of operators like __radd__ to self.operators for later processing by PreprocessReverseOperatorsVisitor. Args: signature: A pytd.Signature Returns: False, to indicate that we don't want this visitor to descend into the subtree of the signature. """ if self.unreversed_name: assert len(signature.params) == 2 left, right = signature.params[0].type, signature.params[1].type if isinstance(left, pytd.ClassType) and isinstance( right, pytd.ClassType): if self._ShouldReplace(right.cls, left.cls, self.unreversed_name, self.function_name): self.operators[right.cls][self.unreversed_name].append( signature.Replace( params=(pytd.Parameter("self", right), pytd.Parameter("other", left)))) else: logging.warn("Ignoring %s on %s: %s has %s", self.function_name, left.name, right.name, self.unreversed_name) else: logging.warn("Unsupported %s operator on %s", self.function_name, type(right).__name__) return False # don't bother descending into this signature
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 to_structural_def(self, node, class_name): """Convert this Unknown to a pytd.Class.""" self_param = (pytd.Parameter("self", pytd.AnythingType(), pytd.ParameterKind.REGULAR, False, None), ) starargs = None starstarargs = None def _make_sig(args, ret): return pytd.Signature(self_param + self._make_params(node, args), starargs, starstarargs, return_type=Unknown._to_pytd(node, ret), exceptions=(), template=()) calls = tuple( pytd_utils.OrderedSet( _make_sig(args, ret) for args, _, ret in self._calls)) if calls: methods = (pytd.Function("__call__", calls, pytd.MethodKind.METHOD), ) else: methods = () return pytd.Class(name=class_name, metaclass=None, bases=(pytd.NamedType("builtins.object"), ), methods=methods, constants=tuple( pytd.Constant(name, Unknown._to_pytd(node, c)) for name, c in self.members.items()), classes=(), decorators=(), slots=None, template=())
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"))))), kwonly=False, optional=False, mutated_type=None), ), starargs=None, starstarargs=None, return_type=pytd.NamedType("object"), template=(), exceptions=()), ), kind=pytd.METHOD) self.assertEqual(f, result1.Lookup("foo")) self.assertEqual(f, result2.Lookup("foo"))
def _simple_func_to_def(self, node, v, name): """Convert a SimpleFunction to a PyTD definition.""" sig = v.signature def get_parameter(p, kind): return pytd.Parameter(p, sig.annotations[p].get_instance_type(node), kind, p in sig.defaults, None) posonly = [ get_parameter(p, pytd.ParameterKind.POSONLY) for p in sig.posonly_params ] params = [ get_parameter(p, pytd.ParameterKind.REGULAR) for p in sig.param_names[sig.posonly_count:] ] kwonly = [ get_parameter(p, pytd.ParameterKind.KWONLY) for p in sig.kwonly_params ] if sig.varargs_name: star = pytd.Parameter( sig.varargs_name, sig.annotations[sig.varargs_name].get_instance_type(node), pytd.ParameterKind.REGULAR, False, None) else: star = None if sig.kwargs_name: starstar = pytd.Parameter( sig.kwargs_name, sig.annotations[sig.kwargs_name].get_instance_type(node), pytd.ParameterKind.REGULAR, False, None) else: starstar = None if sig.has_return_annotation: ret_type = sig.annotations["return"].get_instance_type(node) else: ret_type = pytd.NamedType("builtins.NoneType") pytd_sig = pytd.Signature(params=tuple(posonly + params + kwonly), starargs=star, starstarargs=starstar, return_type=ret_type, exceptions=(), template=()) return pytd.Function(name, (pytd_sig, ), pytd.MethodKind.METHOD)
def _function_call_combination_to_signature(self, func, call_combination, num_combinations): node_after, combination, return_value = call_combination params = [] for i, (name, kind, optional) in enumerate(func.get_parameters()): if i < func.nonstararg_count and name in func.signature.annotations: t = func.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, kind, optional, None)) ret = self._function_call_to_return_type(node_after, func, return_value, num_combinations) if func.has_varargs(): if func.signature.varargs_name in func.signature.annotations: annot = func.signature.annotations[func.signature.varargs_name] typ = annot.get_instance_type(node_after) else: typ = pytd.NamedType("builtins.tuple") starargs = pytd.Parameter(func.signature.varargs_name, typ, pytd.ParameterKind.REGULAR, True, None) else: starargs = None if func.has_kwargs(): if func.signature.kwargs_name in func.signature.annotations: annot = func.signature.annotations[func.signature.kwargs_name] typ = annot.get_instance_type(node_after) else: typ = pytd.NamedType("builtins.dict") starstarargs = pytd.Parameter(func.signature.kwargs_name, typ, pytd.ParameterKind.REGULAR, True, None) else: starstarargs = None return pytd.Signature( params=tuple(params), starargs=starargs, starstarargs=starstarargs, return_type=ret, exceptions=(), # TODO(b/159052087): record exceptions template=())
def _call_traces_to_function(call_traces, prefix=""): funcs = collections.defaultdict(pytd_utils.OrderedSet) for funcvar, args, kws, retvar in call_traces: if isinstance(funcvar.data, abstract.BoundFunction): func = funcvar.data.underlying.signatures[0] else: func = funcvar.data.signatures[0] arg_names = func.get_parameter_names() arg_types = (a.data.to_type() for a in func.get_bound_arguments() + list(args)) ret = pytd_utils.JoinTypes(t.to_type() for t in retvar.data) funcs[funcvar.data.name].add(pytd.Signature( tuple(pytd.Parameter(n, t) for n, t in zip(arg_names, arg_types)) + tuple(pytd.Parameter(name, a.data.to_type()) for name, a in kws), ret, has_optional=False, exceptions=(), template=())) functions = [] for name, signatures in funcs.items(): functions.append(pytd.Function(prefix + name, tuple(signatures))) return functions
def VisitParameter(self, p): """Adjust all parameters called "self" to have their parent class type. But do this only if their original type is unoccupied ("object" or, if configured, "?"). Args: p: pytd.Parameter instance. Returns: Adjusted pytd.Parameter instance. """ if not self.class_types: # We're not within a class, so this is not a parameter of a method. return p if p.name == "self" and (self.force or p.type in self.replaced_self_types): return pytd.Parameter("self", self.class_types[-1]) else: return p