diff options
author | Claudiu Popa <pcmanticore@gmail.com> | 2019-11-17 10:46:45 +0100 |
---|---|---|
committer | Claudiu Popa <pcmanticore@gmail.com> | 2019-11-17 10:46:45 +0100 |
commit | b55fc8d9731e3527ab9c4c34488a6471d0aca62f (patch) | |
tree | 6b721d04f9e819e5bfd9abca6e838e2105c8b80e | |
parent | 060fc2fd30dbcf8b59b8fa7d37fd9a9311af0572 (diff) | |
download | astroid-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-- | ChangeLog | 11 | ||||
-rw-r--r-- | astroid/arguments.py | 2 | ||||
-rw-r--r-- | astroid/protocols.py | 4 | ||||
-rw-r--r-- | astroid/scoped_nodes.py | 12 | ||||
-rw-r--r-- | tests/unittest_inference.py | 24 |
5 files changed, 38 insertions, 15 deletions
@@ -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")) |