-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Extracted overlapping exceptions checker into extension
- Loading branch information
1 parent
2fa1885
commit ea4273c
Showing
10 changed files
with
166 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html | ||
# For details: https://github.com/PyCQA/pylint/blob/master/COPYING | ||
|
||
"""Looks for overlapping exceptions.""" | ||
|
||
import astroid | ||
|
||
from pylint import interfaces | ||
from pylint import checkers | ||
from pylint.checkers import utils | ||
|
||
from pylint.checkers.exceptions import _annotated_unpack_infer | ||
|
||
|
||
class OverlappingExceptionsChecker(checkers.BaseChecker): | ||
"""Checks for two or more exceptions in the same exception handler | ||
clause that are identical or parts of the same inheritence hierarchy | ||
(i.e. overlapping).""" | ||
|
||
__implements__ = interfaces.IAstroidChecker | ||
|
||
name = 'overlap-except' | ||
msgs = {'W0714': ('Overlapping exceptions (%s)', | ||
'overlapping-except', | ||
'Used when exceptions in handler overlap or are identical')} | ||
priority = -2 | ||
options = () | ||
|
||
@utils.check_messages('overlapping-except') | ||
def visit_tryexcept(self, node): | ||
"""check for empty except""" | ||
for _, handler in enumerate(node.handlers): | ||
if handler.type is None: | ||
continue | ||
if isinstance(handler.type, astroid.BoolOp): | ||
continue | ||
try: | ||
excs = list(_annotated_unpack_infer(handler.type)) | ||
except astroid.InferenceError: | ||
continue | ||
|
||
handled_in_clause = [] | ||
for part, exc in excs: | ||
if exc is astroid.YES: | ||
continue | ||
if (isinstance(exc, astroid.Instance) and | ||
utils.inherit_from_std_ex(exc)): | ||
# pylint: disable=protected-access | ||
exc = exc._proxied | ||
|
||
if not isinstance(exc, astroid.ClassDef): | ||
continue | ||
|
||
exc_ancestors = [anc for anc in exc.ancestors() | ||
if isinstance(anc, astroid.ClassDef)] | ||
|
||
for prev_part, prev_exc in handled_in_clause: | ||
prev_exc_ancestors = [anc for anc in prev_exc.ancestors() | ||
if isinstance(anc, astroid.ClassDef)] | ||
if exc == prev_exc: | ||
self.add_message('overlapping-except', | ||
node=handler.type, | ||
args='%s and %s are the same' % | ||
(prev_part.as_string(), | ||
part.as_string())) | ||
elif (prev_exc in exc_ancestors or | ||
exc in prev_exc_ancestors): | ||
ancestor = part if exc in prev_exc_ancestors else prev_part | ||
descendant = part if prev_exc in exc_ancestors else prev_part | ||
self.add_message('overlapping-except', | ||
node=handler.type, | ||
args='%s is an ancestor class of %s' % | ||
(ancestor.as_string(), descendant.as_string())) | ||
handled_in_clause += [(part, exc)] | ||
|
||
|
||
def register(linter): | ||
"""Required method to auto register this checker.""" | ||
linter.register_checker(OverlappingExceptionsChecker(linter)) |
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html | ||
# For details: https://github.com/PyCQA/pylint/blob/master/COPYING | ||
|
||
"""Tests for the pylint checker in :mod:`pylint.extensions.overlapping_exceptions | ||
""" | ||
|
||
from sys import version_info | ||
import os | ||
from os.path import join, dirname | ||
import unittest | ||
|
||
from pylint import checkers | ||
from pylint.lint import PyLinter | ||
from pylint.reporters import BaseReporter | ||
from pylint.extensions.overlapping_exceptions import OverlappingExceptionsChecker | ||
|
||
class TestReporter(BaseReporter): | ||
|
||
def handle_message(self, msg): | ||
self.messages.append(msg) | ||
|
||
def on_set_current_module(self, module, filepath): | ||
self.messages = [] | ||
|
||
|
||
class CheckOverlappingExceptions(unittest.TestCase): | ||
|
||
@classmethod | ||
def setUpClass(cls): | ||
cls._linter = PyLinter() | ||
cls._linter.set_reporter(TestReporter()) | ||
checkers.initialize(cls._linter) | ||
cls._linter.register_checker(OverlappingExceptionsChecker(cls._linter)) | ||
cls._linter.disable('I') | ||
|
||
def test_overlapping_exceptions(self): | ||
test = join(dirname(__file__), 'data', 'overlapping_exceptions.py') | ||
self._linter.check([test]) | ||
msgs = self._linter.reporter.messages | ||
|
||
expected = [ | ||
(13, 'Overlapping exceptions (SomeException and SomeException are the same)'), | ||
(18, 'Overlapping exceptions (SomeException is an ancestor class of SubclassException)'), | ||
(23, 'Overlapping exceptions (SomeException and AliasException are the same)'), | ||
(28, 'Overlapping exceptions (AliasException is an ancestor class of SubclassException)'), | ||
(34, 'Overlapping exceptions (SomeException and AliasException are the same)'), | ||
(34, 'Overlapping exceptions (SomeException is an ancestor class of SubclassException)'), | ||
(34, 'Overlapping exceptions (AliasException is an ancestor class of SubclassException)'), | ||
(39, 'Overlapping exceptions (ArithmeticError is an ancestor class of FloatingPointError)'), | ||
(44, 'Overlapping exceptions (ValueError is an ancestor class of UnicodeDecodeError)') | ||
] | ||
|
||
self.assertEqual(len(msgs), len(expected)) | ||
for msg, exp in zip(msgs, expected): | ||
self.assertEqual(msg.msg_id, 'W0714') | ||
self.assertEqual(msg.symbol, 'overlapping-except') | ||
self.assertEqual(msg.category, 'warning') | ||
self.assertEqual((msg.line, msg.msg), exp) | ||
|
||
@unittest.skipIf(version_info < (3, 3), "not relevant to Python version") | ||
def test_overlapping_exceptions_py33(self): | ||
"""From Python 3.3 both IOError and socket.error are aliases for OSError.""" | ||
test = join(dirname(__file__), 'data', 'overlapping_exceptions_py33.py') | ||
self._linter.check([test]) | ||
msgs = self._linter.reporter.messages | ||
|
||
expected = [ | ||
(7, 'Overlapping exceptions (IOError and OSError are the same)'), | ||
(12, 'Overlapping exceptions (socket.error and OSError are the same)'), | ||
(17, 'Overlapping exceptions (socket.error is an ancestor class of ConnectionError)'), | ||
] | ||
|
||
self.assertEqual(len(msgs), len(expected)) | ||
for msg, exp in zip(msgs, expected): | ||
self.assertEqual(msg.msg_id, 'W0714') | ||
self.assertEqual(msg.symbol, 'overlapping-except') | ||
self.assertEqual(msg.category, 'warning') | ||
self.assertEqual((msg.line, msg.msg), exp) | ||
|
||
|
||
if __name__ == '__main__': | ||
unittest.main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.