diff options
author | Claudiu Popa <pcmanticore@gmail.com> | 2015-03-06 18:22:49 +0200 |
---|---|---|
committer | Claudiu Popa <pcmanticore@gmail.com> | 2015-03-06 18:22:49 +0200 |
commit | 1cd7a64ba8921b3650f91e76eeaaf8528b69ff70 (patch) | |
tree | 02cbb235af9dedf76e0e60772dbaac70c690fbc3 | |
parent | 67a7a7cb2ec9c2682f63864fab15bb45d0a167d1 (diff) | |
parent | a79bec387412da77ab024b35ef1d8579e92d4fff (diff) | |
download | pylint-1cd7a64ba8921b3650f91e76eeaaf8528b69ff70.tar.gz |
Merged in mibalint/pylint (pull request #234)
#422 [pylint sprint] Create pylintrc and disable checking for all issues
-rw-r--r-- | ChangeLog | 22 | ||||
-rw-r--r-- | doc/Makefile | 3 | ||||
-rw-r--r-- | doc/index.rst | 2 | ||||
-rw-r--r-- | pylint/checkers/classes.py | 86 | ||||
-rw-r--r-- | pylint/checkers/python3.py | 45 | ||||
-rw-r--r-- | pylint/lint.py | 18 | ||||
-rw-r--r-- | pylint/test/regrtest_data/py3k_error_flag.py | 11 | ||||
-rw-r--r-- | pylint/test/test_self.py | 50 | ||||
-rw-r--r-- | pylint/test/unittest_checker_python3.py | 13 | ||||
-rw-r--r-- | pylint/testutils.py | 2 | ||||
-rw-r--r-- | pylint/utils.py | 5 |
11 files changed, 200 insertions, 57 deletions
@@ -1,7 +1,7 @@ ChangeLog for Pylint -------------------- -NOT RELEASED YET -- VERSION +RELEASE DATE -- VERSION * Don't require a docstring for empty modules. Closes issue #261. * Fix a false positive with `too-few-format-args` string warning, @@ -44,6 +44,14 @@ NOT RELEASED YET -- VERSION * Add support for combining the Python 3 checker mode with the --jobs flag (--py3k and --jobs). Closes issue #467. + * Add a new warning for the Python 3 porting checker, 'using-cmp-argument', + emitted when the `cmp` argument for the `list.sort` or `sorted builtin` + is encountered. + + * Make the --py3k flag commutative with the -E flag. Also, this patch + fixes the leaks of error messages from the Python 3 checker when + the errors mode was activated. Closes issue #437. + 2015-01-16 -- 1.4.1 @@ -315,9 +323,9 @@ NOT RELEASED YET -- VERSION the 'a' format on Python 3. * Add multiple checks for PEP 3101 advanced string formatting: - 'bad-format-string', 'missing-format-argument-key', - 'unused-format-string-argument', 'format-combined-specification', - 'missing-format-attribute' and 'invalid-format-index'. + 'bad-format-string', 'missing-format-argument-key', + 'unused-format-string-argument', 'format-combined-specification', + 'missing-format-attribute' and 'invalid-format-index'. * Issue broad-except and bare-except even if the number of except handlers is different than 1. Fixes issue #113. @@ -477,7 +485,7 @@ NOT RELEASED YET -- VERSION Golemon * Add new warnings for checking proper class __slots__: - `invalid-slots-object` and `invalid-slots`. + `invalid-slots-object` and `invalid-slots`. * Search for rc file in `~/.config/pylintrc` if `~/.pylintrc` doesn't exists (#121) @@ -799,10 +807,10 @@ NOT RELEASED YET -- VERSION (patch by tmarek@google.com) * #7394: W0212 (access to protected member) not emited on assigments - (patch by lothiraldan@gmail.com) + (patch by lothiraldan@gmail.com) * #18772; no prototype consistency check for mangled methods (patch by - lothiraldan@gmail.com) + lothiraldan@gmail.com) * #92911: emit W0102 when sets are used as default arguments in functions (patch by tmarek@google.com) diff --git a/doc/Makefile b/doc/Makefile index d483f63..2cd7f7e 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -6,6 +6,7 @@ SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build +PYTHONPATH = # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 @@ -138,7 +139,7 @@ features: echo "" >> features.rst echo ".. generated by pylint --full-documentation" >> features.rst echo "" >> features.rst - pylint --full-documentation >> features.rst + PYTHONPATH=$(PYTHONPATH) pylint --full-documentation >> features.rst gen-examples: chmod u+w ../examples/pylintrc diff --git a/doc/index.rst b/doc/index.rst index 01f44c8..d67a28d 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -12,7 +12,7 @@ https://bitbucket.org/logilab/pylint run output message-control - features + features1 options extend ide-integration diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index 8e51a47..87e3bcf 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -107,9 +107,9 @@ def _is_attribute_property(name, klass): MSGS = { 'F0202': ('Unable to check methods signature (%s / %s)', 'method-check-failed', - 'Used when Pylint has been unable to check methods signature \ - compatibility for an unexpected reason. Please report this kind \ - if you don\'t make sense of it.'), + 'Used when Pylint has been unable to check methods signature ' + 'compatibility for an unexpected reason. Please report this kind ' + 'if you don\'t make sense of it.'), 'E0202': ('An attribute defined in %s line %s hides this method', 'method-hidden', @@ -118,35 +118,35 @@ MSGS = { 'client code.'), 'E0203': ('Access to member %r before its definition line %s', 'access-member-before-definition', - 'Used when an instance member is accessed before it\'s actually\ - assigned.'), + 'Used when an instance member is accessed before it\'s actually ' + 'assigned.'), 'W0201': ('Attribute %r defined outside __init__', 'attribute-defined-outside-init', - 'Used when an instance attribute is defined outside the __init__\ - method.'), + 'Used when an instance attribute is defined outside the __init__ ' + 'method.'), 'W0212': ('Access to a protected member %s of a client class', # E0214 'protected-access', - 'Used when a protected member (i.e. class member with a name \ - beginning with an underscore) is access outside the class or a \ - descendant of the class where it\'s defined.'), + 'Used when a protected member (i.e. class member with a name ' + 'beginning with an underscore) is access outside the class or a ' + 'descendant of the class where it\'s defined.'), 'E0211': ('Method has no argument', 'no-method-argument', - 'Used when a method which should have the bound instance as \ - first argument has no argument defined.'), + 'Used when a method which should have the bound instance as ' + 'first argument has no argument defined.'), 'E0213': ('Method should have "self" as first argument', 'no-self-argument', - 'Used when a method has an attribute different the "self" as\ - first argument. This is considered as an error since this is\ - a so common convention that you shouldn\'t break it!'), - 'C0202': ('Class method %s should have %s as first argument', # E0212 + 'Used when a method has an attribute different the "self" as ' + 'first argument. This is considered as an error since this is ' + 'a so common convention that you shouldn\'t break it!'), + 'C0202': ('Class method %s should have %s as first argument', 'bad-classmethod-argument', 'Used when a class method has a first argument named differently ' 'than the value specified in valid-classmethod-first-arg option ' '(default to "cls"), recommended to easily differentiate them ' 'from regular instance methods.'), - 'C0203': ('Metaclass method %s should have %s as first argument', # E0214 + 'C0203': ('Metaclass method %s should have %s as first argument', 'bad-mcs-method-argument', 'Used when a metaclass method has a first agument named ' 'differently than the value specified in valid-classmethod-first' @@ -167,58 +167,58 @@ MSGS = { ), 'R0201': ('Method could be a function', 'no-self-use', - 'Used when a method doesn\'t use its bound instance, and so could\ - be written as a function.' + 'Used when a method doesn\'t use its bound instance, and so could ' + 'be written as a function.' ), 'E0221': ('Interface resolved to %s is not a class', 'interface-is-not-class', - 'Used when a class claims to implement an interface which is not \ - a class.'), + 'Used when a class claims to implement an interface which is not ' + 'a class.'), 'E0222': ('Missing method %r from %s interface', 'missing-interface-method', - 'Used when a method declared in an interface is missing from a \ - class implementing this interface'), + 'Used when a method declared in an interface is missing from a ' + 'class implementing this interface'), 'W0221': ('Arguments number differs from %s %r method', 'arguments-differ', - 'Used when a method has a different number of arguments than in \ - the implemented interface or in an overridden method.'), + 'Used when a method has a different number of arguments than in ' + 'the implemented interface or in an overridden method.'), 'W0222': ('Signature differs from %s %r method', 'signature-differs', - 'Used when a method signature is different than in the \ - implemented interface or in an overridden method.'), + 'Used when a method signature is different than in the ' + 'implemented interface or in an overridden method.'), 'W0223': ('Method %r is abstract in class %r but is not overridden', 'abstract-method', - 'Used when an abstract method (i.e. raise NotImplementedError) is \ - not overridden in concrete class.' + 'Used when an abstract method (i.e. raise NotImplementedError) is ' + 'not overridden in concrete class.' ), - 'F0220': ('failed to resolve interfaces implemented by %s (%s)', # W0224 + 'F0220': ('failed to resolve interfaces implemented by %s (%s)', 'unresolved-interface', - 'Used when a Pylint as failed to find interfaces implemented by \ - a class'), + 'Used when a Pylint as failed to find interfaces implemented by ' + ' a class'), 'W0231': ('__init__ method from base class %r is not called', 'super-init-not-called', - 'Used when an ancestor class method has an __init__ method \ - which is not called by a derived class.'), + 'Used when an ancestor class method has an __init__ method ' + 'which is not called by a derived class.'), 'W0232': ('Class has no __init__ method', 'no-init', - 'Used when a class has no __init__ method, neither its parent \ - classes.'), + 'Used when a class has no __init__ method, neither its parent ' + 'classes.'), 'W0233': ('__init__ method from a non direct base class %r is called', 'non-parent-init-called', - 'Used when an __init__ method is called on a class which is not \ - in the direct ancestors for the analysed class.'), + 'Used when an __init__ method is called on a class which is not ' + 'in the direct ancestors for the analysed class.'), 'W0234': ('__iter__ returns non-iterator', 'non-iterator-returned', - 'Used when an __iter__ method returns something which is not an \ - iterable (i.e. has no `%s` method)' % NEXT_METHOD), + 'Used when an __iter__ method returns something which is not an ' + 'iterable (i.e. has no `%s` method)' % NEXT_METHOD), 'E0235': ('__exit__ must accept 3 arguments: type, value, traceback', 'bad-context-manager', - 'Used when the __exit__ special method, belonging to a \ - context manager, does not accept 3 arguments \ - (type, value, traceback).'), + 'Used when the __exit__ special method, belonging to a ' + 'context manager, does not accept 3 arguments ' + '(type, value, traceback).'), 'E0236': ('Invalid object %r in __slots__, must contain ' 'only non empty strings', 'invalid-slots-object', diff --git a/pylint/checkers/python3.py b/pylint/checkers/python3.py index 3ddf495..837cbef 100644 --- a/pylint/checkers/python3.py +++ b/pylint/checkers/python3.py @@ -18,6 +18,7 @@ import re import tokenize import astroid +from astroid import bases from pylint import checkers, interfaces from pylint.utils import WarningScope from pylint.checkers import utils @@ -217,13 +218,13 @@ class Python3Checker(checkers.BaseChecker): 'W1618': ('import missing `from __future__ import absolute_import`', 'no-absolute-import', 'Used when an import is not accompanied by ' - '`from __future__ import absolute_import`' - ' (default behaviour in Python 3)', + '``from __future__ import absolute_import`` ' + '(default behaviour in Python 3)', {'maxversion': (3, 0)}), 'W1619': ('division w/o __future__ statement', 'old-division', 'Used for non-floor division w/o a float literal or ' - '``from __future__ import division``' + '``from __future__ import division`` ' '(Python 3 returns a float for int division unconditionally)', {'maxversion': (3, 0)}), 'W1620': ('Calling a dict.iter*() method', @@ -326,6 +327,13 @@ class Python3Checker(checkers.BaseChecker): 'Used when the filter built-in is referenced in a non-iterating ' 'context (returns an iterator in Python 3)', {'maxversion': (3, 0)}), + 'W1640': ('Using the cmp argument for list.sort / sorted', + 'using-cmp-argument', + 'Using the cmp argument for list.sort or the sorted ' + 'builtin should be avoided, since it was removed in ' + 'Python 3. Using either `key` or `functools.cmp_to_key` ' + 'should be preferred.', + {'maxversion': (3, 0)}), } _bad_builtins = frozenset([ @@ -425,7 +433,38 @@ class Python3Checker(checkers.BaseChecker): else: self.add_message('old-division', node=node) + def _check_cmp_argument(self, node): + # Check that the `cmp` argument is used + args = [] + if (isinstance(node.func, astroid.Getattr) + and node.func.attrname == 'sort'): + inferred = utils.safe_infer(node.func.expr) + if not inferred: + return + + builtins_list = "{}.list".format(bases.BUILTINS) + if (isinstance(inferred, astroid.List) + or inferred.qname() == builtins_list): + args = node.args + + elif (isinstance(node.func, astroid.Name) + and node.func.name == 'sorted'): + inferred = utils.safe_infer(node.func) + if not inferred: + return + + builtins_sorted = "{}.sorted".format(bases.BUILTINS) + if inferred.qname() == builtins_sorted: + args = node.args + + for arg in args: + if isinstance(arg, astroid.Keyword) and arg.arg == 'cmp': + self.add_message('using-cmp-argument', node=node) + return + def visit_callfunc(self, node): + self._check_cmp_argument(node) + if isinstance(node.func, astroid.Getattr): if any([node.args, node.starargs, node.kwargs]): return diff --git a/pylint/lint.py b/pylint/lint.py index f4c24c6..0c466a1 100644 --- a/pylint/lint.py +++ b/pylint/lint.py @@ -456,6 +456,7 @@ class PyLinter(configuration.OptionsManagerMixIn, self.register_checker(self) self._dynamic_plugins = set() self._python3_porting_mode = False + self._error_mode = False self.load_provider_defaults() if reporter: self.set_reporter(reporter) @@ -584,8 +585,16 @@ class PyLinter(configuration.OptionsManagerMixIn, def error_mode(self): """error mode: enable only errors; no reports, no persistent""" + self._error_mode = True self.disable_noerror_messages() self.disable('miscellaneous') + if self._python3_porting_mode: + self.disable('all') + for msg_id in self._checker_messages('python3'): + if msg_id.startswith('E'): + self.enable(msg_id) + else: + self.disable('python3') self.set_option('reports', False) self.set_option('persistent', False) @@ -593,6 +602,15 @@ class PyLinter(configuration.OptionsManagerMixIn, """Disable all other checkers and enable Python 3 warnings.""" self.disable('all') self.enable('python3') + if self._error_mode: + # The error mode was activated, using the -E flag. + # So we'll need to enable only the errors from the + # Python 3 porting checker. + for msg_id in self._checker_messages('python3'): + if msg_id.startswith('E'): + self.enable(msg_id) + else: + self.disable(msg_id) self._python3_porting_mode = True # block level option handling ############################################# diff --git a/pylint/test/regrtest_data/py3k_error_flag.py b/pylint/test/regrtest_data/py3k_error_flag.py new file mode 100644 index 0000000..c12d250 --- /dev/null +++ b/pylint/test/regrtest_data/py3k_error_flag.py @@ -0,0 +1,11 @@ +"""Contains both normal error messages and Python3 porting error messages."""
+# pylint: disable=bad-builtin, too-few-public-methods
+
+raise Exception, 1 # Error emitted here with the Python 3 checker.
+
+
+class Test(object):
+ """dummy"""
+
+ def __init__(self):
+ return 42
diff --git a/pylint/test/test_self.py b/pylint/test/test_self.py index 1734889..9c296b8 100644 --- a/pylint/test/test_self.py +++ b/pylint/test/test_self.py @@ -11,10 +11,12 @@ # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +import contextlib import sys import os from os.path import join, dirname, abspath import tempfile +import textwrap import unittest import six @@ -28,6 +30,17 @@ from pylint.reporters.json import JSONReporter HERE = abspath(dirname(__file__)) + +@contextlib.contextmanager +def _patch_streams(out): + sys.stderr = sys.stdout = out + try: + yield + finally: + sys.stderr = sys.__stderr__ + sys.stdout = sys.__stdout__ + + class MultiReporter(BaseReporter): def __init__(self, reporters): self._reporters = reporters @@ -85,6 +98,15 @@ class RunTC(unittest.TestCase): sys.stderr = sys.__stderr__ sys.stdout = sys.__stdout__ + def _test_output(self, args, expected_output): + out = six.StringIO() + with _patch_streams(out): + with self.assertRaises(SystemExit): + Run(args) + + actual_output = out.getvalue() + self.assertEqual(expected_output.strip(), actual_output.strip()) + def test_pkginfo(self): """Make pylint check itself.""" self._runtest(['pylint.__pkginfo__'], reporter=TextReporter(six.StringIO()), @@ -151,7 +173,33 @@ class RunTC(unittest.TestCase): rc_code = 2 if six.PY2 else 0 self._runtest([join(HERE, 'functional', 'unpacked_exceptions.py'), '--py3k', '-j 2'], - code=rc_code) + code=rc_code) + + @unittest.skipIf(sys.version_info[0] > 2, "Requires the --py3k flag.") + def test_py3k_commutative_with_errors_only(self): + + # Test what gets emitted with -E only + module = join(HERE, 'regrtest_data', 'py3k_error_flag.py') + expected = textwrap.dedent(""" + No config file found, using default configuration + ************* Module py3k_error_flag + Explicit return in __init__ + """) + self._test_output([module, "-E", "--msg-template='{msg}'"], + expected_output=expected) + + # Test what gets emitted with -E --py3k + expected = textwrap.dedent(""" + No config file found, using default configuration + ************* Module py3k_error_flag + Use raise ErrorClass(args) instead of raise ErrorClass, args. + """) + self._test_output([module, "-E", "--py3k", "--msg-template='{msg}'"], + expected_output=expected) + + # Test what gets emitted with --py3k -E + self._test_output([module, "--py3k", "-E", "--msg-template='{msg}'"], + expected_output=expected) if __name__ == '__main__': diff --git a/pylint/test/unittest_checker_python3.py b/pylint/test/unittest_checker_python3.py index 825e827..bac3f4a 100644 --- a/pylint/test/unittest_checker_python3.py +++ b/pylint/test/unittest_checker_python3.py @@ -384,6 +384,19 @@ class Python3CheckerTest(testutils.CheckerTestCase): with self.assertNoMessages(): self.walk(node) + def test_using_cmp_argument(self): + nodes = test_utils.extract_node(""" + [].sort(cmp=lambda x: x) #@ + a = list(range(x)) + a.sort(cmp=lambda x: x) #@ + + sorted([], cmp=lambda x: x) #@ + """) + for node in nodes: + message = testutils.Message('using-cmp-argument', node=node) + with self.assertAddsMessages(message): + self.checker.visit_callfunc(node) + @python2_only class Python3TokenCheckerTest(testutils.CheckerTestCase): diff --git a/pylint/testutils.py b/pylint/testutils.py index 2f9af4d..2ea440d 100644 --- a/pylint/testutils.py +++ b/pylint/testutils.py @@ -93,7 +93,7 @@ def get_tests_info(input_dir, msg_dir, prefix, suffix): class TestReporter(BaseReporter): """reporter storing plain text messages""" - __implements____ = IReporter + __implements__ = IReporter def __init__(self): # pylint: disable=super-init-not-called diff --git a/pylint/utils.py b/pylint/utils.py index 6685c4a..6c5b975 100644 --- a/pylint/utils.py +++ b/pylint/utils.py @@ -225,6 +225,11 @@ class MessagesHandlerMixIn(object): self._msgs_state = {} self.msg_status = 0 + def _checker_messages(self, checker): + for checker in self._checkers[checker.lower()]: + for msgid in checker.msgs: + yield msgid + def disable(self, msgid, scope='package', line=None, ignore_unknown=False): """don't output message of the given id""" assert scope in ('package', 'module') |