def testUnpackUnion(self): """Test for UnpackUnion.""" ast = self.Parse(""" c1 = ... # type: int or float c2 = ... # type: int c3 = ... # type: list[int or float]""") c1 = ast.Lookup("c1").type c2 = ast.Lookup("c2").type c3 = ast.Lookup("c3").type self.assertItemsEqual(utils.UnpackUnion(c1), c1.type_list) self.assertItemsEqual(utils.UnpackUnion(c2), [c2]) self.assertItemsEqual(utils.UnpackUnion(c3), [c3])
def SignatureHasReturnType(cls, sig, return_type): for desired_type in pytd_utils.UnpackUnion(return_type): if desired_type == return_type: return True elif isinstance(sig.return_type, pytd.UnionType): return desired_type in sig.return_type.type_list else: return False
def convert_constant(self, name, pyval, subst=None, node=None, source_sets=None, discard_concrete_values=False): """Convert a constant to a Variable. This converts a constant to a cfg.Variable. Unlike convert_constant_to_value, it can handle things that need to be represented as a Variable with multiple possible values (i.e., a union type), like pytd.Function. Args: name: The name to give the new variable. pyval: The Python constant to convert. Can be a PyTD definition or a builtin constant. subst: The current type parameters. node: The current CFG node. (For instances) source_sets: An iterator over instances of SourceSet (or just tuples). discard_concrete_values: Whether concrete values should be discarded from type parameters. Returns: A cfg.Variable. Raises: TypeParameterError: if conversion is attempted on a type parameter without a substitution. ValueError: if pytype is not of a known type. """ source_sets = source_sets or [[]] node = node or self.vm.root_cfg_node if isinstance(pyval, pytd.UnionType): options = [self.convert_constant_to_value(pytd.Print(t), t, subst, node) for t in pyval.type_list] return self.vm.program.NewVariable(name, options, [], self.vm.root_cfg_node) elif isinstance(pyval, pytd.NothingType): return self.vm.program.NewVariable(name, [], [], self.vm.root_cfg_node) elif isinstance(pyval, pytd.Alias): return self.convert_constant(pytd.Print(pyval), pyval.type, subst, node, source_sets, discard_concrete_values) elif isinstance(pyval, abstract.AsInstance): cls = pyval.cls if isinstance(cls, pytd.AnythingType): return self.create_new_unsolvable(node, "?") if hasattr(cls, "name"): name = cls.name else: name = cls.__class__.__name__ var = self.vm.program.NewVariable(name) for t in pytd_utils.UnpackUnion(cls): if isinstance(t, pytd.TypeParameter): if not subst or t.name not in subst: raise self.TypeParameterError(t.name) else: for v in subst[t.name].bindings: for source_set in source_sets: var.AddBinding(self._get_maybe_abstract_instance(v.data) if discard_concrete_values else v.data, source_set + [v], node) elif isinstance(t, pytd.NothingType): pass else: value = self.convert_constant_to_value( name, abstract.AsInstance(t), subst, node) for source_set in source_sets: var.AddBinding(value, source_set, node) return var elif isinstance(pyval, pytd.Constant): return self.convert_constant( name, abstract.AsInstance(pyval.type), subst, node, source_sets, discard_concrete_values) result = self.convert_constant_to_value(name, pyval, subst, node) if result is not None: return result.to_variable(self.vm.root_cfg_node, name) # There might still be bugs on the abstract intepreter when it returns, # e.g. a list of values instead of a list of types: assert pyval.__class__ != cfg.Variable, pyval if pyval.__class__ == tuple: # TODO(ampere): This does not allow subclasses. Handle namedtuple # correctly. # This case needs to go at the end because many things are actually also # tuples. return self.build_tuple( self.vm.root_cfg_node, (self.convert_constant("tuple[%d]" % i, v, subst, node, source_sets, discard_concrete_values) for i, v in enumerate(pyval))) raise ValueError( "Cannot convert {} to an abstract value".format(pyval.__class__))