summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaudiu Popa <pcmanticore@gmail.com>2019-11-17 10:46:45 +0100
committerClaudiu Popa <pcmanticore@gmail.com>2019-11-17 10:46:45 +0100
commitb55fc8d9731e3527ab9c4c34488a6471d0aca62f (patch)
tree6b721d04f9e819e5bfd9abca6e838e2105c8b80e
parent060fc2fd30dbcf8b59b8fa7d37fd9a9311af0572 (diff)
downloadastroid-git-b55fc8d9731e3527ab9c4c34488a6471d0aca62f.tar.gz
Add support for inferring exception instances in all contexts
We were able to infer exception instances as ``ExceptionInstance`` only for a handful of cases, but not all. ``ExceptionInstance`` has support for better inference of `.args` and other exception related attributes that normal instances do not have. This additional support should remove certain false positives related to ``.args`` and other exception attributes in ``pylint``. Close PyCQA/pylint#2333
-rw-r--r--ChangeLog11
-rw-r--r--astroid/arguments.py2
-rw-r--r--astroid/protocols.py4
-rw-r--r--astroid/scoped_nodes.py12
-rw-r--r--tests/unittest_inference.py24
5 files changed, 38 insertions, 15 deletions
diff --git a/ChangeLog b/ChangeLog
index c0c3725c..86abec8b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -12,6 +12,17 @@ Release Date: TBA
* Allow inferring positional only arguments.
+* Add support for inferring exception instances in all contexts
+
+ We were able to infer exception instances as ``ExceptionInstance``
+ only for a handful of cases, but not all. ``ExceptionInstance`` has
+ support for better inference of `.args` and other exception related
+ attributes that normal instances do not have.
+ This additional support should remove certain false positives related
+ to ``.args`` and other exception attributes in ``pylint``.
+
+ Close PyCQA/pylint#2333
+
* Add more supported parameters to ``subprocess.check_output``
Close #722
diff --git a/astroid/arguments.py b/astroid/arguments.py
index c4bdc6d9..0a853605 100644
--- a/astroid/arguments.py
+++ b/astroid/arguments.py
@@ -209,7 +209,7 @@ class CallSite:
if funcnode.type == "method":
if not isinstance(boundnode, bases.Instance):
- boundnode = bases.Instance(boundnode)
+ boundnode = boundnode.instantiate_class()
return iter((boundnode,))
if funcnode.type == "classmethod":
return iter((boundnode,))
diff --git a/astroid/protocols.py b/astroid/protocols.py
index 84e3571b..891af0f6 100644
--- a/astroid/protocols.py
+++ b/astroid/protocols.py
@@ -328,7 +328,7 @@ def _arguments_infer_argname(self, name, context):
yield cls
return
if functype == "method":
- yield bases.Instance(cls)
+ yield cls.instantiate_class()
return
if context and context.callcontext:
@@ -341,7 +341,7 @@ def _arguments_infer_argname(self, name, context):
vararg.parent = self
if not self.arguments and self.parent.name == "__init__":
cls = self.parent.parent.scope()
- vararg.elts = [bases.Instance(cls)]
+ vararg.elts = [cls.instantiate_class()]
yield vararg
return
if name == self.kwarg:
diff --git a/astroid/scoped_nodes.py b/astroid/scoped_nodes.py
index 043e479d..e2f79481 100644
--- a/astroid/scoped_nodes.py
+++ b/astroid/scoped_nodes.py
@@ -2133,11 +2133,7 @@ class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG, node_classes.Statement
context = contextmod.bind_context_to_node(context, self)
yield from dunder_call.infer_call_result(caller, context)
else:
- if any(cls.name in EXCEPTION_BASE_CLASSES for cls in self.mro()):
- # Subclasses of exceptions can be exception instances
- yield objects.ExceptionInstance(self)
- else:
- yield bases.Instance(self)
+ yield self.instantiate_class()
def scope_lookup(self, node, name, offset=0):
"""Lookup where the given name is assigned.
@@ -2347,6 +2343,12 @@ class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG, node_classes.Statement
or self if this is not possible.
:rtype: Instance or ClassDef
"""
+ try:
+ if any(cls.name in EXCEPTION_BASE_CLASSES for cls in self.mro()):
+ # Subclasses of exceptions can be exception instances
+ return objects.ExceptionInstance(self)
+ except exceptions.MroError:
+ pass
return bases.Instance(self)
def getattr(self, name, context=None, class_context=True):
diff --git a/tests/unittest_inference.py b/tests/unittest_inference.py
index 9ce0c8d2..e8f99807 100644
--- a/tests/unittest_inference.py
+++ b/tests/unittest_inference.py
@@ -5236,14 +5236,24 @@ def test_infer_context_manager_with_unknown_args():
assert isinstance(next(node.infer()), nodes.Const)
-def test_subclass_of_exception():
- code = """
- class Error(Exception):
- pass
+@pytest.mark.parametrize(
+ "code",
+ [
+ """
+ class Error(Exception):
+ pass
- a = Error()
- a
- """
+ a = Error()
+ a #@
+ """,
+ """
+ class Error(Exception):
+ def method(self):
+ self #@
+ """,
+ ],
+)
+def test_subclass_of_exception(code):
inferred = next(extract_node(code).infer())
assert isinstance(inferred, Instance)
args = next(inferred.igetattr("args"))