From 67ed3537f7e0bee9de22663b479c6be444e527ce Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Fri, 3 Jun 2016 15:44:31 +0100 Subject: Now is_subtype / is_supertype raises an internal exception when a type hierarchy can't be determined It used to return Uninferable, but no call site was actually taking care of this potential return. It is better though to simply raise an exception and to let the call sites to handle them in which way they want to. --- astroid/exceptions.py | 4 ++++ astroid/inference.py | 35 ++++++++++++++++++++++++----------- astroid/interpreter/util.py | 4 ++-- astroid/tests/unittest_helpers.py | 13 ++++++++----- 4 files changed, 38 insertions(+), 18 deletions(-) diff --git a/astroid/exceptions.py b/astroid/exceptions.py index 3e571006..10e24e1b 100644 --- a/astroid/exceptions.py +++ b/astroid/exceptions.py @@ -197,6 +197,10 @@ class BinaryOperationNotSupportedError(NotSupportedError): """ +class _NonDeducibleTypeHierarchy(Exception): + """Raised when is_subtype / is_supertype can't deduce the relation between two types.""" + + # Backwards-compatibility aliases OperationError = util.BadOperationMessage UnaryOperationError = util.BadUnaryOperationMessage diff --git a/astroid/inference.py b/astroid/inference.py index c32c3be1..43483b1b 100644 --- a/astroid/inference.py +++ b/astroid/inference.py @@ -168,9 +168,14 @@ def infer_attribute(self, context=None): # by taking in consideration a redefinition in the subclass. if (isinstance(owner, runtimeabc.Instance) and isinstance(context.boundnode, runtimeabc.Instance)): - if inferenceutil.is_subtype(inferenceutil.object_type(context.boundnode), - inferenceutil.object_type(owner)): - owner = context.boundnode + try: + if inferenceutil.is_subtype(inferenceutil.object_type(context.boundnode), + inferenceutil.object_type(owner)): + owner = context.boundnode + except exceptions._NonDeducibleTypeHierarchy: + # Can't determine anything useful. + pass + try: context.boundnode = owner @@ -615,10 +620,14 @@ def infer_binop(self, context, nodes): yield util.Uninferable return - results = _infer_binary_operation(lhs, rhs, self, context, - _get_binop_flow, nodes) - for result in results: - yield result + try: + results = _infer_binary_operation(lhs, rhs, self, context, + _get_binop_flow, nodes) + except exceptions._NonDeducibleTypeHierarchy: + yield util.Uninferable + else: + for result in results: + yield result @decorators.yes_if_nothing_inferred @@ -652,10 +661,14 @@ def infer_augassign(self, context=None, nodes=None): yield util.Uninferable return - results = _infer_binary_operation(lhs, rhs, self, - context, _get_aug_flow, nodes) - for result in results: - yield result + try: + results = _infer_binary_operation(lhs, rhs, self, + context, _get_aug_flow, nodes) + except exceptions._NonDeducibleTypeHierarchy: + yield util.Uninferable + else: + for result in results: + yield result @decorators.path_wrapper diff --git a/astroid/interpreter/util.py b/astroid/interpreter/util.py index d5ba51de..148804ce 100644 --- a/astroid/interpreter/util.py +++ b/astroid/interpreter/util.py @@ -194,7 +194,7 @@ def has_known_bases(klass, context=None): def _type_check(type1, type2): if not all(map(has_known_bases, (type1, type2))): - return util.Uninferable + raise exceptions._NonDeducibleTypeHierarchy if not all([type1.newstyle, type2.newstyle]): return False @@ -202,7 +202,7 @@ def _type_check(type1, type2): return type1 in type2.mro()[:-1] except exceptions.MroError: # The MRO is invalid. - return util.Uninferable + raise exceptions._NonDeducibleTypeHierarchy def is_subtype(type1, type2): diff --git a/astroid/tests/unittest_helpers.py b/astroid/tests/unittest_helpers.py index efb25f0d..1826cfd7 100644 --- a/astroid/tests/unittest_helpers.py +++ b/astroid/tests/unittest_helpers.py @@ -9,6 +9,7 @@ import six from six.moves import builtins from astroid import builder +from astroid import exceptions from astroid.interpreter import util as interpreterutil from astroid import manager from astroid import nodes @@ -193,8 +194,9 @@ class TestHelpers(unittest.TestCase): class F(D, E): pass #@ ''') self.assertFalse(interpreterutil.is_subtype(cls_e, cls_f)) - self.assertEqual(interpreterutil.is_subtype(cls_f, cls_e), util.Uninferable) - self.assertEqual(interpreterutil.is_supertype(cls_e, cls_f), util.Uninferable) + self.assertFalse(interpreterutil.is_subtype(cls_e, cls_f)) + with self.assertRaises(exceptions._NonDeducibleTypeHierarchy): + interpreterutil.is_subtype(cls_f, cls_e) self.assertFalse(interpreterutil.is_supertype(cls_f, cls_e)) def test_is_subtype_supertype_unknown_bases(self): @@ -203,8 +205,10 @@ class TestHelpers(unittest.TestCase): class A(Unknown): pass #@ class B(A): pass #@ ''') - self.assertTrue(interpreterutil.is_subtype(cls_b, cls_a)) - self.assertTrue(interpreterutil.is_supertype(cls_a, cls_b)) + with self.assertRaises(exceptions._NonDeducibleTypeHierarchy): + interpreterutil.is_subtype(cls_a, cls_b) + with self.assertRaises(exceptions._NonDeducibleTypeHierarchy): + interpreterutil.is_supertype(cls_a, cls_b) def test_is_subtype_supertype_unrelated_classes(self): cls_a, cls_b = test_utils.extract_node(''' @@ -236,7 +240,6 @@ class TestHelpers(unittest.TestCase): @test_utils.require_version(maxver='3.0') def test_old_style_class(self): - # TODO: what is this test supposed to be testing? It will crash as-is because it calls helpers. cls = test_utils.extract_node('''class A: pass''') builtin_type = self._look_up_in_builtins('type') self.assertEqual(interpreterutil.object_type(cls), builtin_type) -- cgit v1.2.1