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(pytd_utils.UnpackUnion(c1), c1.type_list) self.assertItemsEqual(pytd_utils.UnpackUnion(c2), [c2]) self.assertItemsEqual(pytd_utils.UnpackUnion(c3), [c3])
def test_unpack_union(self): """Test for UnpackUnion.""" ast = self.Parse(""" from typing import Union c1 = ... # type: Union[int, float] c2 = ... # type: int c3 = ... # type: list[Union[int, float]]""") c1 = ast.Lookup("c1").type c2 = ast.Lookup("c2").type c3 = ast.Lookup("c3").type six.assertCountEqual(self, pytd_utils.UnpackUnion(c1), c1.type_list) six.assertCountEqual(self, pytd_utils.UnpackUnion(c2), [c2]) six.assertCountEqual(self, pytd_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 constant_to_var(self, 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 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: 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.NothingType): return self.vm.program.NewVariable([], [], self.vm.root_cfg_node) elif isinstance(pyval, pytd.Alias): return self.constant_to_var(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) elif (isinstance(pyval, abstract.AsReturnValue) and isinstance(cls, pytd.NothingType)): return self.no_return.to_variable(node) var = self.vm.program.NewVariable() 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.constant_to_value(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.constant_to_var(abstract.AsInstance(pyval.type), subst, node, source_sets, discard_concrete_values) result = self.constant_to_value(pyval, subst, node) if result is not None: return result.to_variable(node) # 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.constant_to_var( 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__))