summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaudiu Popa <pcmanticore@gmail.com>2016-06-03 15:44:31 +0100
committerClaudiu Popa <pcmanticore@gmail.com>2016-06-03 15:59:11 +0100
commit67ed3537f7e0bee9de22663b479c6be444e527ce (patch)
tree09cb749c3a3d3e0bf2eee6fe932d7e8f5f962ae6
parent7cd9c6b83d6866055ff756787966c8011a857769 (diff)
downloadastroid-git-67ed3537f7e0bee9de22663b479c6be444e527ce.tar.gz
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.
-rw-r--r--astroid/exceptions.py4
-rw-r--r--astroid/inference.py35
-rw-r--r--astroid/interpreter/util.py4
-rw-r--r--astroid/tests/unittest_helpers.py13
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)