def test_is_subtype_supertype_unrelated_classes(self): cls_a, cls_b = builder.extract_node(''' class A(object): pass #@ class B(object): pass #@ ''') self.assertFalse(helpers.is_subtype(cls_a, cls_b)) self.assertFalse(helpers.is_subtype(cls_b, cls_a)) self.assertFalse(helpers.is_supertype(cls_a, cls_b)) self.assertFalse(helpers.is_supertype(cls_b, cls_a))
def test_is_subtype_supertype_unknown_bases(self): cls_a, cls_b = builder.extract_node(''' from unknown import Unknown class A(Unknown): pass #@ class B(A): pass #@ ''') with self.assertRaises(exceptions._NonDeducibleTypeHierarchy): helpers.is_subtype(cls_a, cls_b) with self.assertRaises(exceptions._NonDeducibleTypeHierarchy): helpers.is_supertype(cls_a, cls_b)
def test_is_subtype_supertype_unknown_bases(self) -> None: cls_a, cls_b = builder.extract_node(""" from unknown import Unknown class A(Unknown): pass #@ class B(A): pass #@ """) with self.assertRaises(_NonDeducibleTypeHierarchy): helpers.is_subtype(cls_a, cls_b) with self.assertRaises(_NonDeducibleTypeHierarchy): helpers.is_supertype(cls_a, cls_b)
def test_is_subtype_supertype_old_style_classes(self): cls_a, cls_b = builder.extract_node(''' class A: #@ pass class B(A): #@ pass ''') self.assertFalse(helpers.is_subtype(cls_a, cls_b)) self.assertFalse(helpers.is_subtype(cls_b, cls_a)) self.assertFalse(helpers.is_supertype(cls_a, cls_b)) self.assertFalse(helpers.is_supertype(cls_b, cls_a))
def test_is_subtype_supertype_mro_error(self): cls_e, cls_f = test_utils.extract_node(''' class A(object): pass class B(A): pass class C(A): pass class D(B, C): pass class E(C, B): pass #@ class F(D, E): pass #@ ''') self.assertFalse(helpers.is_subtype(cls_e, cls_f)) self.assertEqual(helpers.is_subtype(cls_f, cls_e), util.Uninferable) self.assertEqual(helpers.is_supertype(cls_e, cls_f), util.Uninferable) self.assertFalse(helpers.is_supertype(cls_f, cls_e))
def _get_binop_flow( left, left_type, binary_opnode, right, right_type, context, reverse_context ): """Get the flow for binary operations. The rules are a bit messy: * if left and right have the same type, then only one method will be called, left.__op__(right) * if left and right are unrelated typewise, then first left.__op__(right) is tried and if this does not exist or returns NotImplemented, then right.__rop__(left) is tried. * if left is a subtype of right, then only left.__op__(right) is tried. * if left is a supertype of right, then right.__rop__(left) is first tried and then left.__op__(right) """ op = binary_opnode.op if _same_type(left_type, right_type): methods = [_bin_op(left, binary_opnode, op, right, context)] elif helpers.is_subtype(left_type, right_type): methods = [_bin_op(left, binary_opnode, op, right, context)] elif helpers.is_supertype(left_type, right_type): methods = [ _bin_op(right, binary_opnode, op, left, reverse_context, reverse=True), _bin_op(left, binary_opnode, op, right, context), ] else: methods = [ _bin_op(left, binary_opnode, op, right, context), _bin_op(right, binary_opnode, op, left, reverse_context, reverse=True), ] return methods
def _get_aug_flow(left, left_type, aug_opnode, right, right_type, context, reverse_context): """Get the flow for augmented binary operations. The rules are a bit messy: * if left and right have the same type, then left.__augop__(right) is first tried and then left.__op__(right). * if left and right are unrelated typewise, then left.__augop__(right) is tried, then left.__op__(right) is tried and then right.__rop__(left) is tried. * if left is a subtype of right, then left.__augop__(right) is tried and then left.__op__(right). * if left is a supertype of right, then left.__augop__(right) is tried, then right.__rop__(left) and then left.__op__(right) """ bin_op = aug_opnode.op.strip("=") aug_op = aug_opnode.op if _same_type(left_type, right_type): methods = [_aug_op(left, aug_opnode, aug_op, right, context), _bin_op(left, aug_opnode, bin_op, right, context)] elif helpers.is_subtype(left_type, right_type): methods = [_aug_op(left, aug_opnode, aug_op, right, context), _bin_op(left, aug_opnode, bin_op, right, context)] elif helpers.is_supertype(left_type, right_type): methods = [_aug_op(left, aug_opnode, aug_op, right, context), _bin_op(right, aug_opnode, bin_op, left, reverse_context, reverse=True), _bin_op(left, aug_opnode, bin_op, right, context)] else: methods = [_aug_op(left, aug_opnode, aug_op, right, context), _bin_op(left, aug_opnode, bin_op, right, context), _bin_op(right, aug_opnode, bin_op, left, reverse_context, reverse=True)] return methods
def _get_binop_flow(left, left_type, binary_opnode, right, right_type, context, reverse_context): """Get the flow for binary operations. The rules are a bit messy: * if left and right have the same type, then only one method will be called, left.__op__(right) * if left and right are unrelated typewise, then first left.__op__(right) is tried and if this does not exist or returns NotImplemented, then right.__rop__(left) is tried. * if left is a subtype of right, then only left.__op__(right) is tried. * if left is a supertype of right, then right.__rop__(left) is first tried and then left.__op__(right) """ op = binary_opnode.op if _same_type(left_type, right_type): methods = [_bin_op(left, binary_opnode, op, right, context)] elif helpers.is_subtype(left_type, right_type): methods = [_bin_op(left, binary_opnode, op, right, context)] elif helpers.is_supertype(left_type, right_type): methods = [_bin_op(right, binary_opnode, op, left, reverse_context, reverse=True), _bin_op(left, binary_opnode, op, right, context)] else: methods = [_bin_op(left, binary_opnode, op, right, context), _bin_op(right, binary_opnode, op, left, reverse_context, reverse=True)] return methods
def test_is_subtype_supertype_classes_metaclasses(self) -> None: cls_a = builder.extract_node(""" class A(type): #@ pass """) builtin_type = self._extract("type") self.assertTrue(helpers.is_supertype(builtin_type, cls_a)) self.assertTrue(helpers.is_subtype(cls_a, builtin_type))
def test_is_subtype_supertype_classes_metaclasses(self): cls_a = builder.extract_node(''' class A(type): #@ pass ''') builtin_type = self._extract('type') self.assertTrue(helpers.is_supertype(builtin_type, cls_a)) self.assertTrue(helpers.is_subtype(cls_a, builtin_type))
def test_is_subtype_supertype_unknown_bases(self): cls_a, cls_b = test_utils.extract_node(''' from unknown import Unknown class A(Unknown): pass #@ class B(A): pass #@ ''') self.assertTrue(helpers.is_subtype(cls_b, cls_a)) self.assertTrue(helpers.is_supertype(cls_a, cls_b))
def test_is_subtype_supertype_classes_no_type_ancestor(self): cls_a = builder.extract_node(''' class A(object): #@ pass ''') builtin_type = self._extract('type') self.assertFalse(helpers.is_supertype(builtin_type, cls_a)) self.assertFalse(helpers.is_subtype(cls_a, builtin_type))
def test_is_subtype_supertype_classes_no_type_ancestor(self) -> None: cls_a = builder.extract_node(""" class A(object): #@ pass """) builtin_type = self._extract("type") self.assertFalse(helpers.is_supertype(builtin_type, cls_a)) self.assertFalse(helpers.is_subtype(cls_a, builtin_type))
def test_is_subtype(self): ast_nodes = builder.extract_node(''' class int_subclass(int): pass class A(object): pass #@ class B(A): pass #@ class C(A): pass #@ int_subclass() #@ ''') cls_a = ast_nodes[0] cls_b = ast_nodes[1] cls_c = ast_nodes[2] int_subclass = ast_nodes[3] int_subclass = helpers.object_type(next(int_subclass.infer())) base_int = self._extract('int') self.assertTrue(helpers.is_subtype(int_subclass, base_int)) self.assertTrue(helpers.is_supertype(base_int, int_subclass)) self.assertTrue(helpers.is_supertype(cls_a, cls_b)) self.assertTrue(helpers.is_supertype(cls_a, cls_c)) self.assertTrue(helpers.is_subtype(cls_b, cls_a)) self.assertTrue(helpers.is_subtype(cls_c, cls_a)) self.assertFalse(helpers.is_subtype(cls_a, cls_b)) self.assertFalse(helpers.is_subtype(cls_a, cls_b))
def test_is_subtype_supertype_mro_error(self): cls_e, cls_f = builder.extract_node(''' class A(object): pass class B(A): pass class C(A): pass class D(B, C): pass class E(C, B): pass #@ class F(D, E): pass #@ ''') self.assertFalse(helpers.is_subtype(cls_e, cls_f)) self.assertFalse(helpers.is_subtype(cls_e, cls_f)) with self.assertRaises(exceptions._NonDeducibleTypeHierarchy): helpers.is_subtype(cls_f, cls_e) self.assertFalse(helpers.is_supertype(cls_f, cls_e))
def test_is_subtype_supertype_mro_error(self) -> None: cls_e, cls_f = builder.extract_node(""" class A(object): pass class B(A): pass class C(A): pass class D(B, C): pass class E(C, B): pass #@ class F(D, E): pass #@ """) self.assertFalse(helpers.is_subtype(cls_e, cls_f)) self.assertFalse(helpers.is_subtype(cls_e, cls_f)) with self.assertRaises(_NonDeducibleTypeHierarchy): helpers.is_subtype(cls_f, cls_e) self.assertFalse(helpers.is_supertype(cls_f, cls_e))