def test_metaclass(self): cls = abstract.InterpreterClass("X", [], {}, None, self._ctx) meta = abstract.InterpreterClass("M", [], {}, None, self._ctx) meta.official_name = "M" cls.cls = meta pytd_cls = cls.to_pytd_def(self._ctx.root_node, "X") self.assertEqual(pytd_cls.metaclass, pytd.NamedType("M"))
def test_class_no_valself(self): meta_members = {"x": self.ctx.convert.none.to_variable(self.node)} meta = abstract.InterpreterClass("M", [], meta_members, None, self.ctx) cls = abstract.InterpreterClass("X", [], {}, meta, self.ctx) _, attr_var = self.attribute_handler.get_attribute(self.node, cls, "x") # Since `valself` was not passed to get_attribute, we do not look at the # metaclass, so M.x is not returned. self.assertIsNone(attr_var)
def test_class_with_class_valself(self): meta_members = {"x": self.ctx.convert.none.to_variable(self.node)} meta = abstract.InterpreterClass("M", [], meta_members, None, self.ctx) cls = abstract.InterpreterClass("X", [], {}, meta, self.ctx) valself = cls.to_binding(self.node) _, attr_var = self.attribute_handler.get_attribute( self.node, cls, "x", valself) # Since `valself` is X itself, we look at the metaclass and return M.x. self.assertEqual(attr_var.data, [self.ctx.convert.none])
def test_class_with_instance_valself(self): meta_members = {"x": self.ctx.convert.none.to_variable(self.node)} meta = abstract.InterpreterClass("M", [], meta_members, None, self.ctx) cls = abstract.InterpreterClass("X", [], {}, meta, self.ctx) valself = abstract.Instance(cls, self.ctx).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_metaclass_union(self): cls = abstract.InterpreterClass("X", [], {}, None, self._ctx) meta1 = abstract.InterpreterClass("M1", [], {}, None, self._ctx) meta2 = abstract.InterpreterClass("M2", [], {}, None, self._ctx) meta1.official_name = "M1" meta2.official_name = "M2" cls.cls = abstract.Union([meta1, meta2], self._ctx) pytd_cls = cls.to_pytd_def(self._ctx.root_node, "X") self.assertEqual(pytd_cls.metaclass, pytd.UnionType( (pytd.NamedType("M1"), pytd.NamedType("M2"))))
def test_inherited_metaclass(self): base = abstract.InterpreterClass("X", [], {}, None, self._ctx) base.official_name = "X" meta = abstract.InterpreterClass("M", [], {}, None, self._ctx) meta.official_name = "M" base.cls = meta child = abstract.InterpreterClass("Y", [base.to_variable(self._ctx.root_node)], {}, None, self._ctx) self.assertIs(child.cls, base.cls) pytd_cls = child.to_pytd_def(self._ctx.root_node, "Y") self.assertIs(pytd_cls.metaclass, None)
def test_type_parameter_equality(self): param1 = abstract.TypeParameter("S", self._ctx) param2 = abstract.TypeParameter("T", self._ctx) cls = abstract.InterpreterClass("S", [], {}, None, self._ctx) self.assertEqual(param1, param1) self.assertNotEqual(param1, param2) self.assertNotEqual(param1, cls)
def test_union_equality(self): union1 = abstract.Union((self._ctx.convert.unsolvable,), self._ctx) union2 = abstract.Union((self._ctx.convert.none,), self._ctx) cls = abstract.InterpreterClass("Union", [], {}, None, self._ctx) self.assertEqual(union1, union1) self.assertNotEqual(union1, union2) self.assertNotEqual(union1, cls)
def test_inherited_abstract_method(self): sized_pytd = self._ctx.loader.typing.Lookup("typing.Sized") sized = self._ctx.convert.constant_to_value(sized_pytd, {}, self._ctx.root_node) cls = abstract.InterpreterClass("X", [sized.to_variable(self._ctx.root_node)], {}, None, self._ctx) self.assertCountEqual(cls.abstract_methods, {"__len__"})
def test_overridden_abstract_method(self): sized_pytd = self._ctx.loader.typing.Lookup("typing.Sized") sized = self._ctx.convert.constant_to_value(sized_pytd, {}, self._ctx.root_node) bases = [sized.to_variable(self._ctx.root_node)] members = {"__len__": self._ctx.new_unsolvable(self._ctx.root_node)} cls = abstract.InterpreterClass("X", bases, members, None, self._ctx) self.assertFalse(cls.abstract_methods)
def test_getitem_with_instance_valself(self): cls = abstract.InterpreterClass("X", [], {}, None, self.ctx) valself = abstract.Instance(cls, self.ctx).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 test_getitem_no_valself(self): cls = abstract.InterpreterClass("X", [], {}, None, self.ctx) _, attr_var = self.attribute_handler.get_attribute( self.node, cls, "__getitem__") attr, = attr_var.data # Since we looked up __getitem__ on a class without passing in `valself`, # the class is treated as an annotation. self.assertIs(attr.func.__func__, abstract.AnnotationClass.getitem_slot)
def test_overridden_abstract_method_still_abstract(self): sized_pytd = self._ctx.loader.typing.Lookup("typing.Sized") sized = self._ctx.convert.constant_to_value(sized_pytd, {}, self._ctx.root_node) bases = [sized.to_variable(self._ctx.root_node)] func = abstract.Function("__len__", self._ctx) func.is_abstract = True members = {"__len__": func.to_variable(self._ctx.root_node)} cls = abstract.InterpreterClass("X", bases, members, None, self._ctx) self.assertCountEqual(cls.abstract_methods, {"__len__"})
def test_interpreter_class_official_name(self): cls = abstract.InterpreterClass("X", [], {}, None, self._ctx) cls.update_official_name("Z") self.assertEqual(cls.official_name, "Z") cls.update_official_name("A") # takes effect because A < Z self.assertEqual(cls.official_name, "A") cls.update_official_name("Z") # no effect self.assertEqual(cls.official_name, "A") cls.update_official_name("X") # takes effect because X == cls.name self.assertEqual(cls.official_name, "X") cls.update_official_name("A") # no effect self.assertEqual(cls.official_name, "X")
def test_union_set_attribute(self): list_instance = abstract.Instance(self._ctx.convert.list_type, self._ctx) cls = abstract.InterpreterClass("obj", [], {}, None, self._ctx) cls_instance = abstract.Instance(cls, self._ctx) union = abstract.Union([cls_instance, list_instance], self._ctx) node = self._ctx.attribute_handler.set_attribute( self._ctx.root_node, union, "rumpelstiltskin", self._ctx.convert.none_type.to_variable(self._ctx.root_node)) self.assertEqual(cls_instance.members["rumpelstiltskin"].data.pop(), self._ctx.convert.none_type) self.assertIs(node, self._ctx.root_node) error, = self._ctx.errorlog.unique_sorted_errors() self.assertEqual(error.name, "not-writable")
def test_instantiate_interpreter_class(self): cls = abstract.InterpreterClass("X", [], {}, None, self._ctx) # When there is no current frame, create a new instance every time. v1 = abstract_utils.get_atomic_value(cls.instantiate(self._node)) v2 = abstract_utils.get_atomic_value(cls.instantiate(self._node)) self.assertIsNot(v1, v2) # Create one instance per opcode. fake_opcode = object() self._ctx.vm.push_frame(frame_state.SimpleFrame(fake_opcode)) v3 = abstract_utils.get_atomic_value(cls.instantiate(self._node)) v4 = abstract_utils.get_atomic_value(cls.instantiate(self._node)) self.assertIsNot(v1, v3) self.assertIsNot(v2, v3) self.assertIs(v3, v4)
def test_abstract_method(self): func = abstract.Function("f", self._ctx).to_variable(self._ctx.root_node) func.data[0].is_abstract = True cls = abstract.InterpreterClass("X", [], {"f": func}, None, self._ctx) self.assertCountEqual(cls.abstract_methods, {"f"})
def test_compatible_with(self): cls = abstract.InterpreterClass("X", [], {}, None, self._ctx) self.assertTruthy(cls)