diff options
-rw-r--r-- | astroid/exceptions.py | 8 | ||||
-rw-r--r-- | astroid/inference.py | 4 | ||||
-rw-r--r-- | astroid/node_classes.py | 64 | ||||
-rw-r--r-- | astroid/protocols.py | 8 |
4 files changed, 55 insertions, 29 deletions
diff --git a/astroid/exceptions.py b/astroid/exceptions.py index ea2b9b07..8b3f2522 100644 --- a/astroid/exceptions.py +++ b/astroid/exceptions.py @@ -196,6 +196,14 @@ class _NonDeducibleTypeHierarchy(Exception): """Raised when is_subtype / is_supertype can't deduce the relation between two types.""" +class AstroidIndexError(AstroidError): + """Raised when an Indexable / Mapping does not have an index / key.""" + + +class AstroidTypeError(AstroidError): + """Raised when a TypeError would be expected in Python code.""" + + # Backwards-compatibility aliases OperationError = util.BadOperationMessage UnaryOperationError = util.BadUnaryOperationMessage diff --git a/astroid/inference.py b/astroid/inference.py index 153e269d..9ce078a2 100644 --- a/astroid/inference.py +++ b/astroid/inference.py @@ -257,7 +257,9 @@ def infer_subscript(self, context=None): try: assigned = value.getitem(index_value, context) - except (IndexError, TypeError, AttributeError) as exc: + except (exceptions.AstroidTypeError, + exceptions.AstroidIndexError, + AttributeError) as exc: util.reraise(exceptions.InferenceError(node=self, error=exc, context=context)) diff --git a/astroid/node_classes.py b/astroid/node_classes.py index 5c44b158..9a8c5bb1 100644 --- a/astroid/node_classes.py +++ b/astroid/node_classes.py @@ -173,17 +173,27 @@ def _infer_slice(node, context=None): def _container_getitem(instance, elts, index, context=None): """Get a slice or an item, using the given *index*, for the given sequence.""" - - if isinstance(index, Slice): - index_slice = _infer_slice(index, context=context) - new_cls = instance.__class__() - new_cls.elts = elts[index_slice] - new_cls.parent = instance.parent - return new_cls - elif isinstance(index, Const): - return elts[index.value] - - raise TypeError('Could not use %s as subscript index' % index) + try: + if isinstance(index, Slice): + index_slice = _infer_slice(index, context=context) + new_cls = instance.__class__() + new_cls.elts = elts[index_slice] + new_cls.parent = instance.parent + return new_cls + elif isinstance(index, Const): + return elts[index.value] + except IndexError: + util.reraise(exceptions.AstroidIndexError( + message='Index {index!s} out of range', + node=instance, index=index, context=context)) + except TypeError as exc: + util.reraise(exceptions.AstroidIndexError( + message='Type error {error!r}', error=exc, + node=instance, index=index, context=context)) + + raise exceptions.AstroidTypeError( + 'Could not use %s as subscript index' % index + ) class NodeNG(object): @@ -1238,19 +1248,26 @@ class Const(NodeNG, bases.Instance): elif isinstance(index, Slice): index_value = _infer_slice(index, context=context) else: - raise TypeError( + raise exceptions.AstroidTypeError( 'Could not use type {} as subscript index'.format(type(index)) ) - if isinstance(self.value, six.string_types): - return Const(self.value[index_value]) - if isinstance(self.value, bytes) and six.PY3: - # Bytes aren't instances of six.string_types - # on Python 3. Also, indexing them should return - # integers. - return Const(self.value[index_value]) + try: + if isinstance(self.value, six.string_types): + return Const(self.value[index_value]) + if isinstance(self.value, bytes) and six.PY3: + # Bytes aren't instances of six.string_types + # on Python 3. Also, indexing them should return + # integers. + return Const(self.value[index_value]) + except TypeError: + # The object does not support this operation, let the + # following error be raised instead. + pass - raise TypeError('%r (value=%s)' % (self, self.value)) + raise exceptions.AstroidTypeError( + '%r (value=%s)' % (self, self.value) + ) def has_dynamic_getattr(self): return False @@ -1353,7 +1370,7 @@ class Dict(NodeNG, bases.Instance): if isinstance(key, DictUnpack): try: return value.getitem(index, context) - except IndexError: + except (exceptions.AstroidTypeError, exceptions.AstroidIndexError): continue for inferredkey in key.infer(context): if inferredkey is util.Uninferable: @@ -1361,9 +1378,8 @@ class Dict(NodeNG, bases.Instance): if isinstance(inferredkey, Const) and isinstance(index, Const): if inferredkey.value == index.value: return value - # This should raise KeyError, but all call sites only catch - # IndexError. Let's leave it like that for now. - raise IndexError(index) + + raise exceptions.AstroidIndexError(index) def bool_value(self): return bool(self.items) diff --git a/astroid/protocols.py b/astroid/protocols.py index 93ec7a61..73954fb6 100644 --- a/astroid/protocols.py +++ b/astroid/protocols.py @@ -219,9 +219,9 @@ def _resolve_looppart(parts, asspath, context): index_node = nodes.Const(index) try: assigned = stmt.getitem(index_node, context) - except (AttributeError, IndexError): - continue - except TypeError: # stmt is unsubscriptable Const + except (AttributeError, + exceptions.AstroidTypeError, + exceptions.AstroidIndexError): continue if not asspath: # we achieved to resolved the assignment path, @@ -370,7 +370,7 @@ def _resolve_asspart(parts, asspath, context): assigned = part.getitem(index_node, context) # XXX raise a specific exception to avoid potential hiding of # unexpected exception ? - except (TypeError, IndexError): + except (exceptions.AstroidTypeError, exceptions.AstroidIndexError): return if not asspath: # we achieved to resolved the assignment path, don't infer the |