def test_union_set_attribute(self): list_instance = abstract.Instance(self._vm.convert.list_type, self._vm) cls = abstract.InterpreterClass("obj", [], {}, None, self._vm) cls_instance = abstract.Instance(cls, self._vm) union = abstract.Union([cls_instance, list_instance], self._vm) node = self._vm.attribute_handler.set_attribute( self._vm.root_cfg_node, union, "rumpelstiltskin", self._vm.convert.none_type.to_variable(self._vm.root_cfg_node)) self.assertEqual(cls_instance.members["rumpelstiltskin"].data.pop(), self._vm.convert.none_type) self.assertIs(node, self._vm.root_cfg_node) error, = self._vm.errorlog.unique_sorted_errors() self.assertEqual(error.name, "not-writable")
def build_set(self, node, content): """Create a VM set from the given sequence.""" content = list(content) # content might be a generator value = abstract.Instance(self.set_type, self.vm, node) value.initialize_type_parameter(node, "T", self.build_content(node, content)) return value.to_variable(node, name="set(...)")
def test_compatible_with_none(self): # This test is specifically for abstract.Instance, so we don't use # self._vm.convert.none, which is an AbstractOrConcreteValue. i = abstract.Instance( self._vm.convert.none_type, self._vm, self._node) self.assertIs(False, i.compatible_with(True)) self.assertIs(True, i.compatible_with(False))
def create_kwargs(self, node): key_type = self.convert.primitive_class_instances[str].to_variable(node) value_type = self.convert.create_new_unknown(node) kwargs = abstract.Instance(self.convert.dict_type, self) kwargs.merge_instance_type_parameter(node, abstract_utils.K, key_type) kwargs.merge_instance_type_parameter(node, abstract_utils.V, value_type) return kwargs.to_variable(node)
def testAnyStrInstanceAgainstAnyStr(self): right = self.vm.convert.name_to_value("typing.AnyStr") dummy_instance = abstract.Instance(self.vm.convert.tuple_type, self.vm) left = abstract.TypeParameterInstance(right, dummy_instance, self.vm) for result in self._match_var(left, right): self.assertItemsEqual([(name, var.data) for name, var in result.items()], [("AnyStr", [left])])
def _setup_pytdclass(self, node, cls): # We need to rewrite the member map of the PytdClass. members = dict(cls._member_map) # pylint: disable=protected-access member_types = [] for name, pytd_val in members.items(): # Only constants need to be transformed. # TODO(tsudol): Ensure only valid enum members are transformed. if not isinstance(pytd_val, pytd.Constant): continue # Build instances directly, because you can't call instantiate() when # creating the class -- pytype complains about recursive types. member = abstract.Instance(cls, self.vm) member.members["name"] = self.vm.convert.constant_to_var( pyval=pytd.Constant(name="name", type=self._str_pytd), node=node) member.members["value"] = self.vm.convert.constant_to_var( pyval=pytd.Constant(name="value", type=pytd_val.type), node=node) cls._member_map[name] = member # pylint: disable=protected-access cls.members[name] = member.to_variable(node) member_types.append(pytd_val.type) member_type = self.vm.convert.constant_to_value( pytd_utils.JoinTypes(member_types)) cls.members["__new__"] = self._make_new(node, member_type, cls) cls.members["__eq__"] = EnumCmpEQ(self.vm).to_variable(node) return node
def build_set(self, node, content): """Create a VM set from the given sequence.""" content = list(content) # content might be a generator value = abstract.Instance(self.set_type, self.vm) value.merge_type_parameter(node, abstract.T, self.build_content(content)) return value.to_variable(node)
def test_getitem_with_instance_valself(self): cls = abstract.InterpreterClass("X", [], {}, None, self.vm) valself = abstract.Instance(cls, self.vm).to_binding(self.node) _, attr_var = self.attribute_handler.get_attribute( self.node, cls, "__getitem__", valself) # Since we passed in `valself` for this lookup of __getitem__ on a class, # it is treated as a normal lookup; X.__getitem__ does not exist. self.assertIsNone(attr_var)
def testToTypeWithView2(self): # to_type(<instance of <str or unsolvable>>, view={__class__: str}) instance = abstract.Instance(self._vm.convert.unsolvable, self._vm) cls_binding = instance.cls.AddBinding( self._vm.convert.str_type, [], self._vm.root_cfg_node) view = {instance.cls: cls_binding} pytd_type = instance.to_type(self._vm.root_cfg_node, seen=None, view=view) self.assertEqual("__builtin__.str", pytd_type.name)
def build_list(self, node, content): """Create a VM list from the given sequence.""" # TODO(rechen): set T to empty if there is nothing in content content = list(content) # content might be a generator value = abstract.Instance(self.list_type, self.vm, node) value.initialize_type_parameter(node, abstract.T, self.build_content(node, content)) return value.to_variable(node)
def test_compatible_with_set(self): i = abstract.Instance(self._vm.convert.set_type, self._vm) # Empty set is not compatible with True. self.assertFalsy(i) # Once a type parameter is set, list is compatible with True and False. i.merge_instance_type_parameter( self._node, abstract_utils.T, self._vm.convert.object_type.to_variable(self._vm.root_cfg_node)) self.assertAmbiguous(i)
def test_call_empty_type_parameter_instance(self): instance = abstract.Instance(self._vm.convert.list_type, self._vm) t = abstract.TypeParameter(abstract_utils.T, self._vm) t_instance = abstract.TypeParameterInstance(t, instance, self._vm) node, ret = t_instance.call(self._node, t_instance.to_binding(self._node), function.Args(posargs=())) self.assertIs(node, self._node) retval, = ret.data self.assertIs(retval, self._vm.convert.empty)
def create_kwargs(self, node): key_type = self.convert.primitive_class_instances[str].to_variable( node, "str") value_type = self.convert.create_new_unknown(node, "kwargs_value") kwargs = abstract.Instance(self.convert.dict_type, self, node) kwargs.overwrite_type_parameter( node, abstract.Dict.KEY_TYPE_PARAM, key_type) kwargs.overwrite_type_parameter( node, abstract.Dict.VALUE_TYPE_PARAM, value_type) return kwargs.to_variable(node, "**kwargs")
def test_instance_with_valself(self): instance = abstract.Instance(self.vm.convert.int_type, self.vm) valself = instance.to_binding(self.node) _, attr_var = self.attribute_handler.get_attribute( self.node, instance, "real", valself) attr_binding, = attr_var.bindings self.assertEqual(attr_binding.data.cls, self.vm.convert.int_type) # Since `valself` was passed to get_attribute, it is added to the # attribute's origins. self.assertIn(valself, _get_origins(attr_binding))
def test_instance_no_valself(self): instance = abstract.Instance(self.vm.convert.int_type, self.vm) _, attr_var = self.attribute_handler.get_attribute( self.node, instance, "real") attr_binding, = attr_var.bindings self.assertEqual(attr_binding.data.cls, self.vm.convert.int_type) # Since `valself` was not passed to get_attribute, a binding to # `instance` is not among the attribute's origins. self.assertNotIn(instance, [o.data for o in _get_origins(attr_binding)])
def test_class_with_instance_valself(self): meta_members = {"x": self.vm.convert.none.to_variable(self.node)} meta = abstract.InterpreterClass("M", [], meta_members, None, self.vm) cls = abstract.InterpreterClass("X", [], {}, meta, self.vm) valself = abstract.Instance(cls, self.vm).to_binding(self.node) _, attr_var = self.attribute_handler.get_attribute( self.node, cls, "x", valself) # Since `valself` is an instance of X, we do not look at the metaclass, so # M.x is not returned. self.assertIsNone(attr_var)
def test_empty_type_parameter_instance(self): t = abstract.TypeParameter( abstract_utils.T, self._vm, bound=self._vm.convert.int_type) instance = abstract.Instance(self._vm.convert.list_type, self._vm) t_instance = abstract.TypeParameterInstance(t, instance, self._vm) node, var = self._vm.attribute_handler.get_attribute( self._vm.root_cfg_node, t_instance, "real") self.assertIs(node, self._vm.root_cfg_node) attr, = var.data self.assertIs(attr, self._vm.convert.primitive_class_instances[int])
def test_compatible_with_set(self): i = abstract.Instance(self._vm.convert.set_type, self._vm, self._node) i.init_type_parameters("T") # Empty list is not compatible with True. self.assertIs(False, i.compatible_with(True)) self.assertIs(True, i.compatible_with(False)) # Once a type parameter is set, list is compatible with True and False. i.merge_type_parameter(self._node, "T", self._vm.convert.object_type) self.assertIs(True, i.compatible_with(True)) self.assertIs(True, i.compatible_with(False))
def testToTypeWithViewAndEmptyParam(self): instance = abstract.Instance(self._vm.convert.list_type, self._vm, self._vm.root_cfg_node) instance.type_parameters["T"] = self._vm.program.NewVariable() view = {instance.cls: instance.cls.bindings[0]} pytd_type = instance.to_type(self._vm.root_cfg_node, seen=None, view=view) self.assertEquals("__builtin__.list", pytd_type.base_type.name) self.assertSequenceEqual((pytd.NothingType(), ), pytd_type.parameters)
def testCallEmptyTypeParameterInstance(self): instance = abstract.Instance(self._vm.convert.list_type, self._vm) instance.initialize_type_parameter( self._node, abstract.T, self._vm.program.NewVariable()) t = abstract.TypeParameter(abstract.T, self._vm) t_instance = abstract.TypeParameterInstance(t, instance, self._vm) node, ret = t_instance.call(self._node, t_instance.to_binding(self._node), abstract.FunctionArgs(posargs=())) self.assertIs(node, self._node) retval, = ret.data self.assertIs(retval, self._vm.convert.empty)
def testCallTypeParameterInstance(self): instance = abstract.Instance(self._vm.convert.list_type, self._vm) instance.initialize_type_parameter( self._node, abstract.T, self._vm.convert.int_type.to_variable(self._vm.root_cfg_node)) t = abstract.TypeParameter(abstract.T, self._vm) t_instance = abstract.TypeParameterInstance(t, instance, self._vm) node, ret = t_instance.call(self._node, t_instance.to_binding(self._node), abstract.FunctionArgs(posargs=())) self.assertIs(node, self._node) retval, = ret.data self.assertListEqual(retval.cls.data, [self._vm.convert.int_type])
def testToTypeWithView2(self): # to_type(<instance of <str or unsolvable>>, view={__class__: str}) cls = self._vm.program.NewVariable([self._vm.convert.unsolvable], [], self._vm.root_cfg_node) cls_binding = cls.AddBinding(self._vm.convert.str_type.data[0], [], self._vm.root_cfg_node) instance = abstract.Instance(cls, self._vm, self._vm.root_cfg_node) view = {cls: cls_binding} pytd_type = instance.to_type(self._vm.root_cfg_node, seen=None, view=view) self.assertEquals("__builtin__.str", pytd_type.name)
def test_call_type_parameter_instance(self): instance = abstract.Instance(self._vm.convert.list_type, self._vm) instance.merge_instance_type_parameter( self._vm.root_cfg_node, abstract_utils.T, self._vm.convert.int_type.to_variable(self._vm.root_cfg_node)) t = abstract.TypeParameter(abstract_utils.T, self._vm) t_instance = abstract.TypeParameterInstance(t, instance, self._vm) node, ret = t_instance.call(self._node, t_instance.to_binding(self._node), function.Args(posargs=())) self.assertIs(node, self._node) retval, = ret.data self.assertEqual(retval.cls, self._vm.convert.int_type)
def testCallTypeParameterInstanceWithWrongArgs(self): instance = abstract.Instance(self._vm.convert.list_type, self._vm) instance.initialize_type_parameter( self._node, abstract.T, self._vm.convert.int_type.to_variable(self._vm.root_cfg_node)) t = abstract.TypeParameter(abstract.T, self._vm) t_instance = abstract.TypeParameterInstance(t, instance, self._vm) posargs = (self._vm.convert.create_new_unsolvable(self._node),) * 3 node, ret = t_instance.call(self._node, t_instance.to_binding(self._node), abstract.FunctionArgs(posargs=posargs)) self.assertIs(node, self._node) self.assertTrue(ret.bindings) error, = self._vm.errorlog self.assertEqual(error.name, "wrong-arg-count")
def test_call_type_parameter_instance_with_wrong_args(self): instance = abstract.Instance(self._vm.convert.list_type, self._vm) instance.merge_instance_type_parameter( self._vm.root_cfg_node, abstract_utils.T, self._vm.convert.int_type.to_variable(self._vm.root_cfg_node)) t = abstract.TypeParameter(abstract_utils.T, self._vm) t_instance = abstract.TypeParameterInstance(t, instance, self._vm) posargs = (self._vm.new_unsolvable(self._node),) * 3 node, ret = t_instance.call(self._node, t_instance.to_binding(self._node), function.Args(posargs=posargs)) self.assertIs(node, self._node) self.assertTrue(ret.bindings) error, = self._vm.errorlog self.assertEqual(error.name, "wrong-arg-count")
def instantiate(self, node, container=None): # Instantiate creates a canonical enum member. This intended for when no # particular enum member is needed, e.g. during analysis. Real members have # these fields set during class creation. # TODO(tsudol): Use the types of other members to set `value`. del container instance = abstract.Instance(self, self.vm) instance.members["name"] = self.vm.convert.build_string(node, "") if self.member_type: value = self.member_type.instantiate(node) else: # instantiate() should never be called before setup_interpreterclass sets # self.member_type, because pytype will complain about recursive types. # But there's no reason not to make sure this function is safe. value = self.vm.new_unsolvable(node) instance.members["value"] = value return instance.to_variable(node)
def _setup_interpreterclass(self, node, cls): member_types = [] for name, value in self._get_class_locals(node, cls.name, cls.members): # Build instances directly, because you can't call instantiate() when # creating the class -- pytype complains about recursive types. member = abstract.Instance(cls, self.vm) member.members["value"] = value.orig member.members["name"] = self.vm.convert.build_string(node, name) cls.members[name] = member.to_variable(node) member_types.extend(value.orig.data) if not member_types: member_types.append(self.vm.convert.unsolvable) member_type = self.vm.convert.merge_classes(member_types) cls.member_type = member_type cls.members["__new__"] = self._make_new(node, member_type, cls) cls.members["__eq__"] = EnumCmpEQ(self.vm).to_variable(node) return node
def testToTypeWithView1(self): # to_type(<instance of List[int or unsolvable]>, view={T: int}) instance = abstract.Instance(self._vm.convert.list_type, self._vm, self._vm.root_cfg_node) instance.type_parameters["T"] = self._vm.program.NewVariable( [self._vm.convert.unsolvable], [], self._vm.root_cfg_node) param_binding = instance.type_parameters["T"].AddBinding( self._vm.convert.primitive_class_instances[int], [], self._vm.root_cfg_node) view = { instance.cls: instance.cls.bindings[0], instance.type_parameters["T"]: param_binding, param_binding.data.cls: param_binding.data.cls.bindings[0] } pytd_type = instance.to_type(self._vm.root_cfg_node, seen=None, view=view) self.assertEquals("__builtin__.list", pytd_type.base_type.name) self.assertSetEqual({"__builtin__.int"}, {t.name for t in pytd_type.parameters})
def test_compatible_with_non_container(self): # Compatible with either True or False. i = abstract.Instance(self._vm.convert.object_type, self._vm) self.assertIs(True, i.compatible_with(True)) self.assertIs(True, i.compatible_with(False))
def _constant_to_value(self, pyval, subst, get_node): """Create a AtomicAbstractValue that represents a python constant. This supports both constant from code constant pools and PyTD constants such as classes. This also supports builtin python objects such as int and float. Args: pyval: The python or PyTD value to convert. subst: The current type parameters. get_node: A getter function for the current node. Returns: A Value that represents the constant, or None if we couldn't convert. Raises: NotImplementedError: If we don't know how to convert a value. TypeParameterError: If we can't find a substitution for a type parameter. """ if pyval.__class__ is str: # We use a subclass of str, compat.BytesPy3, to mark Python 3 # bytestrings, which are converted to abstract bytes instances. # compat.BytesType dispatches to this when appropriate. return abstract.AbstractOrConcreteValue(pyval, self.str_type, self.vm) elif isinstance(pyval, compat.UnicodeType): return abstract.AbstractOrConcreteValue(pyval, self.unicode_type, self.vm) elif isinstance(pyval, compat.BytesType): return abstract.AbstractOrConcreteValue(pyval, self.bytes_type, self.vm) elif isinstance(pyval, bool): return self.true if pyval is True else self.false elif isinstance(pyval, int) and -1 <= pyval <= MAX_IMPORT_DEPTH: # For small integers, preserve the actual value (for things like the # level in IMPORT_NAME). return abstract.AbstractOrConcreteValue(pyval, self.int_type, self.vm) elif isinstance(pyval, compat.LongType): # long is aliased to int return self.primitive_class_instances[int] elif pyval.__class__ in self.primitive_classes: return self.primitive_class_instances[pyval.__class__] elif isinstance(pyval, (loadmarshal.CodeType, blocks.OrderedCode)): return abstract.AbstractOrConcreteValue( pyval, self.primitive_classes[types.CodeType], self.vm) elif pyval is super: return special_builtins.Super(self.vm) elif pyval is object: return special_builtins.Object(self.vm) elif pyval.__class__ is type: try: return self.name_to_value(self._type_to_name(pyval), subst) except (KeyError, AttributeError): log.debug("Failed to find pytd", exc_info=True) raise elif isinstance(pyval, pytd.LateType): actual = self._load_late_type(pyval) return self._constant_to_value(actual, subst, get_node) elif isinstance(pyval, pytd.TypeDeclUnit): return self._create_module(pyval) elif isinstance(pyval, pytd.Module): mod = self.vm.loader.import_name(pyval.module_name) return self._create_module(mod) elif isinstance(pyval, pytd.Class): if pyval.name == "__builtin__.super": return self.vm.special_builtins["super"] elif pyval.name == "__builtin__.object": return self.object_type elif pyval.name == "types.ModuleType": return self.module_type elif pyval.name == "_importlib_modulespec.ModuleType": # Python 3's typeshed uses a stub file indirection to define ModuleType # even though it is exported via types.pyi. return self.module_type else: module, dot, base_name = pyval.name.rpartition(".") try: cls = abstract.PyTDClass(base_name, pyval, self.vm) except mro.MROError as e: self.vm.errorlog.mro_error(self.vm.frames, base_name, e.mro_seqs) cls = self.unsolvable else: if dot: cls.module = module return cls elif isinstance(pyval, pytd.Function): signatures = [ abstract.PyTDSignature(pyval.name, sig, self.vm) for sig in pyval.signatures ] type_new = self.vm.lookup_builtin("__builtin__.type").Lookup( "__new__") if pyval is type_new: f_cls = special_builtins.TypeNew else: f_cls = abstract.PyTDFunction f = f_cls(pyval.name, signatures, pyval.kind, self.vm) f.is_abstract = pyval.is_abstract return f elif isinstance(pyval, pytd.ClassType): assert pyval.cls return self.constant_to_value(pyval.cls, subst, self.vm.root_cfg_node) elif isinstance(pyval, pytd.NothingType): return self.empty elif isinstance(pyval, pytd.AnythingType): return self.unsolvable elif (isinstance(pyval, pytd.Constant) and isinstance(pyval.type, pytd.AnythingType)): # We allow "X = ... # type: Any" to declare X as a type. return self.unsolvable elif isinstance(pyval, pytd.FunctionType): return self.constant_to_value(pyval.function, subst, self.vm.root_cfg_node) elif isinstance(pyval, pytd.UnionType): options = [ self.constant_to_value(t, subst, self.vm.root_cfg_node) for t in pyval.type_list ] if len(options) > 1: return abstract.Union(options, self.vm) else: return options[0] elif isinstance(pyval, pytd.TypeParameter): constraints = tuple( self.constant_to_value(c, {}, self.vm.root_cfg_node) for c in pyval.constraints) bound = (pyval.bound and self.constant_to_value( pyval.bound, {}, self.vm.root_cfg_node)) return abstract.TypeParameter(pyval.name, self.vm, constraints=constraints, bound=bound) elif isinstance(pyval, abstract.AsInstance): cls = pyval.cls if isinstance(cls, pytd.LateType): actual = self._load_late_type(cls) if not isinstance(actual, pytd.ClassType): return self.unsolvable cls = actual.cls if isinstance(cls, pytd.ClassType): cls = cls.cls if (isinstance(cls, pytd.GenericType) and cls.base_type.name == "typing.ClassVar"): param, = cls.parameters return self.constant_to_value(abstract.AsInstance(param), subst, self.vm.root_cfg_node) elif isinstance(cls, pytd.GenericType) or (isinstance(cls, pytd.Class) and cls.template): # If we're converting a generic Class, need to create a new instance of # it. See test_classes.testGenericReinstantiated. if isinstance(cls, pytd.Class): params = tuple(t.type_param.upper_value for t in cls.template) cls = pytd.GenericType(base_type=pytd.ClassType( cls.name, cls), parameters=params) if isinstance(cls.base_type, pytd.LateType): actual = self._load_late_type(cls.base_type) if not isinstance(actual, pytd.ClassType): return self.unsolvable base_cls = actual.cls else: assert isinstance(cls.base_type, pytd.ClassType) base_cls = cls.base_type.cls assert isinstance(base_cls, pytd.Class), base_cls if base_cls.name == "__builtin__.type": c, = cls.parameters if isinstance(c, pytd.TypeParameter): if not subst or c.name not in subst: raise self.TypeParameterError(c.name) return self.merge_classes(get_node(), subst[c.name].data) else: return self.constant_to_value(c, subst, self.vm.root_cfg_node) elif isinstance(cls, pytd.TupleType): content = tuple( self.constant_to_var(abstract.AsInstance(p), subst, get_node()) for p in cls.parameters) return abstract.Tuple(content, self.vm) elif isinstance(cls, pytd.CallableType): clsval = self.constant_to_value(cls, subst, self.vm.root_cfg_node) return abstract.Instance(clsval, self.vm) else: clsval = self.constant_to_value(base_cls, subst, self.vm.root_cfg_node) instance = abstract.Instance(clsval, self.vm) num_params = len(cls.parameters) assert num_params <= len(base_cls.template) for i, formal in enumerate(base_cls.template): if i < num_params: node = get_node() p = self.constant_to_var( abstract.AsInstance(cls.parameters[i]), subst, node) else: # An omitted type parameter implies `Any`. node = self.vm.root_cfg_node p = self.unsolvable.to_variable(node) instance.merge_type_parameter(node, formal.name, p) return instance elif isinstance(cls, pytd.Class): assert not cls.template # This key is also used in __init__ key = (abstract.Instance, cls) if key not in self._convert_cache: if cls.name in [ "__builtin__.type", "__builtin__.property" ]: # An instance of "type" or of an anonymous property can be anything. instance = self._create_new_unknown_value("type") else: mycls = self.constant_to_value(cls, subst, self.vm.root_cfg_node) instance = abstract.Instance(mycls, self.vm) log.info("New pytd instance for %s: %r", cls.name, instance) self._convert_cache[key] = instance return self._convert_cache[key] else: return self.constant_to_value(cls, subst, self.vm.root_cfg_node) elif (isinstance(pyval, pytd.GenericType) and pyval.base_type.name == "typing.ClassVar"): param, = pyval.parameters return self.constant_to_value(param, subst, self.vm.root_cfg_node) elif isinstance(pyval, pytd.GenericType): if isinstance(pyval.base_type, pytd.LateType): actual = self._load_late_type(pyval.base_type) if not isinstance(actual, pytd.ClassType): return self.unsolvable base = actual.cls else: assert isinstance(pyval.base_type, pytd.ClassType) base = pyval.base_type.cls assert isinstance(base, pytd.Class), base base_cls = self.constant_to_value(base, subst, self.vm.root_cfg_node) if not isinstance(base_cls, abstract.Class): # base_cls can be, e.g., an unsolvable due to an mro error. return self.unsolvable if isinstance(pyval, pytd.TupleType): abstract_class = abstract.TupleClass template = list(range(len(pyval.parameters))) + [abstract.T] parameters = pyval.parameters + (pytd.UnionType( pyval.parameters), ) elif isinstance(pyval, pytd.CallableType): abstract_class = abstract.Callable template = list(range(len( pyval.args))) + [abstract.ARGS, abstract.RET] parameters = pyval.args + (pytd_utils.JoinTypes( pyval.args), pyval.ret) else: abstract_class = abstract.ParameterizedClass template = tuple(t.name for t in base.template) parameters = pyval.parameters assert (pyval.base_type.name == "typing.Generic" or len(parameters) <= len(template)) type_parameters = datatypes.LazyDict() for i, name in enumerate(template): if i < len(parameters): type_parameters.add_lazy_item(name, self.constant_to_value, parameters[i], subst, self.vm.root_cfg_node) else: type_parameters[name] = self.unsolvable return abstract_class(base_cls, type_parameters, self.vm) elif pyval.__class__ is tuple: # only match raw tuple, not namedtuple/Node return self.tuple_to_value([ self.constant_to_var(item, subst, self.vm.root_cfg_node) for i, item in enumerate(pyval) ]) else: raise NotImplementedError("Can't convert constant %s %r" % (type(pyval), pyval))