summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaudiu Popa <pcmanticore@gmail.com>2015-03-06 18:22:49 +0200
committerClaudiu Popa <pcmanticore@gmail.com>2015-03-06 18:22:49 +0200
commit1cd7a64ba8921b3650f91e76eeaaf8528b69ff70 (patch)
tree02cbb235af9dedf76e0e60772dbaac70c690fbc3
parent67a7a7cb2ec9c2682f63864fab15bb45d0a167d1 (diff)
parenta79bec387412da77ab024b35ef1d8579e92d4fff (diff)
downloadpylint-1cd7a64ba8921b3650f91e76eeaaf8528b69ff70.tar.gz
Merged in mibalint/pylint (pull request #234)
#422 [pylint sprint] Create pylintrc and disable checking for all issues
-rw-r--r--ChangeLog22
-rw-r--r--doc/Makefile3
-rw-r--r--doc/index.rst2
-rw-r--r--pylint/checkers/classes.py86
-rw-r--r--pylint/checkers/python3.py45
-rw-r--r--pylint/lint.py18
-rw-r--r--pylint/test/regrtest_data/py3k_error_flag.py11
-rw-r--r--pylint/test/test_self.py50
-rw-r--r--pylint/test/unittest_checker_python3.py13
-rw-r--r--pylint/testutils.py2
-rw-r--r--pylint/utils.py5
11 files changed, 200 insertions, 57 deletions
diff --git a/ChangeLog b/ChangeLog
index 9977d16..30a2d28 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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')