summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVlad Temian <vladtemian@gmail.com>2014-11-22 13:06:40 +0200
committerVlad Temian <vladtemian@gmail.com>2014-11-22 13:06:40 +0200
commite0bea04d6990cc8a03a27f77793d6bf0e807b13f (patch)
tree9d23a7b024a8e3a898e52bff925ac00015b71f64
parentaba95d1e150859c3e586d9ad5124e2bec6a936aa (diff)
parent0442fb6e49a9e1b0227cd1db78d339f1fe6788ae (diff)
downloadpylint-e0bea04d6990cc8a03a27f77793d6bf0e807b13f.tar.gz
Merged from remote
-rw-r--r--ChangeLog38
-rw-r--r--Makefile2
-rw-r--r--README105
-rw-r--r--checkers/__init__.py29
-rw-r--r--checkers/base.py70
-rw-r--r--checkers/classes.py4
-rw-r--r--checkers/exceptions.py48
-rw-r--r--checkers/format.py46
-rw-r--r--checkers/imports.py14
-rw-r--r--checkers/logging.py61
-rw-r--r--checkers/newstyle.py6
-rw-r--r--checkers/python3.py202
-rw-r--r--checkers/spelling.py4
-rw-r--r--checkers/stdlib.py6
-rw-r--r--checkers/strings.py23
-rw-r--r--checkers/utils.py8
-rw-r--r--checkers/variables.py67
-rw-r--r--config.py2
-rw-r--r--doc/contribute.rst6
-rw-r--r--doc/extend.rst7
-rw-r--r--doc/faq.rst27
-rw-r--r--doc/ide-integration.rst36
-rw-r--r--doc/options.rst10
-rw-r--r--doc/output.rst2
-rw-r--r--doc/run.rst35
-rwxr-xr-xepylint.py14
-rw-r--r--examples/pylintrc171
-rw-r--r--gui.py32
-rw-r--r--interfaces.py2
-rw-r--r--lint.py197
-rw-r--r--man/pylint.1138
-rw-r--r--pyreverse/diadefslib.py2
-rw-r--r--pyreverse/utils.py2
-rw-r--r--reporters/__init__.py2
-rw-r--r--reporters/guireporter.py1
-rw-r--r--reporters/html.py1
-rw-r--r--reporters/text.py2
-rw-r--r--test/functional/abstract_class_instantiated_py2.py68
-rw-r--r--test/functional/abstract_class_instantiated_py2.rc2
-rw-r--r--test/functional/abstract_class_instantiated_py2.txt4
-rw-r--r--test/functional/abstract_class_instantiated_py3.py (renamed from test/input/func_abstract_class_instantiated_py30.py)15
-rw-r--r--test/functional/abstract_class_instantiated_py3.rc2
-rw-r--r--test/functional/abstract_class_instantiated_py3.txt3
-rw-r--r--test/functional/abstract_class_instantiated_py34.py19
-rw-r--r--test/functional/abstract_class_instantiated_py34.rc2
-rw-r--r--test/functional/abstract_class_instantiated_py34.txt1
-rw-r--r--test/functional/defined_and_used_on_same_line.py2
-rw-r--r--test/functional/indexing_exception.py (renamed from test/input/func_indexing_exceptions_py_30.py)6
-rw-r--r--test/functional/indexing_exception.rc5
-rw-r--r--test/functional/indexing_exception.txt3
-rw-r--r--test/functional/invalid_exceptions_raised.py3
-rw-r--r--test/functional/invalid_exceptions_raised.txt8
-rw-r--r--test/functional/long_lines_with_utf8.txt2
-rw-r--r--test/functional/string_formatting.py19
-rw-r--r--test/functional/unpacked_exceptions.rc3
-rw-r--r--test/input/func_abstract_class_instantiated_py34.py55
-rw-r--r--test/input/func_backtick_deprecated_py_30.py4
-rw-r--r--test/input/func_bad_assigment_to_exception_var.py2
-rw-r--r--test/input/func_toolonglines.py8
-rw-r--r--test/input/func_w0331_py_30.py7
-rw-r--r--test/input/func_w0332_py_30.py2
-rw-r--r--test/input/func_w0701_py_30.py12
-rw-r--r--test/messages/func_abstract_class_instantiated_py30.txt2
-rw-r--r--test/messages/func_abstract_class_instantiated_py34.txt3
-rw-r--r--test/messages/func_backtick_deprecated_py_30.txt2
-rw-r--r--test/messages/func_bad_assigment_to_exception_var.txt8
-rw-r--r--test/messages/func_disable_linebased.txt4
-rw-r--r--test/messages/func_disable_linebased_py30.txt4
-rw-r--r--test/messages/func_indexing_exceptions_py_30.txt3
-rw-r--r--test/messages/func_toolonglines.txt10
-rw-r--r--test/messages/func_toolonglines_py30.txt8
-rw-r--r--test/messages/func_w0152_py29.txt1
-rw-r--r--test/messages/func_w0331_py_30.txt1
-rw-r--r--test/messages/func_w0701_py_30.txt3
-rw-r--r--test/test_functional.py8
-rw-r--r--test/test_import_graph.py3
-rw-r--r--test/test_self.py29
-rw-r--r--test/unittest_checker_format.py11
-rw-r--r--test/unittest_checker_misc.py31
-rw-r--r--test/unittest_checker_python3.py195
-rw-r--r--test/unittest_checker_similar.py27
-rw-r--r--test/unittest_checker_spelling.py12
-rw-r--r--test/unittest_lint.py17
-rw-r--r--test/unittest_pyreverse_diadefs.py4
-rw-r--r--test/unittest_reporting.py6
-rw-r--r--testutils.py50
-rw-r--r--utils.py82
87 files changed, 1281 insertions, 922 deletions
diff --git a/ChangeLog b/ChangeLog
index 0ef5c07..d2e9789 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,14 @@ ChangeLog for Pylint
====================
--
+ * Change default max-line-length to 100 rather than 80
+
+ * Drop BaseRawChecker class which were only there for backward
+ compat for a while now
+
+ * Don't try to analyze string formatting with objects coming from
+ function arguments. Closes issue #373.
+
* Port source code to be Python 2/3 compatible. This drops the
need for 2to3, but does drop support for Python 2.5.
@@ -123,10 +131,11 @@ ChangeLog for Pylint
* Check that various built-ins that do not exist in Python 3 are not
used: apply, basestring, buffer, cmp, coerce, execfile, file, long
- raw_input, reduce, StandardError, unicode, and xrange.
+ raw_input, reduce, StandardError, unicode, reload and xrange.
* Warn for magic methods which are not used in any way in Python 3:
- __coerce__, __delslice__, __getslice__, and __setslice__.
+ __coerce__, __delslice__, __getslice__, __setslice__, __cmp__,
+ __oct__, __nonzero__ and __hex__.
* Don't emit 'assigning-non-slot' when the assignment is for a property.
Closes issue #359.
@@ -160,13 +169,36 @@ ChangeLog for Pylint
* Warn when assigning to __metaclass__ at a class scope; in Python 3 a
metaclass is specified as an argument to the 'class' statement.
+ * Warn when performing parameter tuple unpacking; it is not supported in
+ Python 3.
+
+ * 'abstract-class-instantiated' is also emitted for Python 2.
+ It was previously disabled.
+
+ * Add 'long-suffix' error, emitted when encountering the long suffix
+ on numbers.
+
+ * Add support for disabling a checker, by specifying an 'enabled'
+ attribute on the checker class.
+
+ * Add a new CLI option, --py3k, for enabling Python 3 porting mode. This
+ mode will disable all other checkers and will emit warnings and
+ errors for constructs which are invalid or removed in Python 3.
+
+ * Add 'old-octal-literal' to Python 3 porting checker, emitted when
+ encountering octals with the old syntax.
+
+ * Add 'implicit-map-evaluation' to Python 3 porting checker, emitted
+ when encountering the use of map builtin, without explicit evaluation.
+
+
2014-07-26 -- 1.3.0
* Allow hanging continued indentation for implicitly concatenated
strings. Closes issue #232.
- * PyLint works under Python 2.5 again, and its test suite passes.
+ * Pylint works under Python 2.5 again, and its test suite passes.
* Fix some false positives for the cellvar-from-loop warnings.
Closes issue #233.
diff --git a/Makefile b/Makefile
index b566c2b..daf2bff 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-# Makefile for handling various tasks of PyLint sources
+# Makefile for handling various tasks of Pylint sources
PYVE=pyve
PIP=$(PYVE)/bin/pip
TOX=$(PYVE)/bin/tox
diff --git a/README b/README
index ec6bfde..9613aef 100644
--- a/README
+++ b/README
@@ -1,50 +1,55 @@
-README for Pylint - http://www.pylint.org/
-==========================================
-
-Pylint is a Python source code analyzer which looks for programming errors,
-helps enforcing a coding standard and sniffs for some code smells (as defined in
-Martin Fowler's Refactoring book).
-
-Pylint has many rules enabled by default, way too much to silence them all on a
-minimally sized program. It's highly configurable and handle pragmas to control
-it from within your code. Additionally, it is possible to write plugins to add
-your own checks.
-
-It's a free software distributed under the GNU Public Licence.
-
-Development is hosted on bitbucket: https://bitbucket.org/logilab/pylint/
-
-You can use the code-quality@python.org mailing list to discuss about
-Pylint. Subscribe at https://mail.python.org/mailman/listinfo/code-quality/
-or read the archives at https://mail.python.org/pipermail/code-quality/
-
-Install
--------
-
-Pylint requires the astroid (the later the better; formerly known as
-logilab-astng) and logilab-common (version >= 0.53) packages.
-
-* https://bitbucket.org/logilab/astroid
-* http://www.logilab.org/projects/common
-
-From the source distribution, extract the tarball and run ::
-
- python setup.py install
-
-You'll have to install dependencies in a similar way. For debian and
-rpm packages, use your usual tools according to your Linux distribution.
-
-More information about installation and available distribution format
-may be found in the user manual in the *doc* subdirectory.
-
-Documentation
--------------
-
-Look in the doc/ subdirectory or at http://docs.pylint.org
-
-Pylint is shipped with following additional commands:
-
-* pyreverse: an UML diagram generator
-* symilar: an independent similarities checker
-* epylint: Emacs and Flymake compatible Pylint
-* pylint-gui: a graphical interface
+
+.. image:: https://drone.io/bitbucket.org/logilab/pylint/status.png
+ :alt: drone.io Build Status
+ :target: https://drone.io/bitbucket.org/logilab/pylint
+
+README for Pylint - http://www.pylint.org/
+==========================================
+
+Pylint is a Python source code analyzer which looks for programming errors,
+helps enforcing a coding standard and sniffs for some code smells (as defined in
+Martin Fowler's Refactoring book).
+
+Pylint has many rules enabled by default, way too much to silence them all on a
+minimally sized program. It's highly configurable and handle pragmas to control
+it from within your code. Additionally, it is possible to write plugins to add
+your own checks.
+
+It's a free software distributed under the GNU Public Licence.
+
+Development is hosted on bitbucket: https://bitbucket.org/logilab/pylint/
+
+You can use the code-quality@python.org mailing list to discuss about
+Pylint. Subscribe at https://mail.python.org/mailman/listinfo/code-quality/
+or read the archives at https://mail.python.org/pipermail/code-quality/
+
+Install
+-------
+
+Pylint requires the astroid (the later the better; formerly known as
+logilab-astng) and logilab-common (version >= 0.53) packages.
+
+* https://bitbucket.org/logilab/astroid
+* http://www.logilab.org/projects/common
+
+From the source distribution, extract the tarball and run ::
+
+ python setup.py install
+
+You'll have to install dependencies in a similar way. For debian and
+rpm packages, use your usual tools according to your Linux distribution.
+
+More information about installation and available distribution format
+may be found in the user manual in the *doc* subdirectory.
+
+Documentation
+-------------
+
+Look in the doc/ subdirectory or at http://docs.pylint.org
+
+Pylint is shipped with following additional commands:
+
+* pyreverse: an UML diagram generator
+* symilar: an independent similarities checker
+* epylint: Emacs and Flymake compatible Pylint
+* pylint-gui: a graphical interface \ No newline at end of file
diff --git a/checkers/__init__.py b/checkers/__init__.py
index 0da4dc4..51adb4d 100644
--- a/checkers/__init__.py
+++ b/checkers/__init__.py
@@ -59,7 +59,7 @@ def table_lines_from_stats(stats, old_stats, columns):
lines = []
for m_type in columns:
new = stats[m_type]
- format = str
+ format = str # pylint: disable=redefined-builtin
if isinstance(new, float):
format = lambda num: '%.3f' % num
old = old_stats.get(m_type)
@@ -84,6 +84,8 @@ class BaseChecker(OptionsProviderMixIn):
msgs = {}
# reports issued by this checker
reports = ()
+ # mark this checker as enabled or not.
+ enabled = True
def __init__(self, linter=None):
"""checker instances should have the linter as argument
@@ -107,31 +109,6 @@ class BaseChecker(OptionsProviderMixIn):
"""called after visiting project (i.e set of modules)"""
-class BaseRawChecker(BaseChecker):
- """base class for raw checkers"""
-
- def process_module(self, node):
- """process a module
-
- the module's content is accessible via the stream object
-
- stream must implement the readline method
- """
- warnings.warn("Modules that need access to the tokens should "
- "use the ITokenChecker interface.",
- DeprecationWarning)
- stream = node.file_stream
- stream.seek(0) # XXX may be removed with astroid > 0.23
- if sys.version_info <= (3, 0):
- self.process_tokens(tokenize.generate_tokens(stream.readline))
- else:
- self.process_tokens(tokenize.tokenize(stream.readline))
-
- def process_tokens(self, tokens):
- """should be overridden by subclasses"""
- raise NotImplementedError()
-
-
class BaseTokenChecker(BaseChecker):
"""Base class for checkers that want to have access to the token stream."""
diff --git a/checkers/base.py b/checkers/base.py
index 0271592..8198d16 100644
--- a/checkers/base.py
+++ b/checkers/base.py
@@ -19,10 +19,16 @@
import collections
import itertools
import sys
-import astroid
+import re
+
+import six
+from six.moves import zip # pylint: disable=redefined-builtin
+
from logilab.common.ureports import Table
-from astroid import are_exclusive, InferenceError
+
+import astroid
import astroid.bases
+from astroid import are_exclusive, InferenceError
from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE, HIGH
from pylint.utils import EmptyReport
@@ -42,10 +48,6 @@ from pylint.checkers.utils import (
)
-import re
-import six
-from six.moves import zip
-
# regex for class/function/variable/constant name
CLASS_NAME_RGX = re.compile('[A-Z_][a-zA-Z0-9]+$')
MOD_NAME_RGX = re.compile('(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$')
@@ -126,6 +128,7 @@ def _loop_exits_early(loop):
for child in loop.body:
if isinstance(child, loop_nodes):
# break statement may be in orelse of child loop.
+ # pylint: disable=superfluous-parens
for orelse in (child.orelse or ()):
for _ in orelse.nodes_of_class(astroid.Break, skip_klass=loop_nodes):
return True
@@ -134,6 +137,13 @@ def _loop_exits_early(loop):
return True
return False
+def _is_multi_naming_match(match, node_type, confidence):
+ return (match is not None and
+ match.lastgroup is not None and
+ match.lastgroup not in EXEMPT_NAME_CATEGORIES
+ and (node_type != 'method' or confidence != INFERENCE_FAILURE))
+
+
if sys.version_info < (3, 0):
PROPERTY_CLASSES = set(('__builtin__.property', 'abc.abstractproperty'))
else:
@@ -183,11 +193,12 @@ def decorated_with_abc(func):
return True
def has_abstract_methods(node):
- """ Determine if the given `node` has
+ """
+ Determine if the given `node` has
abstract methods, defined with `abc` module.
"""
return any(decorated_with_abc(meth)
- for meth in node.mymethods())
+ for meth in node.methods())
def report_by_type_stats(sect, stats, old_stats):
"""make a report of
@@ -290,8 +301,7 @@ class BasicErrorChecker(_BasicChecker):
'E0110': ('Abstract class with abstract methods instantiated',
'abstract-class-instantiated',
'Used when an abstract class with `abc.ABCMeta` as metaclass '
- 'has abstract methods and is instantiated.',
- {'minversion': (3, 0)}),
+ 'has abstract methods and is instantiated.'),
'W0120': ('Else clause on loop without a break statement',
'useless-else-on-loop',
'Loops should only have an else clause if they can exit early '
@@ -388,17 +398,16 @@ class BasicErrorChecker(_BasicChecker):
return
# __init__ was called
metaclass = infered.metaclass()
+ abstract_methods = has_abstract_methods(infered)
if metaclass is None:
# Python 3.4 has `abc.ABC`, which won't be detected
# by ClassNode.metaclass()
for ancestor in infered.ancestors():
- if (ancestor.qname() == 'abc.ABC' and
- has_abstract_methods(infered)):
+ if ancestor.qname() == 'abc.ABC' and abstract_methods:
self.add_message('abstract-class-instantiated', node=node)
break
return
- if (metaclass.qname() == 'abc.ABCMeta' and
- has_abstract_methods(infered)):
+ if metaclass.qname() == 'abc.ABCMeta' and abstract_methods:
self.add_message('abstract-class-instantiated', node=node)
def _check_else_on_loop(self, node):
@@ -478,8 +487,9 @@ functions, methods
'times.'),
'W0122': ('Use of exec',
'exec-used',
- 'Used when you use the "exec" statement (function for Python 3), to discourage its '
- 'usage. That doesn\'t mean you can not use it !'),
+ 'Used when you use the "exec" statement (function for Python '
+ '3), to discourage its usage. That doesn\'t '
+ 'mean you can not use it !'),
'W0123': ('Use of eval',
'eval-used',
'Used when you use the "eval" function, to discourage its '
@@ -508,12 +518,6 @@ functions, methods
'A call of assert on a tuple will always evaluate to true if '
'the tuple is not empty, and will always evaluate to false if '
'it is.'),
- 'W0121': ('Use raise ErrorClass(args) instead of raise ErrorClass, args.',
- 'old-raise-syntax',
- "Used when the alternate raise syntax 'raise foo, bar' is used "
- "instead of 'raise foo(bar)'.",
- {'maxversion': (3, 0)}),
-
'C0121': ('Missing required attribute "%s"', # W0103
'missing-module-attribute',
'Used when an attribute required for modules is missing.'),
@@ -565,7 +569,7 @@ functions, methods
if attr not in node:
self.add_message('missing-module-attribute', node=node, args=attr)
- def visit_class(self, node):
+ def visit_class(self, node): # pylint: disable=unused-argument
"""check module name, docstring and redefinition
increment branch counter
"""
@@ -727,16 +731,12 @@ functions, methods
# 2 - Is it inside final body of a try...finally bloc ?
self._check_not_in_finally(node, 'break', (astroid.For, astroid.While,))
- @check_messages('unreachable', 'old-raise-syntax')
+ @check_messages('unreachable')
def visit_raise(self, node):
"""check if the node has a right sibling (if so, that's some unreachable
code)
"""
self._check_unreachable(node)
- if sys.version_info >= (3, 0):
- return
- if node.exc is not None and node.inst is not None and node.tback is None:
- self.add_message('old-raise-syntax', node=node)
@check_messages('exec-used')
def visit_exec(self, node):
@@ -799,7 +799,7 @@ functions, methods
"""update try...finally flag"""
self._tryfinallys.append(node)
- def leave_tryfinally(self, node):
+ def leave_tryfinally(self, node): # pylint: disable=unused-argument
"""update try...finally flag"""
self._tryfinallys.pop()
@@ -968,8 +968,8 @@ class NameChecker(_BasicChecker):
self._check_name('module', node.name.split('.')[-1], node)
self._bad_names = {}
- def leave_module(self, node):
- for category, all_groups in six.iteritems(self._bad_names):
+ def leave_module(self, node): # pylint: disable=unused-argument
+ for all_groups in six.itervalues(self._bad_names):
if len(all_groups) < 2:
continue
groups = collections.defaultdict(list)
@@ -1054,12 +1054,6 @@ class NameChecker(_BasicChecker):
def _find_name_group(self, node_type):
return self._name_group.get(node_type, node_type)
- def _is_multi_naming_match(self, match, node_type, confidence):
- return (match is not None and
- match.lastgroup is not None and
- match.lastgroup not in EXEMPT_NAME_CATEGORIES
- and (node_type != 'method' or confidence != INFERENCE_FAILURE))
-
def _raise_name_warning(self, node, node_type, name, confidence):
type_label = _NAME_TYPES[node_type][1]
hint = ''
@@ -1084,7 +1078,7 @@ class NameChecker(_BasicChecker):
regexp = getattr(self.config, node_type + '_rgx')
match = regexp.match(name)
- if self._is_multi_naming_match(match, node_type, confidence):
+ if _is_multi_naming_match(match, node_type, confidence):
name_group = self._find_name_group(node_type)
bad_name_group = self._bad_names.setdefault(name_group, {})
warnings = bad_name_group.setdefault(match.lastgroup, [])
diff --git a/checkers/classes.py b/checkers/classes.py
index 3be5bd7..14aa90f 100644
--- a/checkers/classes.py
+++ b/checkers/classes.py
@@ -106,7 +106,7 @@ 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 \
+ '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.'),
@@ -193,7 +193,7 @@ MSGS = {
),
'F0220': ('failed to resolve interfaces implemented by %s (%s)', # W0224
'unresolved-interface',
- 'Used when a PyLint as failed to find interfaces implemented by \
+ 'Used when a Pylint as failed to find interfaces implemented by \
a class'),
diff --git a/checkers/exceptions.py b/checkers/exceptions.py
index a52adbd..e8e5a54 100644
--- a/checkers/exceptions.py
+++ b/checkers/exceptions.py
@@ -68,7 +68,7 @@ MSGS = {
'Used when except clauses are not in the correct order (from the '
'more specific to the more generic). If you don\'t fix the order, '
'some exceptions may not be catched by the most specific handler.'),
- 'E0702': ('Raising %s while only classes, instances or string are allowed',
+ 'E0702': ('Raising %s while only classes or instances are allowed',
'raising-bad-type',
'Used when something which is neither a class, an instance or a \
string is raised (i.e. a `TypeError` will be raised).'),
@@ -91,10 +91,6 @@ MSGS = {
'catching-non-exception',
'Used when a class which doesn\'t inherit from \
BaseException is used as an exception in an except clause.'),
-
- 'W0701': ('Raising a string exception',
- 'raising-string',
- 'Used when a string exception is raised.'),
'W0702': ('No exception type(s) specified',
'bare-except',
'Used when an except clause doesn\'t specify exceptions type to \
@@ -117,17 +113,6 @@ MSGS = {
'Used when the exception to catch is of the form \
"except A or B:". If intending to catch multiple, \
rewrite as "except (A, B):"'),
- 'W0712': ('Implicit unpacking of exceptions is not supported in Python 3',
- 'unpacking-in-except',
- 'Python3 will not allow implicit unpacking of exceptions in except '
- 'clauses. '
- 'See http://www.python.org/dev/peps/pep-3110/',
- {'maxversion': (3, 0)}),
- 'W0713': ('Indexing exceptions will not work on Python 3',
- 'indexing-exception',
- 'Indexing exceptions will not work on Python 3. Use '
- '`exception.args[index]` instead.',
- {'maxversion': (3, 0)}),
}
@@ -151,7 +136,7 @@ class ExceptionsChecker(BaseChecker):
),
)
- @check_messages('raising-string', 'nonstandard-exception',
+ @check_messages('nonstandard-exception',
'raising-bad-type', 'raising-non-exception',
'notimplemented-raised', 'bad-exception-context')
def visit_raise(self, node):
@@ -192,7 +177,8 @@ class ExceptionsChecker(BaseChecker):
if isinstance(expr, astroid.Const):
value = expr.value
if isinstance(value, str):
- self.add_message('raising-string', node=node)
+ # raising-string will be emitted from python3 porting checker.
+ pass
else:
self.add_message('raising-bad-type', node=node,
args=value.__class__.__name__)
@@ -206,8 +192,6 @@ class ExceptionsChecker(BaseChecker):
isinstance(expr.func, astroid.Name) and
expr.func.name == 'NotImplemented')):
self.add_message('notimplemented-raised', node=node)
- elif isinstance(expr, astroid.BinOp) and expr.op == '%':
- self.add_message('raising-string', node=node)
elif isinstance(expr, (Instance, astroid.Class)):
if isinstance(expr, Instance):
expr = expr._proxied
@@ -219,31 +203,13 @@ class ExceptionsChecker(BaseChecker):
else:
self.add_message(
'nonstandard-exception', node=node,
- confidence=INFERENCE if has_known_bases(expr) else INFERENCE_FAILURE)
+ confidence=INFERENCE if has_known_bases(expr) else INFERENCE_FAILURE)
else:
value_found = False
else:
value_found = False
return value_found
- @check_messages('unpacking-in-except')
- def visit_excepthandler(self, node):
- """Visit an except handler block and check for exception unpacking."""
- if isinstance(node.name, (astroid.Tuple, astroid.List)):
- self.add_message('unpacking-in-except', node=node)
-
- @check_messages('indexing-exception')
- def visit_subscript(self, node):
- """ Look for indexing exceptions. """
- try:
- for infered in node.value.infer():
- if not isinstance(infered, astroid.Instance):
- continue
- if inherit_from_std_ex(infered):
- self.add_message('indexing-exception', node=node)
- except astroid.InferenceError:
- return
-
@check_messages('bare-except', 'broad-except', 'pointless-except',
'binary-op-exception', 'bad-except-order',
'catching-non-exception')
@@ -285,8 +251,8 @@ class ExceptionsChecker(BaseChecker):
if (isinstance(exc, astroid.Const) and
exc.value is None):
if ((isinstance(handler.type, astroid.Const) and
- handler.type.value is None) or
- handler.type.parent_of(exc)):
+ handler.type.value is None) or
+ handler.type.parent_of(exc)):
# If the exception handler catches None or
# the exception component, which is None, is
# defined by the entire exception handler, then
diff --git a/checkers/format.py b/checkers/format.py
index 813130b..94a9e8e 100644
--- a/checkers/format.py
+++ b/checkers/format.py
@@ -24,9 +24,10 @@ Some parts of the process_token method is based from The Tab Nanny std module.
import keyword
import sys
import tokenize
-from functools import reduce
+from functools import reduce # pylint: disable=redefined-builtin
+
import six
-from six.moves import zip, map, filter
+from six.moves import zip, map, filter # pylint: disable=redefined-builtin
from astroid import nodes
@@ -105,22 +106,12 @@ MSGS = {
{'old_names': [('C0323', 'no-space-after-operator'),
('C0324', 'no-space-after-comma'),
('C0322', 'no-space-before-operator')]}),
- 'W0331': ('Use of the <> operator',
- 'old-ne-operator',
- 'Used when the deprecated "<>" operator is used instead '
- 'of "!=".',
- {'maxversion': (3, 0)}),
'W0332': ('Use of "l" as long integer identifier',
'lowercase-l-suffix',
'Used when a lower case "l" is used to mark a long integer. You '
'should use a upper case "L" since the letter "l" looks too much '
'like the digit "1"',
{'maxversion': (3, 0)}),
- 'W0333': ('Use of the `` operator',
- 'backtick',
- 'Used when the deprecated "``" (backtick) operator is used '
- 'instead of the str() function.',
- {'scope': WarningScope.NODE, 'maxversion': (3, 0)}),
'C0327': ('Mixed line endings LF and CRLF',
'mixed-line-endings',
'Used when there are mixed (LF and CRLF) newline signs in a file.'),
@@ -408,7 +399,6 @@ class FormatChecker(BaseTokenChecker):
* unauthorized constructions
* strict indentation
* line length
- * use of <> instead of !=
"""
__implements__ = (ITokenChecker, IAstroidChecker, IRawChecker)
@@ -420,7 +410,7 @@ class FormatChecker(BaseTokenChecker):
# configuration options
# for available dict keys/values see the optik parser 'add_option' method
options = (('max-line-length',
- {'default' : 80, 'type' : "int", 'metavar' : '<int>',
+ {'default' : 100, 'type' : "int", 'metavar' : '<int>',
'help' : 'Maximum number of characters on a single line.'}),
('ignore-long-lines',
{'type': 'regexp', 'metavar': '<regexp>',
@@ -453,7 +443,7 @@ class FormatChecker(BaseTokenChecker):
{'type': 'choice', 'metavar': '<empty or LF or CRLF>', 'default': '',
'choices': ['', 'LF', 'CRLF'],
'help': 'Expected format of line ending, e.g. empty (any line ending), LF or CRLF.'}),
- )
+ )
def __init__(self, linter=None):
BaseTokenChecker.__init__(self, linter)
@@ -633,13 +623,13 @@ class FormatChecker(BaseTokenChecker):
return 'No', 'allowed'
def _name_construct(token):
- if tokens[i][1] == ',':
+ if token[1] == ',':
return 'comma'
- elif tokens[i][1] == ':':
+ elif token[1] == ':':
return ':'
- elif tokens[i][1] in '()[]{}':
+ elif token[1] in '()[]{}':
return 'bracket'
- elif tokens[i][1] in ('<', '>', '<=', '>=', '!=', '=='):
+ elif token[1] in ('<', '>', '<=', '>=', '!=', '=='):
return 'comparison'
else:
if self._inside_brackets('('):
@@ -648,7 +638,8 @@ class FormatChecker(BaseTokenChecker):
return 'assignment'
good_space = [True, True]
- pairs = [(tokens[i-1], tokens[i]), (tokens[i], tokens[i+1])]
+ token = tokens[i]
+ pairs = [(tokens[i-1], token), (token, tokens[i+1])]
for other_idx, (policy, token_pair) in enumerate(zip(policies, pairs)):
if token_pair[other_idx][0] in _EOL or policy == _IGNORE:
@@ -669,19 +660,15 @@ class FormatChecker(BaseTokenChecker):
if not ok:
warnings.append((policy, position))
for policy, position in warnings:
- construct = _name_construct(tokens[i])
+ construct = _name_construct(token)
count, state = _policy_string(policy)
- self.add_message('bad-whitespace', line=tokens[i][2][0],
+ self.add_message('bad-whitespace', line=token[2][0],
args=(count, state, position, construct,
- _underline_token(tokens[i])))
+ _underline_token(token)))
def _inside_brackets(self, left):
return self._bracket_stack[-1] == left
- def _handle_old_ne_operator(self, tokens, i):
- if tokens[i][1] == '<>':
- self.add_message('old-ne-operator', line=tokens[i][2][0])
-
def _prepare_token_dispatcher(self):
raw = [
(_KEYWORD_TOKENS,
@@ -701,7 +688,6 @@ class FormatChecker(BaseTokenChecker):
(['lambda'], self._open_lambda),
- (['<>'], self._handle_old_ne_operator),
]
dispatch = {}
@@ -919,10 +905,6 @@ class FormatChecker(BaseTokenChecker):
self.add_message('multiple-statements', node=node)
self._visited_lines[line] = 2
- @check_messages('backtick')
- def visit_backquote(self, node):
- self.add_message('backtick', node=node)
-
def check_lines(self, lines, i):
"""check lines have less than a maximum number of characters
"""
diff --git a/checkers/imports.py b/checkers/imports.py
index 22c0b3c..1969eeb 100644
--- a/checkers/imports.py
+++ b/checkers/imports.py
@@ -18,6 +18,9 @@
import sys
from collections import defaultdict
+import six
+from six.moves import map # pylint: disable=redefined-builtin
+
from logilab.common.graph import get_cycles, DotBackend
from logilab.common.ureports import VerbatimText, Paragraph
@@ -29,8 +32,6 @@ from pylint.interfaces import IAstroidChecker
from pylint.utils import EmptyReport
from pylint.checkers import BaseChecker
from pylint.checkers.utils import check_messages, is_import_error
-import six
-from six.moves import map
def _except_import_error(node):
"""
@@ -237,14 +238,15 @@ given file (report RP0402 must not be disabled)'}
"""called before visiting project (i.e set of modules)"""
# don't try to compute cycles if the associated message is disabled
if self.linter.is_message_enabled('cyclic-import'):
- for cycle in get_cycles(self.import_graph):
+ vertices = list(self.import_graph)
+ for cycle in get_cycles(self.import_graph, vertices=vertices):
self.add_message('cyclic-import', args=' -> '.join(cycle))
def visit_import(self, node):
"""triggered when an import statement is seen"""
modnode = node.root()
for name, _ in node.names:
- importedmodnode = self.get_imported_module(modnode, node, name)
+ importedmodnode = self.get_imported_module(node, name)
if importedmodnode is None:
continue
self._check_relative_import(modnode, node, importedmodnode, name)
@@ -271,7 +273,7 @@ given file (report RP0402 must not be disabled)'}
if name == '*':
self.add_message('wildcard-import', args=basename, node=node)
modnode = node.root()
- importedmodnode = self.get_imported_module(modnode, node, basename)
+ importedmodnode = self.get_imported_module(node, basename)
if importedmodnode is None:
return
self._check_relative_import(modnode, node, importedmodnode, basename)
@@ -281,7 +283,7 @@ given file (report RP0402 must not be disabled)'}
self._add_imported_module(node, '%s.%s' % (importedmodnode.name, name))
self._check_reimport(node, name, basename, node.level)
- def get_imported_module(self, modnode, importnode, modname):
+ def get_imported_module(self, importnode, modname):
try:
return importnode.do_import_module(modname)
except astroid.InferenceError as ex:
diff --git a/checkers/logging.py b/checkers/logging.py
index 277b8c0..897c1c7 100644
--- a/checkers/logging.py
+++ b/checkers/logging.py
@@ -35,14 +35,14 @@ MSGS = {
'interpolation in those cases in which no message will be '
'logged. For more, see '
'http://www.python.org/dev/peps/pep-0282/.'),
- 'W1202': ('Use % formatting in logging functions but pass the % '
- 'parameters as arguments',
- 'logging-format-interpolation',
- 'Used when a logging statement has a call form of '
- '"logging.<logging method>(format_string.format(format_args...))"'
- '. Such calls should use % formatting instead, but leave '
- 'interpolation to the logging function by passing the parameters '
- 'as arguments.'),
+ 'W1202': ('Use % formatting in logging functions but pass the % '
+ 'parameters as arguments',
+ 'logging-format-interpolation',
+ 'Used when a logging statement has a call form of '
+ '"logging.<logging method>(format_string.format(format_args...))"'
+ '. Such calls should use % formatting instead, but leave '
+ 'interpolation to the logging function by passing the parameters '
+ 'as arguments.'),
'E1200': ('Unsupported logging format character %r (%#02x) at index %d',
'logging-unsupported-format',
'Used when an unsupported format character is used in a logging\
@@ -64,6 +64,27 @@ CHECKED_CONVENIENCE_FUNCTIONS = set([
'critical', 'debug', 'error', 'exception', 'fatal', 'info', 'warn',
'warning'])
+def is_method_call(callfunc_node, types=(), methods=()):
+ """Determines if a CallFunc node represents a method call.
+
+ Args:
+ callfunc_node: The CallFunc AST node to check.
+ types: Optional sequence of caller type names to restrict check.
+ methods: Optional sequence of method names to restrict check.
+
+ Returns:
+ True, if the node represents a method call for the given type and
+ method names, False otherwise.
+ """
+ if not isinstance(callfunc_node, astroid.CallFunc):
+ return False
+ func = utils.safe_infer(callfunc_node.func)
+ return (isinstance(func, astroid.BoundMethod)
+ and isinstance(func.bound, astroid.Instance)
+ and (func.bound.name in types if types else True)
+ and (func.name in methods if methods else True))
+
+
class LoggingChecker(checkers.BaseChecker):
"""Checks use of the logging module."""
@@ -81,7 +102,7 @@ class LoggingChecker(checkers.BaseChecker):
),
)
- def visit_module(self, unused_node):
+ def visit_module(self, node): # pylint: disable=unused-argument
"""Clears any state left in this checker from last module checked."""
# The code being checked can just as easily "import logging as foo",
# so it is necessary to process the imports and store in this field
@@ -142,26 +163,6 @@ class LoggingChecker(checkers.BaseChecker):
return
self._check_log_method(node, name)
- def is_method_call(self, callfunc_node, types=(), methods=()):
- """Determines if a CallFunc node represents a method call.
-
- Args:
- callfunc_node: The CallFunc AST node to check.
- types: Optional sequence of caller type names to restrict check.
- methods: Optional sequence of method names to restrict check.
-
- Returns:
- True, if the node represents a method call for the given type and
- method names, False otherwise.
- """
- if not isinstance(callfunc_node, astroid.CallFunc):
- return False
- func = utils.safe_infer(callfunc_node.func)
- return (isinstance(func, astroid.BoundMethod)
- and isinstance(func.bound, astroid.Instance)
- and (func.bound.name in types if types else True)
- and (func.name in methods if methods else True))
-
def _check_log_method(self, node, name):
"""Checks calls to logging.log(level, format, *format_args)."""
if name == 'log':
@@ -192,7 +193,7 @@ class LoggingChecker(checkers.BaseChecker):
Args:
callfunc_node: CallFunc AST node to be checked.
"""
- if self.is_method_call(callfunc_node, ('str', 'unicode'), ('format',)):
+ if is_method_call(callfunc_node, ('str', 'unicode'), ('format',)):
self.add_message('logging-format-interpolation', node=callfunc_node)
def _check_format_string(self, node, format_arg):
diff --git a/checkers/newstyle.py b/checkers/newstyle.py
index 335b052..1656806 100644
--- a/checkers/newstyle.py
+++ b/checkers/newstyle.py
@@ -43,7 +43,7 @@ MSGS = {
{'maxversion': (3, 0)}),
'W1001': ('Use of "property" on an old style class',
'property-on-old-class',
- 'Used when PyLint detect the use of the builtin "property" \
+ 'Used when Pylint detect the use of the builtin "property" \
on an old style class while this is relying on new style \
classes features.',
{'maxversion': (3, 0)}),
@@ -101,7 +101,7 @@ class NewStyleConflictChecker(BaseChecker):
else INFERENCE_FAILURE)
name = node.func.name
if name == 'property':
- self.add_message('property-on-old-class', node=node,
+ self.add_message('property-on-old-class', node=node,
confidence=confidence)
@check_messages('super-on-old-class', 'bad-super-call', 'missing-super-argument')
@@ -125,7 +125,7 @@ class NewStyleConflictChecker(BaseChecker):
if not klass.newstyle:
# super should not be used on an old style class
self.add_message('super-on-old-class', node=node,
- confidence=confidence)
+ confidence=confidence)
else:
# super first arg should be the class
if not call.args and sys.version_info[0] == 3:
diff --git a/checkers/python3.py b/checkers/python3.py
index ca34f09..bdabe20 100644
--- a/checkers/python3.py
+++ b/checkers/python3.py
@@ -14,14 +14,24 @@
"""Check Python 2 code for Python 2/3 source-compatible issues."""
from __future__ import absolute_import
+import re
+import tokenize
+
import astroid
from pylint import checkers, interfaces
+from pylint.utils import WarningScope
from pylint.checkers import utils
+
+_OLD_OCTAL = re.compile("^0+[1-7]+$")
+
+def _is_old_octal(literal):
+ return _OLD_OCTAL.match(literal)
+
def _check_dict_node(node):
- inferred = node.infer()
inferred_types = set()
try:
+ inferred = node.infer()
for inferred_node in inferred:
inferred_types.add(inferred_node)
except (astroid.InferenceError, astroid.UnresolvableName):
@@ -33,7 +43,7 @@ def _check_dict_node(node):
class Python3Checker(checkers.BaseChecker):
__implements__ = interfaces.IAstroidChecker
-
+ enabled = False
name = 'python3'
msgs = {
@@ -44,6 +54,34 @@ class Python3Checker(checkers.BaseChecker):
'Used when a print statement is used '
'(`print` is a function in Python 3)',
{'maxversion': (3, 0)}),
+ 'E1602': ('Parameter unpacking specified',
+ 'parameter-unpacking',
+ 'Used when parameter unpacking is specified for a function'
+ "(Python 3 doesn't allow it)",
+ {'maxversion': (3, 0)}),
+ 'E1603': ('Implicit unpacking of exceptions is not supported '
+ 'in Python 3',
+ 'unpacking-in-except',
+ 'Python3 will not allow implicit unpacking of '
+ 'exceptions in except clauses. '
+ 'See http://www.python.org/dev/peps/pep-3110/',
+ {'maxversion': (3, 0),
+ 'old_names': [('W0712', 'unpacking-in-except')]}),
+ 'E1604': ('Use raise ErrorClass(args) instead of '
+ 'raise ErrorClass, args.',
+ 'old-raise-syntax',
+ "Used when the alternate raise syntax "
+ "'raise foo, bar' is used "
+ "instead of 'raise foo(bar)'.",
+ {'maxversion': (3, 0),
+ 'old_names': [('W0121', 'old-raise-syntax')]}),
+ 'E1605': ('Use of the `` operator',
+ 'backtick',
+ 'Used when the deprecated "``" (backtick) operator is used '
+ 'instead of the str() function.',
+ {'scope': WarningScope.NODE,
+ 'maxversion': (3, 0),
+ 'old_names': [('W0333', 'backtick')]}),
'W1601': ('apply built-in referenced',
'apply-builtin',
'Used when the apply built-in function is referenced '
@@ -161,6 +199,52 @@ class Python3Checker(checkers.BaseChecker):
"Used when a metaclass is specified by assigning to __metaclass__ "
'(Python 3 specifies the metaclass as a class statement argument)',
{'maxversion': (3, 0)}),
+ 'W1624': ('Indexing exceptions will not work on Python 3',
+ 'indexing-exception',
+ 'Indexing exceptions will not work on Python 3. Use '
+ '`exception.args[index]` instead.',
+ {'maxversion': (3, 0),
+ 'old_names': [('W0713', 'indexing-exception')]}),
+ 'W1625': ('Raising a string exception',
+ 'raising-string',
+ 'Used when a string exception is raised. This will not '
+ 'work on Python 3.',
+ {'maxversion': (3, 0),
+ 'old_names': [('W0701', 'raising-string')]}),
+ 'W1626': ('reload built-in referenced',
+ 'reload-builtin',
+ 'Used when the reload built-in function is referenced '
+ '(missing from Python 3). You can use instead imp.reload '
+ 'or importlib.reload.',
+ {'maxversion': (3, 0)}),
+ 'W1627': ('__oct__ method defined',
+ 'oct-method',
+ 'Used when a __oct__ method is defined '
+ '(method is not used by Python 3)',
+ {'maxversion': (3, 0)}),
+ 'W1628': ('__hex__ method defined',
+ 'hex-method',
+ 'Used when a __hex__ method is defined '
+ '(method is not used by Python 3)',
+ {'maxversion': (3, 0)}),
+ 'W1629': ('__nonzero__ method defined',
+ 'nonzero-method',
+ 'Used when a __nonzero__ method is defined '
+ '(method is not used by Python 3)',
+ {'maxversion': (3, 0)}),
+ 'W1630': ('__cmp__ method defined',
+ 'cmp-method',
+ 'Used when a __cmp__ method is defined '
+ '(method is not used by Python 3)',
+ {'maxversion': (3, 0)}),
+ 'W1631': ('map is used as implicitly evaluated call',
+ 'implicit-map-evaluation',
+ 'Used when the map builtin is used as implicitly '
+ 'evaluated call, as in "map(func, args)" on a single line. '
+ 'This behaviour will not work in Python 3, where '
+ 'map is a generator and must be evaluated. '
+ 'Prefer a for-loop as alternative.',
+ {'maxversion': (3, 0)}),
}
_missing_builtins = frozenset([
@@ -177,6 +261,7 @@ class Python3Checker(checkers.BaseChecker):
'StandardError',
'unicode',
'xrange',
+ 'reload',
])
_unused_magic_methods = frozenset([
@@ -184,6 +269,10 @@ class Python3Checker(checkers.BaseChecker):
'__delslice__',
'__getslice__',
'__setslice__',
+ '__oct__',
+ '__hex__',
+ '__nonzero__',
+ '__cmp__',
])
def __init__(self, *args, **kwargs):
@@ -198,6 +287,21 @@ class Python3Checker(checkers.BaseChecker):
method_name = node.name[2:-2]
self.add_message(method_name + '-method', node=node)
+ @utils.check_messages('parameter-unpacking')
+ def visit_arguments(self, node):
+ for arg in node.args:
+ if isinstance(arg, astroid.Tuple):
+ self.add_message('parameter-unpacking', node=arg)
+
+ @utils.check_messages('implicit-map-evaluation')
+ def visit_discard(self, node):
+ if (isinstance(node.value, astroid.CallFunc) and
+ isinstance(node.value.func, astroid.Name) and
+ node.value.func.name == 'map'):
+ module = node.value.func.lookup('map')[0]
+ if getattr(module, 'name', None) == '__builtin__':
+ self.add_message('implicit-map-evaluation', node=node)
+
def visit_name(self, node):
"""Detect when a built-in that is missing in Python 3 is referenced."""
found_node = node.lookup(node.name)[0]
@@ -240,6 +344,9 @@ class Python3Checker(checkers.BaseChecker):
else:
self.add_message('old-division', node=node)
+ @utils.check_messages('next-method-called',
+ 'dict-iter-method',
+ 'dict-view-method')
def visit_callfunc(self, node):
if not isinstance(node.func, astroid.Getattr):
return
@@ -254,6 +361,97 @@ class Python3Checker(checkers.BaseChecker):
elif node.func.attrname in ('viewkeys', 'viewvalues', 'viewitems'):
self.add_message('dict-view-method', node=node)
+ @utils.check_messages('indexing-exception')
+ def visit_subscript(self, node):
+ """ Look for indexing exceptions. """
+ try:
+ for infered in node.value.infer():
+ if not isinstance(infered, astroid.Instance):
+ continue
+ if utils.inherit_from_std_ex(infered):
+ self.add_message('indexing-exception', node=node)
+ except astroid.InferenceError:
+ return
+
+ @utils.check_messages('unpacking-in-except')
+ def visit_excepthandler(self, node):
+ """Visit an except handler block and check for exception unpacking."""
+ if isinstance(node.name, (astroid.Tuple, astroid.List)):
+ self.add_message('unpacking-in-except', node=node)
+
+ @utils.check_messages('backtick')
+ def visit_backquote(self, node):
+ self.add_message('backtick', node=node)
+
+ @utils.check_messages('raising-string', 'old-raise-syntax')
+ def visit_raise(self, node):
+ """Visit a raise statement and check for raising
+ strings or old-raise-syntax.
+ """
+ if (node.exc is not None and
+ node.inst is not None and
+ node.tback is None):
+ self.add_message('old-raise-syntax', node=node)
+
+ # Ignore empty raise.
+ if node.exc is None:
+ return
+ expr = node.exc
+ if self._check_raise_value(node, expr):
+ return
+ else:
+ try:
+ value = next(astroid.unpack_infer(expr))
+ except astroid.InferenceError:
+ return
+ self._check_raise_value(node, value)
+
+ def _check_raise_value(self, node, expr):
+ if isinstance(expr, astroid.Const):
+ value = expr.value
+ if isinstance(value, str):
+ self.add_message('raising-string', node=node)
+ return True
+
+
+class Python3TokenChecker(checkers.BaseTokenChecker):
+ __implements__ = interfaces.ITokenChecker
+ name = 'python3'
+ enabled = False
+
+ msgs = {
+ 'E1606': ('Use of long suffix',
+ 'long-suffix',
+ 'Used when "l" or "L" is used to mark a long integer. '
+ 'This will not work in Python 3, since `int` and `long` '
+ 'types have merged.',
+ {'maxversion': (3, 0)}),
+ 'E1607': ('Use of the <> operator',
+ 'old-ne-operator',
+ 'Used when the deprecated "<>" operator is used instead '
+ 'of "!=". This is removed in Python 3.',
+ {'maxversion': (3, 0),
+ 'old_names': [('W0331', 'old-ne-operator')]}),
+ 'E1608': ('Use of old octal literal',
+ 'old-octal-literal',
+ 'Usen when encountering the old octal syntax, '
+ 'removed in Python 3. To use the new syntax, '
+ 'prepend 0o on the number.',
+ {'maxversion': (3, 0)}),
+ }
+
+ def process_tokens(self, tokens):
+ for idx, (tok_type, token, start, _, _) in enumerate(tokens):
+ if tok_type == tokenize.NUMBER:
+ if token.lower().endswith('l'):
+ # This has a different semantic than lowercase-l-suffix.
+ self.add_message('long-suffix', line=start[0])
+ elif _is_old_octal(token):
+ self.add_message('old-octal-literal', line=start[0])
+ if tokens[idx][1] == '<>':
+ self.add_message('old-ne-operator', line=tokens[idx][2][0])
+
def register(linter):
linter.register_checker(Python3Checker(linter))
+ linter.register_checker(Python3TokenChecker(linter))
diff --git a/checkers/spelling.py b/checkers/spelling.py
index ee02564..3990633 100644
--- a/checkers/spelling.py
+++ b/checkers/spelling.py
@@ -135,7 +135,7 @@ class SpellingChecker(BaseTokenChecker):
words = []
for word in line2.split():
# Skip words with digits.
- if len(re.findall("\d", word)) > 0:
+ if len(re.findall(r"\d", word)) > 0:
continue
# Skip words with mixed big and small letters,
@@ -181,7 +181,7 @@ class SpellingChecker(BaseTokenChecker):
# TODO: add support for customising this.
suggestions = self.spelling_dict.suggest(word)[:4]
- m = re.search("(\W|^)(%s)(\W|$)" % word, line.lower())
+ m = re.search(r"(\W|^)(%s)(\W|$)" % word, line.lower())
if m:
# Start position of second group in regex.
col = m.regs[2][0]
diff --git a/checkers/stdlib.py b/checkers/stdlib.py
index 9a56434..d8b5fde 100644
--- a/checkers/stdlib.py
+++ b/checkers/stdlib.py
@@ -32,9 +32,9 @@ if sys.version_info >= (3, 0):
else:
OPEN_MODULE = '__builtin__'
-class OpenModeChecker(BaseChecker):
+class StdlibChecker(BaseChecker):
__implements__ = (IAstroidChecker,)
- name = 'open_mode'
+ name = 'stdlib'
msgs = {
'W1501': ('"%s" is not a valid mode for open.',
@@ -104,5 +104,5 @@ class OpenModeChecker(BaseChecker):
def register(linter):
"""required method to auto register this checker """
- linter.register_checker(OpenModeChecker(linter))
+ linter.register_checker(StdlibChecker(linter))
diff --git a/checkers/strings.py b/checkers/strings.py
index f603e80..e88085d 100644
--- a/checkers/strings.py
+++ b/checkers/strings.py
@@ -453,12 +453,9 @@ class StringMethodsChecker(BaseChecker):
# use attribute / item access
continue
if argument.parent and isinstance(argument.parent, astroid.Arguments):
- # Check to see if our argument is kwarg or vararg,
- # and skip the check for this argument if so, because when inferring,
- # astroid will return empty objects (dicts and tuples) and
- # that can lead to false positives.
- if argname.name in (argument.parent.kwarg, argument.parent.vararg):
- continue
+ # Ignore any object coming from an argument,
+ # because we can't infer its value properly.
+ continue
previous = argument
parsed = []
for is_attribute, specifier in specifiers:
@@ -541,17 +538,18 @@ class StringConstantChecker(BaseTokenChecker):
self._unicode_literals = 'unicode_literals' in module.future_imports
def process_tokens(self, tokens):
- for (tok_type, token, (start_row, start_col), _, _) in tokens:
+ for (tok_type, token, (start_row, _), _, _) in tokens:
if tok_type == tokenize.STRING:
# 'token' is the whole un-parsed token; we can look at the start
# of it to see whether it's a raw or unicode string etc.
- self.process_string_token(token, start_row, start_col)
+ self.process_string_token(token, start_row)
- def process_string_token(self, token, start_row, start_col):
+ def process_string_token(self, token, start_row):
for i, c in enumerate(token):
if c in '\'\"':
quote_char = c
break
+ # pylint: disable=undefined-loop-variable
prefix = token[:i].lower() # markers like u, b, r.
after_prefix = token[i:]
if after_prefix[:3] == after_prefix[-3:] == 3 * quote_char:
@@ -560,18 +558,15 @@ class StringConstantChecker(BaseTokenChecker):
string_body = after_prefix[1:-1] # Chop off quotes
# No special checks on raw strings at the moment.
if 'r' not in prefix:
- self.process_non_raw_string_token(prefix, string_body,
- start_row, start_col)
+ self.process_non_raw_string_token(prefix, string_body, start_row)
- def process_non_raw_string_token(self, prefix, string_body, start_row,
- start_col):
+ def process_non_raw_string_token(self, prefix, string_body, start_row):
"""check for bad escapes in a non-raw string.
prefix: lowercase string of eg 'ur' string prefix markers.
string_body: the un-parsed body of the string, not including the quote
marks.
start_row: integer line number in the source.
- start_col: integer column number in the source.
"""
# Walk through the string; if we see a backslash then escape the next
# character, and skip over it. If we see a non-escaped character,
diff --git a/checkers/utils.py b/checkers/utils.py
index 4be844c..f3a7d17 100644
--- a/checkers/utils.py
+++ b/checkers/utils.py
@@ -157,12 +157,12 @@ def is_defined_before(var_node):
if ass_node.name == varname:
return True
elif isinstance(_node, astroid.With):
- for expr, vars in _node.items:
+ for expr, ids in _node.items:
if expr.parent_of(var_node):
break
- if (vars and
- isinstance(vars, astroid.AssName) and
- vars.name == varname):
+ if (ids and
+ isinstance(ids, astroid.AssName) and
+ ids.name == varname):
return True
elif isinstance(_node, (astroid.Lambda, astroid.Function)):
if _node.args.is_argument(varname):
diff --git a/checkers/variables.py b/checkers/variables.py
index b5ea34f..8f6f957 100644
--- a/checkers/variables.py
+++ b/checkers/variables.py
@@ -21,9 +21,8 @@ import re
from copy import copy
import astroid
-from astroid import are_exclusive, builtin_lookup, AstroidBuildingException
-
-from logilab.common.modutils import file_from_modpath
+from astroid import are_exclusive, builtin_lookup
+from astroid import modutils
from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE, HIGH
from pylint.utils import get_global_option
@@ -171,6 +170,23 @@ def _fix_dot_imports(not_consumed):
names[second_name] = stmt
return sorted(names.items(), key=lambda a: a[1].fromlineno)
+def _find_frame_imports(name, frame):
+ """
+ Detect imports in the frame, with the required
+ *name*. Such imports can be considered assignments.
+ Returns True if an import for the given name was found.
+ """
+ imports = frame.nodes_of_class((astroid.Import, astroid.From))
+ for import_node in imports:
+ for import_name, import_alias in import_node.names:
+ # If the import uses an alias, check only that.
+ # Otherwise, check only the import name.
+ if import_alias:
+ if import_alias == name:
+ return True
+ elif import_name and import_name == name:
+ return True
+
MSGS = {
'E0601': ('Using variable %r before assignment',
@@ -201,7 +217,7 @@ MSGS = {
'W0603': ('Using the global statement', # W0121
'global-statement',
'Used when you use the "global" statement to update a global \
- variable. PyLint just try to discourage this \
+ variable. Pylint just try to discourage this \
usage. That doesn\'t mean you can not use it !'),
'W0604': ('Using the global statement at the module level', # W0103
'global-at-module-level',
@@ -348,7 +364,7 @@ builtins. Remember that you should avoid to define new builtins when possible.'
if os.path.basename(basename) == '__init__':
name = node.name + "." + elt_name
try:
- file_from_modpath(name.split("."))
+ modutils.file_from_modpath(name.split("."))
except ImportError:
self.add_message('undefined-all-variable',
args=elt_name,
@@ -564,23 +580,6 @@ builtins. Remember that you should avoid to define new builtins when possible.'
continue
self.add_message('unused-variable', args=name, node=stmt)
- def _find_frame_imports(self, name, frame):
- """
- Detect imports in the frame, with the required
- *name*. Such imports can be considered assignments.
- Returns True if an import for the given name was found.
- """
- imports = frame.nodes_of_class((astroid.Import, astroid.From))
- for import_node in imports:
- for import_name, import_alias in import_node.names:
- # If the import uses an alias, check only that.
- # Otherwise, check only the import name.
- if import_alias:
- if import_alias == name:
- return True
- elif import_name and import_name == name:
- return True
-
@check_messages('global-variable-undefined', 'global-variable-not-assigned', 'global-statement',
'global-at-module-level', 'redefined-builtin')
def visit_global(self, node):
@@ -606,7 +605,7 @@ builtins. Remember that you should avoid to define new builtins when possible.'
# same scope level assignment
break
else:
- if not self._find_frame_imports(name, frame):
+ if not _find_frame_imports(name, frame):
self.add_message('global-variable-not-assigned',
args=name, node=node)
default_message = False
@@ -626,7 +625,7 @@ builtins. Remember that you should avoid to define new builtins when possible.'
if default_message:
self.add_message('global-statement', node=node)
- def _check_late_binding_closure(self, node, assignment_node, scope_type):
+ def _check_late_binding_closure(self, node, assignment_node):
def _is_direct_lambda_call():
return (isinstance(node_scope.parent, astroid.CallFunc)
and node_scope.parent.func is node_scope)
@@ -763,7 +762,7 @@ builtins. Remember that you should avoid to define new builtins when possible.'
# variable used outside the loop
if name in consumed:
defnode = assign_parent(consumed[name][0])
- self._check_late_binding_closure(node, defnode, scope_type)
+ self._check_late_binding_closure(node, defnode)
self._loopvar_name(node, name)
break
# mark the name as consumed if it's defined in this scope
@@ -775,7 +774,7 @@ builtins. Remember that you should avoid to define new builtins when possible.'
# checks for use before assignment
defnode = assign_parent(to_consume[name][0])
if defnode is not None:
- self._check_late_binding_closure(node, defnode, scope_type)
+ self._check_late_binding_closure(node, defnode)
defstmt = defnode.statement()
defframe = defstmt.frame()
maybee0601 = True
@@ -802,15 +801,15 @@ builtins. Remember that you should avoid to define new builtins when possible.'
annotation_return = False
# The class reuses itself in the class scope.
recursive_klass = (frame is defframe and
- defframe.parent_of(node) and
- isinstance(defframe, astroid.Class) and
- node.name == defframe.name)
+ defframe.parent_of(node) and
+ isinstance(defframe, astroid.Class) and
+ node.name == defframe.name)
if (self._to_consume[-1][-1] == 'lambda' and
isinstance(frame, astroid.Class)
and name in frame.locals):
maybee0601 = True
elif (isinstance(defframe, astroid.Class) and
- isinstance(frame, astroid.Function)):
+ isinstance(frame, astroid.Function)):
# Special rule for function return annotations,
# which uses the same name as the class where
# the function lives.
@@ -835,8 +834,8 @@ builtins. Remember that you should avoid to define new builtins when possible.'
'Exception',
'BaseException'))):
if recursive_klass or (defstmt is stmt and
- isinstance(node, (astroid.DelName,
- astroid.AssName))):
+ isinstance(node, (astroid.DelName,
+ astroid.AssName))):
self.add_message('undefined-variable', args=name, node=node)
elif annotation_return:
self.add_message('undefined-variable', args=name, node=node)
@@ -894,9 +893,7 @@ builtins. Remember that you should avoid to define new builtins when possible.'
level = getattr(node, 'level', None)
try:
module = node.root().import_module(name_parts[0], level=level)
- except AstroidBuildingException:
- return
- except Exception:
+ except Exception: # pylint: disable=broad-except
return
module = self._check_module_attrs(node, module, name_parts[1:])
if not module:
diff --git a/config.py b/config.py
index 942dc21..ebfe578 100644
--- a/config.py
+++ b/config.py
@@ -53,7 +53,7 @@ def load_results(base):
try:
with open(data_file, _PICK_LOAD) as stream:
return pickle.load(stream)
- except:
+ except Exception: # pylint: disable=broad-except
return {}
if sys.version_info < (3, 0):
diff --git a/doc/contribute.rst b/doc/contribute.rst
index 6a45dea..43e5dfb 100644
--- a/doc/contribute.rst
+++ b/doc/contribute.rst
@@ -47,7 +47,7 @@ http://lists.logilab.org/pipermail/python-projects/
Forge
-----
-Pylint is developped using the mercurial_ distributed version control system.
+Pylint is developed using the mercurial_ distributed version control system.
You can clone Pylint and its dependencies from ::
@@ -64,7 +64,7 @@ your patch gets accepted.
- Pylint keeps a set of unit tests in the /test directory. The
`test_func.py` module uses external files to have some kind of easy
- functionnal testing. To get your patch accepted you must write (or change)
+ functional testing. To get your patch accepted you must write (or change)
a test input file in the `test/input` directory and message file in the
`test/messages` directory. Then run `python test_func.py` to ensure that
your test is green.
@@ -117,7 +117,7 @@ without installing them. You can run all the unit tests like so::
The -S flag keeps distutils from interfering with sys.path. YMMV.
-Adding new functionnal tests
+Adding new functional tests
----------------------------
Pylint comes with an easy way to write functional tests for new checks:
diff --git a/doc/extend.rst b/doc/extend.rst
index 476c994..b767852 100644
--- a/doc/extend.rst
+++ b/doc/extend.rst
@@ -8,7 +8,8 @@ You can find some simple examples in the examples
directory of the distribution (custom.py and custom_raw.py). I'll try to
quickly explain the essentials here.
-First, there are two kinds of checkers :
+First, there are two kinds of checkers:
+
* raw checkers, which are analysing each module as a raw file stream
* ast checkers, which are working on an ast representation of the module
@@ -25,8 +26,8 @@ Checkers are ordered by priority. For each module, Pylint's engine:
1. give the module source file as a stream to raw checkers
2. get an ast representation for the module
-3. make a depth first descent of the tree, calling visit_<> on each AST
- checker when entering a node, and living_<> on the back traversal
+3. make a depth first descent of the tree, calling ``visit_<>`` on each AST
+ checker when entering a node, and ``leave_<>`` on the back traversal
Notice that the source code is probably the best source of
documentation, it should be clear and well documented. Don't hesitate to
diff --git a/doc/faq.rst b/doc/faq.rst
index e3cd73e..c81bbc3 100644
--- a/doc/faq.rst
+++ b/doc/faq.rst
@@ -1,5 +1,7 @@
.. -*- coding: utf-8 -*-
+.. _faq:
+
==========================
Frequently Asked Questions
==========================
@@ -169,7 +171,6 @@ No, starting from 0.25.3, you can use symbolic names for messages::
# pylint: disable=fixme, line-too-long
-You can show these symbols in the output with the `-sy` option.
4.5 I have a callback function where I have no control over received arguments. How do I avoid getting unused argument warnings?
----------------------------------------------------------------------------------------------------------------------------------
@@ -192,30 +193,6 @@ tricks like: ::
method-hidden,
too-many-lines
-4.7 Why do I get a lot of spurious "unused variables messages" when using psyobj from psyco_?
-----------------------------------------------------------------------------------------------
-
-This is actually due to a bug in psyco, making the locals()
-function for objects inheriting from *psyobj* returning an empty
-dictionary. For the moment, the only way to fix this is to use the
-PYLINT_IMPORT environment variable to not use psyco during Pylint
-checking. Sample code ::
-
- import os
- try:
- if os.environ.has_key('PYLINT_IMPORT'):
- raise ImportError()
- from psyco.classes import psyobj
- except ImportError:
- class psyobj:
- pass
-
-NOTICE: this problem should not occur with Pylint >= 0.5 since from
-this version Pylint is not looking anymore for information in living
-objects (i.e. it no longer imports analysed modules)
-
-.. _psyco: http://psyco.sf.net
-
5. Classes and Inheritance
==========================
diff --git a/doc/ide-integration.rst b/doc/ide-integration.rst
index de5116c..7507fb1 100644
--- a/doc/ide-integration.rst
+++ b/doc/ide-integration.rst
@@ -3,35 +3,33 @@
IDE integration
=================
-To use Pylint with Emacs, see http://www.emacswiki.org/emacs/PythonProgrammingInEmacs#toc8
+To use Pylint with:
-To use Pylint with Vim, see
-http://www.vim.org/scripts/script.php?script_id=891
+ - Emacs_, see http://www.emacswiki.org/emacs/PythonProgrammingInEmacs#toc8,
+ - Vim_, see http://www.vim.org/scripts/script.php?script_id=891,
+ - Eclipse_ and PyDev_, see http://pydev.org/manual_adv_pylint.html,
+ - Komodo_, see http://mateusz.loskot.net/posts/2006/01/15/running-pylint-from-komodo/,
+ - gedit_, see https://launchpad.net/gedit-pylint-2 or https://wiki.gnome.org/Apps/Gedit/PylintPlugin,
+ - WingIDE_, see http://www.wingware.com/doc/edit/pylint,
+ - PyCharm_, see http://blog.saturnlaboratories.co.za/archive/2012/09/10/running-pylint-pycharm.
-To use Pylint with Eclipse, see http://pydev.org
+Pylint is integrated in:
-To use Pylint with Komodo_, see
-http://mateusz.loskot.net/2006/01/15/running-pylint-from-komodo/
-
-To use Pylint with gedit_, see
-http://live.gnome.org/Gedit/PylintPlugin
-
-To use Pylint with WingIDE_, see
-http://www.wingware.com/doc/edit/pylint
-
-Pylint is integrated in Eric_ IDE, see the `Project > Check` menu.
-
-Pylint is integrated in Spyder_, see http://packages.python.org/spyder/pylint.html
-
-Pylint is integrated in pyscripter_, see the `Tool -> Tools` menu.
+ - Eric_ IDE, see the `Project > Check` menu,
+ - Spyder_, see http://packages.python.org/spyder/pylint.html,
+ - pyscripter_, see the `Tool -> Tools` menu.
+.. _Emacs: http://www.gnu.org/software/emacs/
+.. _Vim: http://www.vim.org/
+.. _Eclipse: https://www.eclipse.org/
.. _Eric: http://eric-ide.python-projects.org/
.. _pyscripter: http://code.google.com/p/pyscripter/
.. _pydev: http://pydev.org
.. _Komodo: http://www.activestate.com/Products/Komodo/
-.. _gedit: http://www.gnome.org/projects/gedit/
+.. _gedit: https://wiki.gnome.org/Apps/Gedit
.. _WingIDE: http://www.wingware.com/
.. _spyder: http://code.google.com/p/spyderlib/
+.. _PyCharm: http://www.jetbrains.com/pycharm/
Using Pylint thru flymake in Emacs
==================================
diff --git a/doc/options.rst b/doc/options.rst
index 702d93c..d005dd2 100644
--- a/doc/options.rst
+++ b/doc/options.rst
@@ -1,13 +1,13 @@
.. -*- coding: utf-8 -*-
===============
- Configuration
+ Configuration
===============
Naming Styles
-------------
-PyLint recognizes a number of different name types internally. With a few
+Pylint recognizes a number of different name types internally. With a few
exceptions, the type of the name is governed by the location the assignment to a
name is found in, and not the type of object assigned.
@@ -88,10 +88,10 @@ Large code bases that have been worked on for multiple years often exhibit an
evolution in style as well. In some cases, modules can be in the same package,
but still have different naming style based on the stratum they belong to.
However, intra-module consistency should still be required, to make changes
-inside a single file easier. For this case, PyLint supports regular expression
-with several named capturing group.
+inside a single file easier. For this case, Pylint supports regular expression
+with several named capturing group.
-Rather than emitting name warnings immediately, PyLint will determine the
+Rather than emitting name warnings immediately, Pylint will determine the
prevalent naming style inside each module and enforce it on all names.
Consider the following (simplified) example::
diff --git a/doc/output.rst b/doc/output.rst
index 3a87547..537bfcb 100644
--- a/doc/output.rst
+++ b/doc/output.rst
@@ -33,7 +33,7 @@ C
category
fullname of the message category
-For exemple the former (pre 1.0) default format can be obtained with::
+For example, the former (pre 1.0) default format can be obtained with::
pylint --msg-template='{msg_id}:{line:3d},{column}: {obj}: {msg}'
diff --git a/doc/run.rst b/doc/run.rst
index f8a65e2..418e915 100644
--- a/doc/run.rst
+++ b/doc/run.rst
@@ -31,9 +31,9 @@ directory is automatically added on top of the python path ::
will work if "directory" is a python package (i.e. has an __init__.py
file) or if "directory" is in the python path.
-For more details on this see the Frequently Asked Questions.
+For more details on this see the :ref:`faq`.
-You can also start a thin gui around Pylint (require TkInter) by
+You can also start a thin gui around Pylint (require tkinter) by
typing ::
pylint-gui
@@ -43,7 +43,7 @@ or module to check, at Pylint messages will be displayed in the user
interface.
It is also possible to call Pylint from an other python program,
-thanks to ``py_run()`` function in ``lint`` module,
+thanks to ``py_run()`` function in ``epylint`` module,
assuming Pylint options are stored in ``pylint_options`` string, as:
.. sourcecode:: python
@@ -52,12 +52,12 @@ assuming Pylint options are stored in ``pylint_options`` string, as:
lint.py_run(pylint_options)
To silently run Pylint on a ``module_name.py`` module,
-and get its standart output and error:
+and get its standard output and error:
.. sourcecode:: python
from pylint import epylint as lint
- (pylint_stdout, pylint_stderr) = lint.py_run('module_name.py', True)
+ (pylint_stdout, pylint_stderr) = lint.py_run('module_name.py', return_std=True)
Command line options
@@ -134,20 +134,23 @@ Other useful global options include:
Parallel execution
------------------
-It is possible to speed up the execution of Pylint. If the running computer has more CPUs than one
-then the files to be checked could be spread on all processors to Pylint sub-processes.
-This functionality is exposed via ``-j`` command line parameter. It takes a number of sub-processes
-that should be spawned. If provided number is 0 then the number of CPUs will be taken.
-Default number is 1.
+It is possible to speed up the execution of Pylint. If the running computer
+has more CPUs than one, then the files to be checked could be spread on all
+processors to Pylint sub-processes.
+This functionality is exposed via ``-j`` command line parameter.
+It takes a number of sub-processes that should be spawned.
+If the provided number is 0 then the number of CPUs will be used.
+The default number of workers is 1.
Example::
pylint -j 4 mymodule1.py mymodule2.py mymodule3.py mymodule4.py
-This will spawn 4 parallel Pylint sub-process. Each provided module will be checked in parallel.
-Discovered problems by checkers are not displayed immediatelly. They are shown just after completing
-checking a module.
+This will spawn 4 parallel Pylint sub-process, where each provided module will
+be checked in parallel. Discovered problems by checkers are not displayed
+immediately. They are shown just after completing checking a module.
-There are some limitations in running checks in parallel in current implementation.
-It is not possible to use custom plugins (i.e. ``--load-plugins`` option).
-It is also not possible to use initialization hook (i.e. ``--init-hook`` option).
+There are some limitations in running checks in parallel in current
+implementation. It is not possible to use custom plugins
+(i.e. ``--load-plugins`` option), nor it is not possible to use
+initialization hooks (i.e. ``--init-hook`` option).
diff --git a/epylint.py b/epylint.py
index b34ef63..4fd683e 100755
--- a/epylint.py
+++ b/epylint.py
@@ -114,18 +114,18 @@ def py_run(command_options='', return_std=False, stdout=None, stderr=None,
"""Run pylint from python
``command_options`` is a string containing ``pylint`` command line options;
- ``return_std`` (boolean) indicates return of created standart output
+ ``return_std`` (boolean) indicates return of created standard output
and error (see below);
- ``stdout`` and ``stderr`` are 'file-like' objects in which standart output
+ ``stdout`` and ``stderr`` are 'file-like' objects in which standard output
could be written.
Calling agent is responsible for stdout/err management (creation, close).
- Default standart output and error are those from sys,
+ Default standard output and error are those from sys,
or standalone ones (``subprocess.PIPE``) are used
if they are not set and ``return_std``.
If ``return_std`` is set to ``True``, this function returns a 2-uple
- containing standart output and error related to created process,
+ containing standard output and error related to created process,
as follows: ``(stdout, stderr)``.
A trivial usage could be as follows:
@@ -134,14 +134,14 @@ def py_run(command_options='', return_std=False, stdout=None, stderr=None,
pylint 0.18.1,
...
- To silently run Pylint on a module, and get its standart output and error:
+ To silently run Pylint on a module, and get its standard output and error:
>>> (pylint_stdout, pylint_stderr) = py_run( 'module_name.py', True)
"""
# Create command line to call pylint
if os.name == 'nt':
script += '.bat'
command_line = script + ' ' + command_options
- # Providing standart output and/or error if not set
+ # Providing standard output and/or error if not set
if stdout is None:
if return_std:
stdout = PIPE
@@ -156,7 +156,7 @@ def py_run(command_options='', return_std=False, stdout=None, stderr=None,
p = Popen(command_line, shell=True, stdout=stdout, stderr=stderr,
env=_get_env(), universal_newlines=True)
p.wait()
- # Return standart output and error
+ # Return standard output and error
if return_std:
return (p.stdout, p.stderr)
diff --git a/examples/pylintrc b/examples/pylintrc
index 5f21f58..69f0ee1 100644
--- a/examples/pylintrc
+++ b/examples/pylintrc
@@ -12,7 +12,7 @@ profile=no
# Add files or directories to the blacklist. They should be base names, not
# paths.
-ignore=.hg,test
+ignore=CVS
# Pickle collected data for later comparisons.
persistent=yes
@@ -27,9 +27,16 @@ include-ids=no
# DEPRECATED
symbols=no
+# Use multiple processes to speed up Pylint.
+jobs=1
+
[MESSAGES CONTROL]
+# Only show warnings with the listed confidence levels. Leave empty to show
+# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
+confidence=
+
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time. See also the "--disable" option for examples.
@@ -85,30 +92,22 @@ comment=no
logging-modules=logging
-[FORMAT]
-
-# Maximum number of characters on a single line.
-max-line-length=80
-
-# Regexp for a line that is allowed to be longer than the limit.
-ignore-long-lines=^\s*(# )?<?https?://\S+>?$
-
-# Allow the body of an if to be on the same line as the test if there is no
-# else.
-single-line-if-stmt=no
+[VARIABLES]
-# List of optional constructs for which whitespace checking is disabled
-no-space-check=trailing-comma,dict-separator
+# Tells whether we should check for unused import in __init__ files.
+init-import=no
-# Maximum number of lines in a module
-max-module-lines=1000
+# A regular expression matching the name of dummy variables (i.e. expectedly
+# not used).
+dummy-variables-rgx=_$|dummy
-# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
-# tab).
-indent-string=' '
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
-# Number of spaces of indent required inside a hanging or continued line.
-indent-after-paren=4
+# List of strings which can identify a callback function by name. A callback
+# name must start or end with one of those strings.
+callbacks=cb_,_cb
[BASIC]
@@ -117,7 +116,7 @@ indent-after-paren=4
required-attributes=
# List of builtins function names that should not be used, separated by a comma
-bad-functions=map,filter,apply,input
+bad-functions=map,filter,input
# Good variable names which should always be accepted, separated by a comma
good-names=i,j,k,ex,Run,_
@@ -201,21 +200,6 @@ no-docstring-rgx=__.*__
docstring-min-length=-1
-[SIMILARITIES]
-
-# Minimum lines number of a similarity.
-min-similarity-lines=4
-
-# Ignore comments when computing similarities.
-ignore-comments=yes
-
-# Ignore docstrings when computing similarities.
-ignore-docstrings=yes
-
-# Ignore imports when computing similarities.
-ignore-imports=no
-
-
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
@@ -247,36 +231,85 @@ zope=no
generated-members=REQUEST,acl_users,aq_parent
-[VARIABLES]
+[SPELLING]
-# Tells whether we should check for unused import in __init__ files.
-init-import=no
+# Spelling dictionary name. Available dictionaries: none. To make it working
+# install python-enchant package.
+spelling-dict=
-# A regular expression matching the name of dummy variables (i.e. expectedly
-# not used).
-dummy-variables-rgx=_|dummy
+# List of comma separated words that should not be checked.
+spelling-ignore-words=
-# List of additional names supposed to be defined in builtins. Remember that
-# you should avoid to define new builtins when possible.
-additional-builtins=
+# A path to a file that contains private dictionary; one word per line.
+spelling-private-dict-file=
+# Tells whether to store unknown words to indicated private dictionary in
+# --spelling-private-dict-file option instead of raising a message.
+spelling-store-unknown-words=no
-[IMPORTS]
-# Deprecated modules which should not be used, separated by a comma
-deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
+[FORMAT]
-# Create a graph of every (i.e. internal and external) dependencies in the
-# given file (report RP0402 must not be disabled)
-import-graph=
+# Maximum number of characters on a single line.
+max-line-length=80
-# Create a graph of external dependencies in the given file (report RP0402 must
-# not be disabled)
-ext-import-graph=
+# Regexp for a line that is allowed to be longer than the limit.
+ignore-long-lines=^\s*(# )?<?https?://\S+>?$
-# Create a graph of internal dependencies in the given file (report RP0402 must
-# not be disabled)
-int-import-graph=
+# Allow the body of an if to be on the same line as the test if there is no
+# else.
+single-line-if-stmt=no
+
+# List of optional constructs for which whitespace checking is disabled
+no-space-check=trailing-comma,dict-separator
+
+# Maximum number of lines in a module
+max-module-lines=1000
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+indent-string=' '
+
+# Number of spaces of indent required inside a hanging or continued line.
+indent-after-paren=4
+
+# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
+expected-line-ending-format=
+
+
+[SIMILARITIES]
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+# Ignore imports when computing similarities.
+ignore-imports=no
+
+
+[CLASSES]
+
+# List of interface methods to ignore, separated by a comma. This is used for
+# instance to not check methods defines in Zope's Interface base class.
+ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,__new__,setUp
+
+# List of valid names for the first argument in a class method.
+valid-classmethod-first-arg=cls
+
+# List of valid names for the first argument in a metaclass class method.
+valid-metaclass-classmethod-first-arg=mcs
+
+# List of member names, which should be excluded from the protected access
+# warning.
+exclude-protected=_asdict,_fields,_replace,_source,_make
[DESIGN]
@@ -313,20 +346,22 @@ min-public-methods=2
max-public-methods=20
-[CLASSES]
+[IMPORTS]
-# List of interface methods to ignore, separated by a comma. This is used for
-# instance to not check methods defines in Zope's Interface base class.
-ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=regsub,TERMIOS,Bastion,rexec
-# List of method names used to declare (i.e. assign) instance attributes.
-defining-attr-methods=__init__,__new__,setUp
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report RP0402 must not be disabled)
+import-graph=
-# List of valid names for the first argument in a class method.
-valid-classmethod-first-arg=cls
+# Create a graph of external dependencies in the given file (report RP0402 must
+# not be disabled)
+ext-import-graph=
-# List of valid names for the first argument in a metaclass class method.
-valid-metaclass-classmethod-first-arg=mcs
+# Create a graph of internal dependencies in the given file (report RP0402 must
+# not be disabled)
+int-import-graph=
[EXCEPTIONS]
diff --git a/gui.py b/gui.py
index bf624b4..b3edd28 100644
--- a/gui.py
+++ b/gui.py
@@ -19,13 +19,19 @@ from __future__ import print_function
import os
import sys
import re
-import Queue
from threading import Thread
-from Tkinter import (Tk, Frame, Listbox, Entry, Label, Button, Scrollbar,
- Checkbutton, Radiobutton, IntVar, StringVar)
-from Tkinter import (TOP, LEFT, RIGHT, BOTTOM, END, X, Y, BOTH, SUNKEN, W,
- HORIZONTAL, DISABLED, NORMAL, W)
-from tkFileDialog import askopenfilename, askdirectory
+
+import six
+
+from six.moves.tkinter import (
+ Tk, Frame, Listbox, Entry, Label, Button, Scrollbar,
+ Checkbutton, Radiobutton, IntVar, StringVar,
+ TOP, LEFT, RIGHT, BOTTOM, END, X, Y, BOTH, SUNKEN, W,
+ HORIZONTAL, DISABLED, NORMAL, W,
+)
+from six.moves.tkinter_tkfiledialog import (
+ askopenfilename, askdirectory,
+)
import pylint.lint
from pylint.reporters.guireporter import GUIReporter
@@ -98,7 +104,7 @@ class BasicStream(object):
self.gui.tabs = self.outdict
try:
self.gui.rating.set(self.outdict['Global evaluation'][0])
- except:
+ except KeyError:
self.gui.rating.set('Error')
self.gui.refresh_results_window()
@@ -119,7 +125,7 @@ class LintGui(object):
#reporter
self.reporter = None
#message queue for output from reporter
- self.msg_queue = Queue.Queue()
+ self.msg_queue = six.moves.queue.Queue()
self.msgs = []
self.visible_msgs = []
self.filenames = []
@@ -322,7 +328,7 @@ class LintGui(object):
self.txt_module.focus_set()
- def select_recent_file(self, event):
+ def select_recent_file(self, event): # pylint: disable=unused-argument
"""adds the selected file in the history listbox to the Module box"""
if not self.showhistory.size():
return
@@ -353,7 +359,7 @@ class LintGui(object):
try:
for res in self.tabs[self.box.get()]:
self.results.insert(END, res)
- except:
+ except KeyError:
pass
def process_incoming(self):
@@ -376,7 +382,7 @@ class LintGui(object):
fg_color = COLORS.get(msg_str[:3], 'black')
self.lb_messages.itemconfigure(END, fg=fg_color)
- except Queue.Empty:
+ except six.moves.queue.Empty:
pass
return True
@@ -396,7 +402,7 @@ class LintGui(object):
"""quit the application"""
self.root.quit()
- def halt(self):
+ def halt(self): # pylint: disable=no-self-use
"""program halt placeholder"""
return
@@ -477,7 +483,7 @@ class LintGui(object):
self.root.configure(cursor='')
- def show_sourcefile(self, event=None):
+ def show_sourcefile(self, event=None): # pylint: disable=unused-argument
selected = self.lb_messages.curselection()
if not selected:
return
diff --git a/interfaces.py b/interfaces.py
index ea3b40f..067aaa6 100644
--- a/interfaces.py
+++ b/interfaces.py
@@ -10,7 +10,7 @@
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-"""Interfaces for PyLint objects"""
+"""Interfaces for Pylint objects"""
from collections import namedtuple
from logilab.common.interface import Interface
diff --git a/lint.py b/lint.py
index 9ef818f..1b46b90 100644
--- a/lint.py
+++ b/lint.py
@@ -37,12 +37,13 @@ from collections import defaultdict
from contextlib import contextmanager
from operator import attrgetter
from warnings import warn
-
+from itertools import chain
try:
import multiprocessing
except ImportError:
multiprocessing = None
+import six
from logilab.common.configuration import (
UnsupportedAction, OptionsManagerMixIn, format_option_value)
from logilab.common.optik_ext import check_csv
@@ -50,7 +51,6 @@ from logilab.common.interface import implements
from logilab.common.textutils import splitstrip, unquote
from logilab.common.ureports import Table, Text, Section
from logilab.common.__pkginfo__ import version as common_version
-
from astroid import MANAGER, AstroidBuildingException
from astroid.__pkginfo__ import version as astroid_version
from astroid.modutils import load_module_from_name, get_module_part
@@ -66,11 +66,25 @@ from pylint.checkers import (BaseTokenChecker,
initialize as checkers_initialize)
from pylint.reporters import initialize as reporters_initialize, CollectingReporter
from pylint import config
-
from pylint.__pkginfo__ import version
-import six
+def _get_new_args(message):
+ location = (
+ message.abspath,
+ message.path,
+ message.module,
+ message.obj,
+ message.line,
+ message.column,
+ )
+ return (
+ message.msg_id,
+ message.symbol,
+ location,
+ message.msg,
+ message.confidence,
+ )
def _get_python_path(filepath):
dirname = os.path.realpath(os.path.expanduser(filepath))
@@ -85,6 +99,20 @@ def _get_python_path(filepath):
return os.getcwd()
+def _merge_stats(stats):
+ merged = {}
+ for stat in stats:
+ for key, item in six.iteritems(stat):
+ if key not in merged:
+ merged[key] = item
+ else:
+ if isinstance(item, dict):
+ merged[key].update(item)
+ else:
+ merged[key] = merged[key] + item
+ return merged
+
+
# Python Linter class #########################################################
MSGS = {
@@ -158,16 +186,16 @@ MSGS = {
def _deprecated_option(shortname, opt_type):
- def _warn_deprecated(option, optname, *args):
+ def _warn_deprecated(option, optname, *args): # pylint: disable=unused-argument
sys.stderr.write('Warning: option %s is deprecated and ignored.\n' % (optname,))
return {'short': shortname, 'help': 'DEPRECATED', 'hide': True,
'type': opt_type, 'action': 'callback', 'callback': _warn_deprecated}
if multiprocessing is not None:
- class ChildLinter(multiprocessing.Process): # pylint: disable=no-member
+ class ChildLinter(multiprocessing.Process): # pylint: disable=no-member
def run(self):
- tasks_queue, results_queue, config = self._args # pylint: disable=no-member
+ tasks_queue, results_queue, config = self._args # pylint: disable=no-member
for file_or_module in iter(tasks_queue.get, 'STOP'):
result = self._run_linter(config, file_or_module[0])
@@ -197,7 +225,7 @@ if multiprocessing is not None:
linter_config = {}
filter_options = {"symbols", "include-ids"}
for opt_providers in six.itervalues(linter._all_options):
- for optname, optdict in opt_providers.options:
+ for optname, _ in opt_providers.options:
if optname not in filter_options:
linter_config[optname] = config[optname]
linter_config['jobs'] = 1 # Child does not parallelize any further.
@@ -208,9 +236,9 @@ if multiprocessing is not None:
# Run the checks.
linter.check(file_or_module)
- msgs = [m.get_init_args() for m in linter.reporter.messages]
+ msgs = [_get_new_args(m) for m in linter.reporter.messages]
return (file_or_module, linter.file_state.base_name, linter.current_name,
- msgs, linter.stats, linter.msg_status)
+ msgs, linter.stats, linter.msg_status)
class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn,
@@ -234,7 +262,6 @@ class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn,
priority = 0
level = 0
msgs = MSGS
- may_be_disabled = False
@staticmethod
def make_options():
@@ -305,7 +332,7 @@ class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn,
'group': 'Messages control',
'help' : 'Only show warnings with the listed confidence levels.'
' Leave empty to show all. Valid levels: %s' % (
- ', '.join(c.name for c in CONFIDENCE_LEVELS),)}),
+ ', '.join(c.name for c in CONFIDENCE_LEVELS),)}),
('enable',
{'type' : 'csv', 'metavar': '<msg ids>',
@@ -349,9 +376,9 @@ class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn,
{'type' : 'int', 'metavar': '<n-processes>',
'short': 'j',
'default': 1,
- 'help' : '''Use multiple processes to speed up PyLint.''',
- }), # jobs
- )
+ 'help' : '''Use multiple processes to speed up Pylint.''',
+ }), # jobs
+ )
option_groups = (
('Messages control', 'Options controling analysis messages'),
@@ -504,6 +531,11 @@ class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn,
self.msgs_store.register_messages(checker)
checker.load_defaults()
+ # Register the checker, but disable all of its messages.
+ # TODO(cpopa): we should have a better API for this.
+ if not getattr(checker, 'enabled', True):
+ self.disable(checker.name)
+
def disable_noerror_messages(self):
for msgcat, msgids in six.iteritems(self.msgs_store._msgs_by_category):
if msgcat == 'E':
@@ -599,14 +631,14 @@ class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn,
messages = set(msg for msg in checker.msgs
if msg[0] != 'F' and self.is_message_enabled(msg))
if (messages or
- any(self.report_is_enabled(r[0]) for r in checker.reports)):
+ any(self.report_is_enabled(r[0]) for r in checker.reports)):
neededcheckers.append(checker)
# Sort checkers by priority
neededcheckers = sorted(neededcheckers, key=attrgetter('priority'),
reverse=True)
return neededcheckers
- def should_analyze_file(self, modname, path): # pylint: disable=unused-argument
+ def should_analyze_file(self, modname, path): # pylint: disable=unused-argument, no-self-use
"""Returns whether or not a module should be checked.
This implementation returns True for all python source file, indicating
@@ -638,26 +670,32 @@ class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn,
if self.config.jobs == 1:
self._do_check(files_or_modules)
else:
- self._parallel_check(files_or_modules)
-
- def _parallel_check(self, files_or_modules):
- """Spawn a defined number of subprocesses."""
-
- manager = multiprocessing.Manager() # pylint: disable=no-member
- tasks_queue = manager.Queue() # pylint: disable=no-member
- results_queue = manager.Queue() # pylint: disable=no-member
+ # Hack that permits running pylint, on Windows, with -m switch
+ # and with --jobs, as in 'py -2 -m pylint .. --jobs'.
+ # For more details why this is needed,
+ # see Python issue http://bugs.python.org/issue10845.
+
+ mock_main = six.PY2 and __name__ != '__main__' # -m switch
+ if mock_main:
+ sys.modules['__main__'] = sys.modules[__name__]
+ try:
+ self._parallel_check(files_or_modules)
+ finally:
+ if mock_main:
+ sys.modules.pop('__main__')
+ def _parallel_task(self, files_or_modules):
# Prepare configuration for child linters.
config = {}
for opt_providers in six.itervalues(self._all_options):
for optname, optdict, val in opt_providers.options_and_values():
config[optname] = format_option_value(optdict, val)
- # Reset stats.
- self.open()
-
- # Spawn child linters.
childs = []
+ manager = multiprocessing.Manager() # pylint: disable=no-member
+ tasks_queue = manager.Queue() # pylint: disable=no-member
+ results_queue = manager.Queue() # pylint: disable=no-member
+
for _ in range(self.config.jobs):
cl = ChildLinter(args=(tasks_queue, results_queue, config))
cl.start() # pylint: disable=no-member
@@ -669,23 +707,41 @@ class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn,
# collect results from child linters
failed = False
- all_stats = []
- for i in range(len(files_or_modules)):
+ for _ in files_or_modules:
try:
- (
- file_or_module,
- self.file_state.base_name,
- module,
- messages,
- stats,
- msg_status
- ) = results_queue.get()
+ result = results_queue.get()
except Exception as ex:
print("internal error while receiving results from child linter",
file=sys.stderr)
print(ex, file=sys.stderr)
failed = True
break
+ yield result
+
+ # Stop child linters and wait for their completion.
+ for _ in range(self.config.jobs):
+ tasks_queue.put('STOP')
+ for cl in childs:
+ cl.join()
+
+ if failed:
+ print("Error occured, stopping the linter.", file=sys.stderr)
+ sys.exit(32)
+
+ def _parallel_check(self, files_or_modules):
+ # Reset stats.
+ self.open()
+
+ all_stats = []
+ for result in self._parallel_task(files_or_modules):
+ (
+ file_or_module,
+ self.file_state.base_name,
+ module,
+ messages,
+ stats,
+ msg_status
+ ) = result
if file_or_module == files_or_modules[-1]:
last_module = module
@@ -698,19 +754,7 @@ class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn,
all_stats.append(stats)
self.msg_status |= msg_status
- # Stop child linters and wait for their completion.
- for i in range(self.config.jobs):
- tasks_queue.put('STOP')
- for cl in childs:
- cl.join()
-
- if failed:
- print("Error occured, stopping the linter.", file=sys.stderr)
- sys.exit(32)
-
- all_stats.append(self.stats)
- all_stats = self._merge_stats(all_stats)
- self.stats = all_stats
+ self.stats = _merge_stats(chain(all_stats, [self.stats]))
self.current_name = last_module
# Insert stats data to local checkers.
@@ -718,19 +762,6 @@ class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn,
if checker is not self:
checker.stats = self.stats
- def _merge_stats(self, stats):
- merged = {}
- for stat in stats:
- for key, item in six.iteritems(stat):
- if key not in merged:
- merged[key] = item
- else:
- if isinstance(item, dict):
- merged[key].update(item)
- else:
- merged[key] = merged[key] + item
- return merged
-
def _do_check(self, files_or_modules):
walker = PyLintASTWalker(self)
checkers = self.prepare_checkers()
@@ -808,7 +839,7 @@ class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn,
self.add_message('syntax-error', line=ex.lineno, args=ex.msg)
except AstroidBuildingException as ex:
self.add_message('parse-error', args=ex)
- except Exception as ex:
+ except Exception as ex: # pylint: disable=broad-except
import traceback
traceback.print_exc()
self.add_message('astroid-error', args=(ex.__class__, ex))
@@ -893,8 +924,8 @@ class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn,
# get a global note for the code
evaluation = self.config.evaluation
try:
- note = eval(evaluation, {}, self.stats)
- except Exception as ex:
+ note = eval(evaluation, {}, self.stats) # pylint: disable=eval-used
+ except Exception as ex: # pylint: disable=broad-except
msg = 'An exception occurred while rating: %s' % ex
else:
stats['global_note'] = note
@@ -969,12 +1000,6 @@ def report_messages_by_module_stats(sect, stats, _):
# utilities ###################################################################
-# this may help to import modules using gettext
-# XXX syt, actually needed since we don't import code?
-
-from logilab.common.compat import builtins
-builtins._ = str
-
class ArgumentPreprocessingError(Exception):
"""Raised if an error occurs during argument preprocessing."""
@@ -1092,7 +1117,7 @@ group are mutually exclusive.'),
('list-conf-levels',
{'action' : 'callback',
- 'callback' : self.cb_list_confidence_levels,
+ 'callback' : cb_list_confidence_levels,
'group': 'Commands', 'level': 1,
'help' : "Generate pylint's messages."}),
@@ -1122,6 +1147,12 @@ group are mutually exclusive.'),
'disabled and for others, only the ERROR messages are '
'displayed, and no reports are done by default'''}),
+ ('py3k',
+ {'action' : 'callback', 'callback' : self.cb_python3_porting_mode,
+ 'help' : 'In Python 3 porting mode, all checkers will be '
+ 'disabled and only messages emitted by the porting '
+ 'checker will be displayed'}),
+
('profile',
{'type' : 'yn', 'metavar' : '<y_or_n>',
'default': False, 'hide': True,
@@ -1268,14 +1299,20 @@ group are mutually exclusive.'),
self.linter.msgs_store.list_messages()
sys.exit(0)
- def cb_list_confidence_levels(self, option, optname, value, parser):
- for level in CONFIDENCE_LEVELS:
- print('%-18s: %s' % level)
- sys.exit(0)
+ def cb_python3_porting_mode(self, *args, **kwargs):
+ """Activate only the python3 porting checker."""
+ self.linter.disable('all')
+ self.linter.enable('python3')
+
+
+def cb_list_confidence_levels(option, optname, value, parser):
+ for level in CONFIDENCE_LEVELS:
+ print('%-18s: %s' % level)
+ sys.exit(0)
def cb_init_hook(optname, value):
"""exec arbitrary code to set sys.path for instance"""
- exec(value)
+ exec(value) # pylint: disable=exec-used
if __name__ == '__main__':
diff --git a/man/pylint.1 b/man/pylint.1
index 57954c8..3a5c3e9 100644
--- a/man/pylint.1
+++ b/man/pylint.1
@@ -1,4 +1,4 @@
-.TH pylint 1 "2014-7-25" pylint
+.TH pylint 1 "2014-11-4" pylint
.SH NAME
.B pylint
\- python code static checker
@@ -45,23 +45,29 @@ Python code to execute, usually for sys.path manipulation such as pygtk.require(
.IP "--errors-only, -E"
In error mode, checkers without error messages are disabled and for others, only the ERROR messages are displayed, and no reports are done by default
.IP "--ignore=<file>[,<file>...]"
-Add files or directories to the blacklist. They should be base names, not paths. [current: .hg,test]
+Add files or directories to the blacklist. They should be base names, not paths. [current: CVS]
.IP "--persistent=<y_or_n>"
Pickle collected data for later comparisons. [current: yes]
.IP "--load-plugins=<modules>"
List of plugins (as comma separated values of python modules names) to load, usually to register additional checkers. [current: none]
+.IP "--jobs=<n-processes>, -j <n-processes>"
+Use multiple processes to speed up Pylint. [current: 1]
.SH COMMANDS
.IP "--help-msg=<msg-id>"
Display a help message for the given message id and exit. The value may be a comma separated list of message ids.
.IP "--list-msgs"
Generate pylint's messages.
+.IP "--list-conf-levels"
+Generate pylint's messages.
.IP "--full-documentation"
Generate pylint's full documentation.
.IP "--generate-rcfile"
Generate a sample configuration file according to the current configuration. You can put other options before this one to get them in the generated configuration.
.SH MESSAGES CONTROL
+.IP "--confidence=<levels>"
+Only show warnings with the listed confidence levels. Leave empty to show all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED [current: none]
.IP "--enable=<msg ids>, -e <msg ids>"
Enable the message, report, category or checker with the given id(s). You can either give multiple identifier separated by comma (,) or put this option multiple time. See also the "--disable" option for examples.
.IP "--disable=<msg ids>, -d <msg ids>"
@@ -81,20 +87,36 @@ Add a comment according to your evaluation note. This is used by the global eval
.IP "--msg-template=<template>"
Template used to display messages. This is a python new-style format string used to format the message information. See doc for all details
-.SH IMPORTS
-.IP "--deprecated-modules=<modules>"
-Deprecated modules which should not be used, separated by a comma [current: regsub,string,TERMIOS,Bastion,rexec]
-.IP "--import-graph=<file.dot>"
-Create a graph of every (i.e. internal and external) dependencies in the given file (report RP0402 must not be disabled) [current: none]
-.IP "--ext-import-graph=<file.dot>"
-Create a graph of external dependencies in the given file (report RP0402 must not be disabled) [current: none]
-.IP "--int-import-graph=<file.dot>"
-Create a graph of internal dependencies in the given file (report RP0402 must not be disabled) [current: none]
+.SH EXCEPTIONS
+.IP "--overgeneral-exceptions=<comma-separated class names>"
+Exceptions that will emit a warning when being caught. Defaults to "Exception" [current: Exception]
+
+.SH CLASSES
+.IP "--ignore-iface-methods=<method names>"
+List of interface methods to ignore, separated by a comma. This is used for instance to not check methods defines in Zope's Interface base class. [current: isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by]
+.IP "--defining-attr-methods=<method names>"
+List of method names used to declare (i.e. assign) instance attributes. [current: __init__,__new__,setUp]
+.IP "--valid-classmethod-first-arg=<argument names>"
+List of valid names for the first argument in a class method. [current: cls]
+.IP "--valid-metaclass-classmethod-first-arg=<argument names>"
+List of valid names for the first argument in a metaclass class method. [current: mcs]
+.IP "--exclude-protected=<protected access exclusions>"
+List of member names, which should be excluded from the protected access warning. [current: _asdict,_fields,_replace,_source,_make]
.SH LOGGING
.IP "--logging-modules=<comma separated list>"
Logging modules to check that the string format arguments are in logging function parameter format [current: logging]
+.SH VARIABLES
+.IP "--init-import=<y_or_n>"
+Tells whether we should check for unused import in __init__ files. [current: no]
+.IP "--dummy-variables-rgx=<regexp>"
+A regular expression matching the name of dummy variables (i.e. expectedly not used). [current: _$|dummy]
+.IP "--additional-builtins=<comma separated list>"
+List of additional names supposed to be defined in builtins. Remember that you should avoid to define new builtins when possible. [current: none]
+.IP "--callbacks=<callbacks>"
+List of strings which can identify a callback function by name. A callback name must start or end with one of those strings. [current: cb_,_cb]
+
.SH DESIGN
.IP "--max-args=<int>"
Maximum number of arguments for function / method [current: 5]
@@ -117,27 +139,11 @@ Minimum number of public methods for a class (see R0903). [current: 2]
.IP "--max-public-methods=<num>"
Maximum number of public methods for a class (see R0904). [current: 20]
-.SH FORMAT
-.IP "--max-line-length=<int>"
-Maximum number of characters on a single line. [current: 80]
-.IP "--ignore-long-lines=<regexp>"
-Regexp for a line that is allowed to be longer than the limit. [current: ^\s*(# )?<?https?://\S+>?$]
-.IP "--single-line-if-stmt=<y_or_n>"
-Allow the body of an if to be on the same line as the test if there is no else. [current: no]
-.IP "--no-space-check=NO_SPACE_CHECK"
-List of optional constructs for which whitespace checking is disabled [current: trailing-comma,dict-separator]
-.IP "--max-module-lines=<int>"
-Maximum number of lines in a module [current: 1000]
-.IP "--indent-string=<string>"
-String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 tab). [current: ' ']
-.IP "--indent-after-paren=<int>"
-Number of spaces of indent required inside a hanging or continued line. [current: 4]
-
.SH BASIC
.IP "--required-attributes=<attributes>"
Required attributes for module, separated by a comma [current: none]
.IP "--bad-functions=<builtin function names>"
-List of builtins function names that should not be used, separated by a comma [current: map,filter,apply,input]
+List of builtins function names that should not be used, separated by a comma [current: map,filter,input]
.IP "--good-names=<names>"
Good variable names which should always be accepted, separated by a comma [current: i,j,k,ex,Run,_]
.IP "--bad-names=<names>"
@@ -191,20 +197,6 @@ Regular expression which should only match function or class names that do not r
.IP "--docstring-min-length=<int>"
Minimum line length for functions/classes that require docstrings, shorter ones are exempt. [current: -1]
-.SH EXCEPTIONS
-.IP "--overgeneral-exceptions=<comma-separated class names>"
-Exceptions that will emit a warning when being caught. Defaults to "Exception" [current: Exception]
-
-.SH SIMILARITIES
-.IP "--min-similarity-lines=<int>"
-Minimum lines number of a similarity. [current: 4]
-.IP "--ignore-comments=<y or n>"
-Ignore comments when computing similarities. [current: yes]
-.IP "--ignore-docstrings=<y or n>"
-Ignore docstrings when computing similarities. [current: yes]
-.IP "--ignore-imports=<y or n>"
-Ignore imports when computing similarities. [current: no]
-
.SH MISCELLANEOUS
.IP "--notes=<comma separated values>"
List of note tags to take in consideration, separated by a comma. [current: FIXME,XXX,TODO]
@@ -221,23 +213,53 @@ When zope mode is activated, add a predefined set of Zope acquired attributes to
.IP "--generated-members=<members names>"
List of members which are set dynamically and missed by pylint inference system, and so shouldn't trigger E0201 when accessed. Python regular expressions are accepted. [current: REQUEST,acl_users,aq_parent]
-.SH VARIABLES
-.IP "--init-import=<y_or_n>"
-Tells whether we should check for unused import in __init__ files. [current: no]
-.IP "--dummy-variables-rgx=<regexp>"
-A regular expression matching the name of dummy variables (i.e. expectedly not used). [current: _|dummy]
-.IP "--additional-builtins=<comma separated list>"
-List of additional names supposed to be defined in builtins. Remember that you should avoid to define new builtins when possible. [current: none]
+.SH SPELLING
+.IP "--spelling-dict=<dict name>"
+Spelling dictionary name. Available dictionaries: none. To make it working install python-enchant package. [current: none]
+.IP "--spelling-ignore-words=<comma separated words>"
+List of comma separated words that should not be checked. [current: none]
+.IP "--spelling-private-dict-file=<path to file>"
+A path to a file that contains private dictionary; one word per line. [current: none]
+.IP "--spelling-store-unknown-words=<y_or_n>"
+Tells whether to store unknown words to indicated private dictionary in --spelling-private-dict-file option instead of raising a message. [current: no]
-.SH CLASSES
-.IP "--ignore-iface-methods=<method names>"
-List of interface methods to ignore, separated by a comma. This is used for instance to not check methods defines in Zope's Interface base class. [current: isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by]
-.IP "--defining-attr-methods=<method names>"
-List of method names used to declare (i.e. assign) instance attributes. [current: __init__,__new__,setUp]
-.IP "--valid-classmethod-first-arg=<argument names>"
-List of valid names for the first argument in a class method. [current: cls]
-.IP "--valid-metaclass-classmethod-first-arg=<argument names>"
-List of valid names for the first argument in a metaclass class method. [current: mcs]
+.SH FORMAT
+.IP "--max-line-length=<int>"
+Maximum number of characters on a single line. [current: 80]
+.IP "--ignore-long-lines=<regexp>"
+Regexp for a line that is allowed to be longer than the limit. [current: ^\s*(# )?<?https?://\S+>?$]
+.IP "--single-line-if-stmt=<y_or_n>"
+Allow the body of an if to be on the same line as the test if there is no else. [current: no]
+.IP "--no-space-check=NO_SPACE_CHECK"
+List of optional constructs for which whitespace checking is disabled [current: trailing-comma,dict-separator]
+.IP "--max-module-lines=<int>"
+Maximum number of lines in a module [current: 1000]
+.IP "--indent-string=<string>"
+String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 tab). [current: ' ']
+.IP "--indent-after-paren=<int>"
+Number of spaces of indent required inside a hanging or continued line. [current: 4]
+.IP "--expected-line-ending-format=<empty or LF or CRLF>"
+Expected format of line ending, e.g. empty (any line ending), LF or CRLF. [current: none]
+
+.SH IMPORTS
+.IP "--deprecated-modules=<modules>"
+Deprecated modules which should not be used, separated by a comma [current: regsub,TERMIOS,Bastion,rexec]
+.IP "--import-graph=<file.dot>"
+Create a graph of every (i.e. internal and external) dependencies in the given file (report RP0402 must not be disabled) [current: none]
+.IP "--ext-import-graph=<file.dot>"
+Create a graph of external dependencies in the given file (report RP0402 must not be disabled) [current: none]
+.IP "--int-import-graph=<file.dot>"
+Create a graph of internal dependencies in the given file (report RP0402 must not be disabled) [current: none]
+
+.SH SIMILARITIES
+.IP "--min-similarity-lines=<int>"
+Minimum lines number of a similarity. [current: 4]
+.IP "--ignore-comments=<y or n>"
+Ignore comments when computing similarities. [current: yes]
+.IP "--ignore-docstrings=<y or n>"
+Ignore docstrings when computing similarities. [current: yes]
+.IP "--ignore-imports=<y or n>"
+Ignore imports when computing similarities. [current: no]
.SH ENVIRONMENT VARIABLES
diff --git a/pyreverse/diadefslib.py b/pyreverse/diadefslib.py
index 0b42065..e0dc8cf 100644
--- a/pyreverse/diadefslib.py
+++ b/pyreverse/diadefslib.py
@@ -145,7 +145,7 @@ class DefaultDiadefGenerator(LocalsVisitor, DiaDefGenerator):
self.pkgdiagram = None
self.classdiagram = ClassDiagram('classes %s' % node.name, mode)
- def leave_project(self, node):
+ def leave_project(self, node): # pylint: disable=unused-argument
"""leave the astroid.Project node
return the generated diagram definition
diff --git a/pyreverse/utils.py b/pyreverse/utils.py
index 58559d7..5d6d133 100644
--- a/pyreverse/utils.py
+++ b/pyreverse/utils.py
@@ -128,5 +128,5 @@ class FilterMixIn(object):
"""return true if the node should be treated
"""
visibility = get_visibility(getattr(node, 'name', node))
- return not (self.__mode & VIS_MOD[visibility])
+ return not self.__mode & VIS_MOD[visibility]
diff --git a/reporters/__init__.py b/reporters/__init__.py
index 5ebb6aa..ea3281f 100644
--- a/reporters/__init__.py
+++ b/reporters/__init__.py
@@ -25,7 +25,7 @@ CMPS = ['=', '-', '+']
# py3k has no more cmp builtin
if sys.version_info >= (3, 0):
- def cmp(a, b):
+ def cmp(a, b): # pylint: disable=redefined-builtin
return (a > b) - (a < b)
def diff_string(old, new):
diff --git a/reporters/guireporter.py b/reporters/guireporter.py
index f908f76..4ad4ebb 100644
--- a/reporters/guireporter.py
+++ b/reporters/guireporter.py
@@ -4,7 +4,6 @@ import sys
from pylint.interfaces import IReporter
from pylint.reporters import BaseReporter
-from pylint import utils
from logilab.common.ureports import TextWriter
diff --git a/reporters/html.py b/reporters/html.py
index 3e5a1a7..1c6c260 100644
--- a/reporters/html.py
+++ b/reporters/html.py
@@ -18,7 +18,6 @@ from cgi import escape
from logilab.common.ureports import HTMLWriter, Section, Table
-from pylint import utils
from pylint.interfaces import IReporter
from pylint.reporters import BaseReporter
diff --git a/reporters/text.py b/reporters/text.py
index acb22b5..bc86313 100644
--- a/reporters/text.py
+++ b/reporters/text.py
@@ -133,7 +133,7 @@ class ColorizedTextReporter(TextReporter):
msg = msg._replace(
**{attr: colorize_ansi(getattr(msg, attr), color, style)
- for attr in ('msg', 'symbol', 'category', 'C')})
+ for attr in ('msg', 'symbol', 'category', 'C')})
self.write_message(msg)
diff --git a/test/functional/abstract_class_instantiated_py2.py b/test/functional/abstract_class_instantiated_py2.py
new file mode 100644
index 0000000..670abd4
--- /dev/null
+++ b/test/functional/abstract_class_instantiated_py2.py
@@ -0,0 +1,68 @@
+"""Check that instantiating a class with
+`abc.ABCMeta` as metaclass fails if it defines
+abstract methods.
+"""
+
+# pylint: disable=too-few-public-methods, missing-docstring, abstract-class-not-used
+# pylint: disable=no-absolute-import, metaclass-assignment, abstract-class-little-used
+# pylint: disable=abstract-method
+
+__revision__ = 0
+
+import abc
+from abc import ABCMeta
+
+class GoodClass(object):
+ __metaclass__ = abc.ABCMeta
+
+class SecondGoodClass(object):
+ __metaclass__ = abc.ABCMeta
+
+ def test(self):
+ """ do nothing. """
+
+class ThirdGoodClass(object):
+ __metaclass__ = abc.ABCMeta
+
+ def test(self):
+ raise NotImplementedError()
+
+class FourthGoodClass(object):
+ __metaclass__ = ABCMeta
+
+class BadClass(object):
+ __metaclass__ = abc.ABCMeta
+
+ @abc.abstractmethod
+ def test(self):
+ """ do nothing. """
+
+class SecondBadClass(object):
+ __metaclass__ = abc.ABCMeta
+
+ @property
+ @abc.abstractmethod
+ def test(self):
+ """ do nothing. """
+
+class ThirdBadClass(object):
+ __metaclass__ = ABCMeta
+
+ @abc.abstractmethod
+ def test(self):
+ pass
+
+class FourthBadClass(ThirdBadClass):
+ pass
+
+
+def main():
+ """ do nothing """
+ GoodClass()
+ SecondGoodClass()
+ ThirdGoodClass()
+ FourthGoodClass()
+ BadClass() # [abstract-class-instantiated]
+ SecondBadClass() # [abstract-class-instantiated]
+ ThirdBadClass() # [abstract-class-instantiated]
+ FourthBadClass() # [abstract-class-instantiated]
diff --git a/test/functional/abstract_class_instantiated_py2.rc b/test/functional/abstract_class_instantiated_py2.rc
new file mode 100644
index 0000000..b11e16d
--- /dev/null
+++ b/test/functional/abstract_class_instantiated_py2.rc
@@ -0,0 +1,2 @@
+[testoptions]
+max_pyver=3.0 \ No newline at end of file
diff --git a/test/functional/abstract_class_instantiated_py2.txt b/test/functional/abstract_class_instantiated_py2.txt
new file mode 100644
index 0000000..034795a
--- /dev/null
+++ b/test/functional/abstract_class_instantiated_py2.txt
@@ -0,0 +1,4 @@
+abstract-class-instantiated:65:main:Abstract class with abstract methods instantiated
+abstract-class-instantiated:66:main:Abstract class with abstract methods instantiated
+abstract-class-instantiated:67:main:Abstract class with abstract methods instantiated
+abstract-class-instantiated:68:main:Abstract class with abstract methods instantiated
diff --git a/test/input/func_abstract_class_instantiated_py30.py b/test/functional/abstract_class_instantiated_py3.py
index 7e7c5ab..146666e 100644
--- a/test/input/func_abstract_class_instantiated_py30.py
+++ b/test/functional/abstract_class_instantiated_py3.py
@@ -3,7 +3,9 @@
abstract methods.
"""
-# pylint: disable=too-few-public-methods, missing-docstring, abstract-class-not-used
+# pylint: disable=too-few-public-methods, missing-docstring
+# pylint: disable=abstract-class-not-used, abstract-class-little-used
+# pylint: disable=abstract-method
__revision__ = 0
@@ -15,7 +17,6 @@ class GoodClass(object, metaclass=abc.ABCMeta):
class SecondGoodClass(object, metaclass=abc.ABCMeta):
def test(self):
""" do nothing. """
- pass
class ThirdGoodClass(object, metaclass=abc.ABCMeta):
""" This should not raise the warning. """
@@ -26,7 +27,6 @@ class BadClass(object, metaclass=abc.ABCMeta):
@abc.abstractmethod
def test(self):
""" do nothing. """
- pass
class SecondBadClass(object, metaclass=abc.ABCMeta):
@property
@@ -34,10 +34,15 @@ class SecondBadClass(object, metaclass=abc.ABCMeta):
def test(self):
""" do nothing. """
+class ThirdBadClass(SecondBadClass):
+ pass
+
+
def main():
""" do nothing """
GoodClass()
SecondGoodClass()
ThirdGoodClass()
- BadClass()
- SecondBadClass()
+ BadClass() # [abstract-class-instantiated]
+ SecondBadClass() # [abstract-class-instantiated]
+ ThirdBadClass() # [abstract-class-instantiated]
diff --git a/test/functional/abstract_class_instantiated_py3.rc b/test/functional/abstract_class_instantiated_py3.rc
new file mode 100644
index 0000000..a2ab06c
--- /dev/null
+++ b/test/functional/abstract_class_instantiated_py3.rc
@@ -0,0 +1,2 @@
+[testoptions]
+min_pyver=3.0 \ No newline at end of file
diff --git a/test/functional/abstract_class_instantiated_py3.txt b/test/functional/abstract_class_instantiated_py3.txt
new file mode 100644
index 0000000..3c4f0c2
--- /dev/null
+++ b/test/functional/abstract_class_instantiated_py3.txt
@@ -0,0 +1,3 @@
+abstract-class-instantiated:46:main:Abstract class with abstract methods instantiated
+abstract-class-instantiated:47:main:Abstract class with abstract methods instantiated
+abstract-class-instantiated:48:main:Abstract class with abstract methods instantiated \ No newline at end of file
diff --git a/test/functional/abstract_class_instantiated_py34.py b/test/functional/abstract_class_instantiated_py34.py
new file mode 100644
index 0000000..8f710a2
--- /dev/null
+++ b/test/functional/abstract_class_instantiated_py34.py
@@ -0,0 +1,19 @@
+"""
+Check that instantiating a class with `abc.ABCM` as ancestor fails if it
+defines abstract methods.
+"""
+
+# pylint: disable=too-few-public-methods, missing-docstring, abstract-class-not-used, no-init
+
+__revision__ = 0
+
+import abc
+
+class BadClass(abc.ABC):
+ @abc.abstractmethod
+ def test(self):
+ pass
+
+def main():
+ """ do nothing """
+ BadClass() # [abstract-class-instantiated]
diff --git a/test/functional/abstract_class_instantiated_py34.rc b/test/functional/abstract_class_instantiated_py34.rc
new file mode 100644
index 0000000..1fb7b87
--- /dev/null
+++ b/test/functional/abstract_class_instantiated_py34.rc
@@ -0,0 +1,2 @@
+[testoptions]
+min_pyver=3.4 \ No newline at end of file
diff --git a/test/functional/abstract_class_instantiated_py34.txt b/test/functional/abstract_class_instantiated_py34.txt
new file mode 100644
index 0000000..793df2e
--- /dev/null
+++ b/test/functional/abstract_class_instantiated_py34.txt
@@ -0,0 +1 @@
+abstract-class-instantiated:19:main:Abstract class with abstract methods instantiated
diff --git a/test/functional/defined_and_used_on_same_line.py b/test/functional/defined_and_used_on_same_line.py
index f317ba3..fc19dc7 100644
--- a/test/functional/defined_and_used_on_same_line.py
+++ b/test/functional/defined_and_used_on_same_line.py
@@ -1,5 +1,5 @@
"""Check for definitions and usage happening on the same line."""
-#pylint: disable=missing-docstring,multiple-statements,print-statement,no-absolute-import
+#pylint: disable=missing-docstring,multiple-statements,print-statement,no-absolute-import,parameter-unpacking
print [index
for index in range(10)]
diff --git a/test/input/func_indexing_exceptions_py_30.py b/test/functional/indexing_exception.py
index 0147cfc..7ea50d5 100644
--- a/test/input/func_indexing_exceptions_py_30.py
+++ b/test/functional/indexing_exception.py
@@ -9,7 +9,7 @@ from unknown import ExtensionException
class SubException(IndexError):
""" empty """
-_ = IndexError("test")[0]
-_ = ZeroDivisionError("error")[0]
+_ = IndexError("test")[0] # [indexing-exception]
+_ = ZeroDivisionError("error")[0] # [indexing-exception]
_ = ExtensionException("error")[0]
-_ = SubException("error")[1]
+_ = SubException("error")[1] # [indexing-exception]
diff --git a/test/functional/indexing_exception.rc b/test/functional/indexing_exception.rc
new file mode 100644
index 0000000..9540bc9
--- /dev/null
+++ b/test/functional/indexing_exception.rc
@@ -0,0 +1,5 @@
+[testoptions]
+max_pyver=3.0
+
+[Messages Control]
+enable=python3 \ No newline at end of file
diff --git a/test/functional/indexing_exception.txt b/test/functional/indexing_exception.txt
new file mode 100644
index 0000000..9f30678
--- /dev/null
+++ b/test/functional/indexing_exception.txt
@@ -0,0 +1,3 @@
+indexing-exception:12::Indexing exceptions will not work on Python 3
+indexing-exception:13::Indexing exceptions will not work on Python 3
+indexing-exception:15::Indexing exceptions will not work on Python 3 \ No newline at end of file
diff --git a/test/functional/invalid_exceptions_raised.py b/test/functional/invalid_exceptions_raised.py
index fe6223d..8728581 100644
--- a/test/functional/invalid_exceptions_raised.py
+++ b/test/functional/invalid_exceptions_raised.py
@@ -28,7 +28,7 @@ def bad_case1():
def bad_case2():
"""raise"""
- # +2:<3.0:[old-raise-syntax,nonstandard-exception]
+ # +2:<3.0:[nonstandard-exception]
# +1:>=3.0:[raising-non-exception]
raise OldStyleClass, 'hop'
@@ -38,7 +38,6 @@ def bad_case3():
def bad_case4():
"""raise"""
- # +1:<3.0:[old-raise-syntax]
raise NotImplemented, 'hop' # [notimplemented-raised]
def bad_case5():
diff --git a/test/functional/invalid_exceptions_raised.txt b/test/functional/invalid_exceptions_raised.txt
index 54ea0f4..b12f77f 100644
--- a/test/functional/invalid_exceptions_raised.txt
+++ b/test/functional/invalid_exceptions_raised.txt
@@ -2,10 +2,8 @@ nonstandard-exception:23:bad_case0:"Exception doesn't inherit from standard ""Ex
raising-non-exception:23:bad_case0:Raising a new style class which doesn't inherit from BaseException
raising-non-exception:27:bad_case1:Raising a new style class which doesn't inherit from BaseException
nonstandard-exception:33:bad_case2:"Exception doesn't inherit from standard ""Exception"" class":INFERENCE
-old-raise-syntax:33:bad_case2:Use raise ErrorClass(args) instead of raise ErrorClass, args.
raising-non-exception:33:bad_case2:Raising a new style class which doesn't inherit from BaseException
raising-non-exception:37:bad_case3:Raising a new style class which doesn't inherit from BaseException
-notimplemented-raised:42:bad_case4:NotImplemented raised - should raise NotImplementedError
-old-raise-syntax:42:bad_case4:Use raise ErrorClass(args) instead of raise ErrorClass, args.
-raising-bad-type:46:bad_case5:Raising int while only classes, instances or string are allowed
-raising-bad-type:50:base_case6:Raising NoneType while only classes, instances or string are allowed
+notimplemented-raised:41:bad_case4:NotImplemented raised - should raise NotImplementedError
+raising-bad-type:45:bad_case5:Raising int while only classes or instances are allowed
+raising-bad-type:49:base_case6:Raising NoneType while only classes or instances are allowed
diff --git a/test/functional/long_lines_with_utf8.txt b/test/functional/long_lines_with_utf8.txt
index 14f6529..42a7976 100644
--- a/test/functional/long_lines_with_utf8.txt
+++ b/test/functional/long_lines_with_utf8.txt
@@ -1 +1 @@
-line-too-long:7::Line too long (108/80)
+line-too-long:7::Line too long (108/100)
diff --git a/test/functional/string_formatting.py b/test/functional/string_formatting.py
index f57cf49..94554c6 100644
--- a/test/functional/string_formatting.py
+++ b/test/functional/string_formatting.py
@@ -152,3 +152,22 @@ def issue351():
fmt('arg1') # [too-few-format-args]
fmt('arg1', 'arg2')
fmt('arg1', 'arg2', 'arg3') # [too-many-format-args]
+
+def issue373():
+ """
+ Ignore any object coming from an argument.
+ """
+ class SomeClass(object):
+ """ empty docstring. """
+ def __init__(self, opts=None):
+ self.opts = opts
+
+ def dunc(self, arg):
+ """Don't try to analyze this."""
+ return "A{0}{1}".format(arg, self.opts)
+
+ def func(self):
+ """Don't try to analyze the following string."""
+ return 'AAA{0[iface]}BBB{0[port]}'.format(self.opts)
+
+ return SomeClass
diff --git a/test/functional/unpacked_exceptions.rc b/test/functional/unpacked_exceptions.rc
index a650233..9540bc9 100644
--- a/test/functional/unpacked_exceptions.rc
+++ b/test/functional/unpacked_exceptions.rc
@@ -1,2 +1,5 @@
[testoptions]
max_pyver=3.0
+
+[Messages Control]
+enable=python3 \ No newline at end of file
diff --git a/test/input/func_abstract_class_instantiated_py34.py b/test/input/func_abstract_class_instantiated_py34.py
deleted file mode 100644
index 3c869c6..0000000
--- a/test/input/func_abstract_class_instantiated_py34.py
+++ /dev/null
@@ -1,55 +0,0 @@
-"""Check that instantiating a class with
-`abc.ABCMeta` as metaclass fails if it defines
-abstract methods.
-"""
-
-# pylint: disable=too-few-public-methods, missing-docstring, abstract-class-not-used, no-init
-
-__revision__ = 0
-
-import abc
-
-class GoodClass(object, metaclass=abc.ABCMeta):
- pass
-
-class SecondGoodClass(object, metaclass=abc.ABCMeta):
- def test(self):
- """ do nothing. """
- pass
-
-class ThirdGoodClass(object, metaclass=abc.ABCMeta):
- """ This should not raise the warning. """
- def test(self):
- raise NotImplementedError()
-
-class FourthGoodClass(abc.ABC):
- """ Neither this. """
- def test(self):
- pass
-
-class BadClass(object, metaclass=abc.ABCMeta):
- @abc.abstractmethod
- def test(self):
- """ do nothing. """
- pass
-
-class SecondBadClass(object, metaclass=abc.ABCMeta):
- @property
- @abc.abstractmethod
- def test(self):
- """ do nothing. """
-
-class ThirdBadClass(abc.ABC):
- @abc.abstractmethod
- def test(self):
- pass
-
-def main():
- """ do nothing """
- GoodClass()
- SecondGoodClass()
- ThirdGoodClass()
- FourthGoodClass()
- BadClass()
- SecondBadClass()
- ThirdBadClass()
diff --git a/test/input/func_backtick_deprecated_py_30.py b/test/input/func_backtick_deprecated_py_30.py
deleted file mode 100644
index db3203b..0000000
--- a/test/input/func_backtick_deprecated_py_30.py
+++ /dev/null
@@ -1,4 +0,0 @@
-"""W0333: flag backtick as deprecated"""
-from __future__ import print_function
-__revision__ = None
-print(`1`)
diff --git a/test/input/func_bad_assigment_to_exception_var.py b/test/input/func_bad_assigment_to_exception_var.py
index 67200f2..b147446 100644
--- a/test/input/func_bad_assigment_to_exception_var.py
+++ b/test/input/func_bad_assigment_to_exception_var.py
@@ -12,7 +12,7 @@ try:
except Exception, ex:
print ex
_, _, tb = sys.exc_info()
- raise e2
+
def func():
diff --git a/test/input/func_toolonglines.py b/test/input/func_toolonglines.py
index d02e4bb..272395f 100644
--- a/test/input/func_toolonglines.py
+++ b/test/input/func_toolonglines.py
@@ -1,5 +1,5 @@
-##########################################################################################
-""" that one is too long tooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"""
+#####################################################################################################
+""" that one is too long tooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo loooooong"""
__revision__ = ''
@@ -14,7 +14,7 @@ THIS_IS_A_VERY_LONG_VARIABLE_NAME = 'Существительное Частиц
# line longer than 80 characters is a trailing pylint disable.
var = 'This line has a disable pragma and whitespace trailing beyond 80 chars. ' # pylint:disable=invalid-name
-badname = 'This line is already longer than 80 characters even without the pragma.' # pylint:disable=invalid-name
+badname = 'This line is already longer than 100 characters even without the pragma. Trust me. Please.' # pylint:disable=invalid-name
# http://example.com/this/is/a/very/long/url?but=splitting&urls=is&a=pain&so=they&can=be&long
@@ -22,6 +22,6 @@ badname = 'This line is already longer than 80 characters even without the pragm
def function():
"""This is a docstring.
- That contains a very, very long line that exceeds the 80 character limit by a good margin.
+ That contains a very, very long line that exceeds the 100 characters limit by a good margin. So good?
"""
pass
diff --git a/test/input/func_w0331_py_30.py b/test/input/func_w0331_py_30.py
deleted file mode 100644
index 6ee6006..0000000
--- a/test/input/func_w0331_py_30.py
+++ /dev/null
@@ -1,7 +0,0 @@
-"""check use of <>
-"""
-
-__revision__ = 1
-
-if __revision__ <> 2:
- __revision__ = 2
diff --git a/test/input/func_w0332_py_30.py b/test/input/func_w0332_py_30.py
index d6fed47..6a38e8c 100644
--- a/test/input/func_w0332_py_30.py
+++ b/test/input/func_w0332_py_30.py
@@ -1,4 +1,4 @@
"""check use of l as long int marker
"""
-
+# pylint: disable=long-suffix
__revision__ = 1l
diff --git a/test/input/func_w0701_py_30.py b/test/input/func_w0701_py_30.py
deleted file mode 100644
index 9c1b727..0000000
--- a/test/input/func_w0701_py_30.py
+++ /dev/null
@@ -1,12 +0,0 @@
-"""test string exception
-"""
-
-__revision__ = ''
-
-def function1():
- """hehehe"""
- raise "String Exception"
-
-def function2():
- """hehehe"""
- raise 'exception', 'message'
diff --git a/test/messages/func_abstract_class_instantiated_py30.txt b/test/messages/func_abstract_class_instantiated_py30.txt
deleted file mode 100644
index 28dc11e..0000000
--- a/test/messages/func_abstract_class_instantiated_py30.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-E: 42:main: Abstract class with abstract methods instantiated
-E: 43:main: Abstract class with abstract methods instantiated \ No newline at end of file
diff --git a/test/messages/func_abstract_class_instantiated_py34.txt b/test/messages/func_abstract_class_instantiated_py34.txt
deleted file mode 100644
index 310f8ea..0000000
--- a/test/messages/func_abstract_class_instantiated_py34.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-E: 53:main: Abstract class with abstract methods instantiated
-E: 54:main: Abstract class with abstract methods instantiated
-E: 55:main: Abstract class with abstract methods instantiated \ No newline at end of file
diff --git a/test/messages/func_backtick_deprecated_py_30.txt b/test/messages/func_backtick_deprecated_py_30.txt
deleted file mode 100644
index c78baa1..0000000
--- a/test/messages/func_backtick_deprecated_py_30.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-W: 4: Use of the `` operator
-
diff --git a/test/messages/func_bad_assigment_to_exception_var.txt b/test/messages/func_bad_assigment_to_exception_var.txt
index 5c4f11a..119fbe2 100644
--- a/test/messages/func_bad_assigment_to_exception_var.txt
+++ b/test/messages/func_bad_assigment_to_exception_var.txt
@@ -1,5 +1,5 @@
-E: 11: Raising int while only classes, instances or string are allowed
-E: 20:func: Raising NoneType while only classes, instances or string are allowed
-E: 30: Raising NoneType while only classes, instances or string are allowed
-W: 15: Raising a string exception
+E: 11: Raising int while only classes or instances are allowed
+E: 20:func: Raising NoneType while only classes or instances are allowed
+E: 30: Raising NoneType while only classes or instances are allowed
+W: 12: Catching too general exception Exception
diff --git a/test/messages/func_disable_linebased.txt b/test/messages/func_disable_linebased.txt
index e4e74a7..e417c5d 100644
--- a/test/messages/func_disable_linebased.txt
+++ b/test/messages/func_disable_linebased.txt
@@ -1,2 +1,2 @@
-C: 1: Line too long (127/80)
-C: 14: Line too long (113/80)
+C: 1: Line too long (127/100)
+C: 14: Line too long (113/100)
diff --git a/test/messages/func_disable_linebased_py30.txt b/test/messages/func_disable_linebased_py30.txt
index 7054dfc..06e391d 100644
--- a/test/messages/func_disable_linebased_py30.txt
+++ b/test/messages/func_disable_linebased_py30.txt
@@ -1,2 +1,2 @@
-C: 1: Line too long (127/80)
-C: 14: Line too long (114/80)
+C: 1: Line too long (127/100)
+C: 14: Line too long (114/100)
diff --git a/test/messages/func_indexing_exceptions_py_30.txt b/test/messages/func_indexing_exceptions_py_30.txt
deleted file mode 100644
index 9c86ba5..0000000
--- a/test/messages/func_indexing_exceptions_py_30.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-W: 12: Indexing exceptions will not work on Python 3
-W: 13: Indexing exceptions will not work on Python 3
-W: 15: Indexing exceptions will not work on Python 3 \ No newline at end of file
diff --git a/test/messages/func_toolonglines.txt b/test/messages/func_toolonglines.txt
index a984023..ddf05af 100644
--- a/test/messages/func_toolonglines.txt
+++ b/test/messages/func_toolonglines.txt
@@ -1,6 +1,6 @@
-C: 1: Line too long (90/80)
-C: 2: Line too long (94/80)
-C: 11: Line too long (101/80)
-C: 17: Line too long (83/80)
-C: 25: Line too long (94/80)
+C: 1: Line too long (101/100)
+C: 2: Line too long (104/100)
+C: 11: Line too long (101/100)
+C: 17: Line too long (102/100)
+C: 25: Line too long (105/100)
W: 11: Cannot decode using encoding "ascii", unexpected byte at position 37
diff --git a/test/messages/func_toolonglines_py30.txt b/test/messages/func_toolonglines_py30.txt
index 7678258..cd594f5 100644
--- a/test/messages/func_toolonglines_py30.txt
+++ b/test/messages/func_toolonglines_py30.txt
@@ -1,4 +1,4 @@
-C: 1: Line too long (90/80)
-C: 2: Line too long (94/80)
-C: 17: Line too long (83/80)
-C: 25: Line too long (94/80)
+C: 1: Line too long (101/100)
+C: 2: Line too long (104/100)
+C: 17: Line too long (102/100)
+C: 25: Line too long (105/100)
diff --git a/test/messages/func_w0152_py29.txt b/test/messages/func_w0152_py29.txt
index a6cf2f3..484d5bf 100644
--- a/test/messages/func_w0152_py29.txt
+++ b/test/messages/func_w0152_py29.txt
@@ -1,3 +1,2 @@
W: 5: Used * or ** magic
-W: 5: reduce built-in referenced
W: 14: Used * or ** magic
diff --git a/test/messages/func_w0331_py_30.txt b/test/messages/func_w0331_py_30.txt
deleted file mode 100644
index 8134a32..0000000
--- a/test/messages/func_w0331_py_30.txt
+++ /dev/null
@@ -1 +0,0 @@
-W: 6: Use of the <> operator
diff --git a/test/messages/func_w0701_py_30.txt b/test/messages/func_w0701_py_30.txt
deleted file mode 100644
index d643517..0000000
--- a/test/messages/func_w0701_py_30.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-W: 8:function1: Raising a string exception
-W: 12:function2: Raising a string exception
-W: 12:function2: Use raise ErrorClass(args) instead of raise ErrorClass, args.
diff --git a/test/test_functional.py b/test/test_functional.py
index ead8f58..f9e33fa 100644
--- a/test/test_functional.py
+++ b/test/test_functional.py
@@ -2,7 +2,6 @@
from __future__ import unicode_literals
import csv
import collections
-import ConfigParser
import io
import operator
import os
@@ -11,6 +10,9 @@ import sys
import platform
import unittest
+import six
+from six.moves import configparser
+
from pylint import checkers
from pylint import interfaces
from pylint import lint
@@ -113,7 +115,7 @@ class TestFile(object):
self._parse_options()
def _parse_options(self):
- cp = ConfigParser.ConfigParser()
+ cp = configparser.ConfigParser()
cp.add_section('testoptions')
try:
cp.read(self.option_file)
@@ -204,7 +206,7 @@ def multiset_difference(left_op, right_op):
missing = left_op.copy()
missing.subtract(right_op)
unexpected = {}
- for key, value in missing.items():
+ for key, value in list(six.iteritems(missing)):
if value <= 0:
missing.pop(key)
if value < 0:
diff --git a/test/test_import_graph.py b/test/test_import_graph.py
index 2948dd1..2b41536 100644
--- a/test/test_import_graph.py
+++ b/test/test_import_graph.py
@@ -1,9 +1,10 @@
import sys
import os
from os.path import exists
-from cStringIO import StringIO
import unittest
+import six
+
from pylint.checkers import initialize, imports
from pylint.lint import PyLinter
diff --git a/test/test_self.py b/test/test_self.py
index 6ef3c49..aa23a2f 100644
--- a/test/test_self.py
+++ b/test/test_self.py
@@ -14,10 +14,11 @@
import sys
import os
from os.path import join, dirname, abspath
-from cStringIO import StringIO
import tempfile
import unittest
+import six
+
from pylint.lint import Run
from pylint.reporters import BaseReporter
from pylint.reporters.text import *
@@ -62,7 +63,7 @@ class RunTC(unittest.TestCase):
def _runtest(self, args, reporter=None, out=None, code=28):
if out is None:
- out = StringIO()
+ out = six.StringIO()
try:
sys.stderr = sys.stdout = out
try:
@@ -86,16 +87,16 @@ class RunTC(unittest.TestCase):
def test_pkginfo(self):
"""Make pylint check itself."""
- self._runtest(['pylint.__pkginfo__'], reporter=TextReporter(StringIO()),
+ self._runtest(['pylint.__pkginfo__'], reporter=TextReporter(six.StringIO()),
code=0)
def test_all(self):
"""Make pylint check itself."""
reporters = [
- TextReporter(StringIO()),
- HTMLReporter(StringIO()),
- ColorizedTextReporter(StringIO()),
- JSONReporter(StringIO()),
+ TextReporter(six.StringIO()),
+ HTMLReporter(six.StringIO()),
+ ColorizedTextReporter(six.StringIO()),
+ JSONReporter(six.StringIO())
]
self._runtest(['pylint/test/functional/arguments.py'],
reporter=MultiReporter(reporters), code=1)
@@ -127,17 +128,23 @@ class RunTC(unittest.TestCase):
if sys.version_info < (3, 0):
strio = tempfile.TemporaryFile()
else:
- strio = StringIO()
+ strio = six.StringIO()
assert strio.encoding is None
self._runtest([join(HERE, 'regrtest_data/no_stdout_encoding.py')],
out=strio)
- @unittest.skipIf(sys.platform.startswith("win") and sys.version_info[0] == 2,
- "This test does not work on Python 2.X due to a bug in "
- "multiprocessing.")
def test_parallel_execution(self):
self._runtest(['-j 2', 'pylint/test/functional/arguments.py',
'pylint/test/functional/bad_continuation.py'], code=1)
+ def test_py3k_option(self):
+ # Test that --py3k flag works.
+ rc_code = 2 if six.PY2 else 0
+ self._runtest([join(HERE, 'functional', 'unpacked_exceptions.py'),
+ '--py3k'],
+ code=rc_code)
+
+
+
if __name__ == '__main__':
unittest.main()
diff --git a/test/unittest_checker_format.py b/test/unittest_checker_format.py
index b246c02..7d5a32f 100644
--- a/test/unittest_checker_format.py
+++ b/test/unittest_checker_format.py
@@ -16,22 +16,17 @@
Check format checker helper functions
"""
from __future__ import unicode_literals
-import io
from os import linesep
import re
import sys
-import tokenize
-
from astroid import test_utils
from pylint.checkers.format import *
-from pylint.testutils import CheckerTestCase, Message, set_config
-
-
-def tokenize_str(code):
- return list(tokenize.generate_tokens(io.StringIO(code).readline))
+from pylint.testutils import (
+ CheckerTestCase, Message, set_config, tokenize_str,
+)
class MultiStatementLineTest(CheckerTestCase):
diff --git a/test/unittest_checker_misc.py b/test/unittest_checker_misc.py
index 940b76d..03e3cc2 100644
--- a/test/unittest_checker_misc.py
+++ b/test/unittest_checker_misc.py
@@ -14,37 +14,15 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
"""Tests for the misc checker."""
-import contextlib
-import os
-import sys
-import tempfile
import unittest
-from astroid import test_utils
from pylint.checkers import misc
-from pylint.testutils import CheckerTestCase, Message, set_config
+from pylint.testutils import (
+ CheckerTestCase, Message,
+ set_config, create_file_backed_module,
+)
-@contextlib.contextmanager
-def create_file_backed_module(code):
- # Can't use tempfile.NamedTemporaryFile here
- # because on Windows the file must be closed before writing to it,
- # see http://bugs.python.org/issue14243
- fd, tmp = tempfile.mkstemp()
- if sys.version_info >= (3, 0):
- # erff
- os.write(fd, bytes(code, 'ascii'))
- else:
- os.write(fd, code)
-
- try:
- module = test_utils.build_module(code)
- module.file = tmp
- yield module
- finally:
- os.close(fd)
- os.remove(tmp)
-
class FixmeTest(CheckerTestCase):
CHECKER_CLASS = misc.EncodingChecker
@@ -66,5 +44,6 @@ class FixmeTest(CheckerTestCase):
with self.assertNoMessages():
self.checker.process_module(module)
+
if __name__ == '__main__':
unittest.main()
diff --git a/test/unittest_checker_python3.py b/test/unittest_checker_python3.py
index ecfbc4e..bc7fc87 100644
--- a/test/unittest_checker_python3.py
+++ b/test/unittest_checker_python3.py
@@ -16,6 +16,7 @@ from __future__ import absolute_import
import sys
import unittest
+import textwrap
from astroid import test_utils
@@ -27,9 +28,9 @@ def python2_only(test):
"""Decorator for any tests that will fail under Python 3."""
return unittest.skipIf(sys.version_info[0] > 2, 'Python 2 only')(test)
+# TODO(cpopa): Port these to the functional test framework instead.
class Python3CheckerTest(testutils.CheckerTestCase):
-
CHECKER_CLASS = checker.Python3Checker
def check_bad_builtin(self, builtin_name):
@@ -39,88 +40,57 @@ class Python3CheckerTest(testutils.CheckerTestCase):
self.checker.visit_name(node)
@python2_only
- def test_apply_builtin(self):
- self.check_bad_builtin('apply')
-
- @python2_only
- def test_buffer_builtin(self):
- self.check_bad_builtin('buffer')
-
- @python2_only
- def test_cmp_builtin(self):
- self.check_bad_builtin('cmp')
-
- @python2_only
- def test_coerce_builtin(self):
- self.check_bad_builtin('coerce')
-
- @python2_only
- def test_execfile_builtin(self):
- self.check_bad_builtin('execfile')
-
- @python2_only
- def test_file_builtin(self):
- self.check_bad_builtin('file')
-
- @python2_only
- def test_long_builtin(self):
- self.check_bad_builtin('long')
-
- @python2_only
- def test_raw_input_builtin(self):
- self.check_bad_builtin('raw_input')
-
- @python2_only
- def test_reduce_builtin(self):
- self.check_bad_builtin('reduce')
-
- @python2_only
- def test_StandardError_builtin(self):
- self.check_bad_builtin('StandardError')
-
- @python2_only
- def test_unicode_builtin(self):
- self.check_bad_builtin('unicode')
-
- @python2_only
- def test_xrange_builtin(self):
- self.check_bad_builtin('xrange')
-
- def test_delslice_method(self):
+ def test_bad_builtins(self):
+ builtins = [
+ 'apply',
+ 'buffer',
+ 'cmp',
+ 'coerce',
+ 'execfile',
+ 'file',
+ 'long',
+ 'raw_input',
+ 'reduce',
+ 'StandardError',
+ 'unicode',
+ 'xrange',
+ 'reload',
+ ]
+ for builtin in builtins:
+ self.check_bad_builtin(builtin)
+
+ def _test_defined_method(self, method, warning):
node = test_utils.extract_node("""
class Foo(object):
- def __delslice__(self, i, j): #@
- pass""")
- message = testutils.Message('delslice-method', node=node)
+ def __{0}__(self, other): #@
+ pass""".format(method))
+ message = testutils.Message(warning, node=node)
with self.assertAddsMessages(message):
self.checker.visit_function(node)
+ def test_delslice_method(self):
+ self._test_defined_method('delslice', 'delslice-method')
+
def test_getslice_method(self):
- node = test_utils.extract_node("""
- class Foo(object):
- def __getslice__(self, i, j): #@
- pass""")
- message = testutils.Message('getslice-method', node=node)
- with self.assertAddsMessages(message):
- self.checker.visit_function(node)
+ self._test_defined_method('getslice', 'getslice-method')
def test_setslice_method(self):
- node = test_utils.extract_node("""
- class Foo(object):
- def __setslice__(self, i, j, value): #@
- pass""")
- message = testutils.Message('setslice-method', node=node)
- with self.assertAddsMessages(message):
- self.checker.visit_function(node)
+ self._test_defined_method('setslice', 'setslice-method')
def test_coerce_method(self):
- node = test_utils.extract_node("""
- class Foo(object):
- def __coerce__(self, other): #@
- pass""")
- message = testutils.Message('coerce-method', node=node)
- with self.assertAddsMessages(message):
- self.checker.visit_function(node)
+ self._test_defined_method('coerce', 'coerce-method')
+
+ def test_oct_method(self):
+ self._test_defined_method('oct', 'oct-method')
+
+ def test_hex_method(self):
+ self._test_defined_method('hex', 'hex-method')
+
+ def test_nonzero_method(self):
+ self._test_defined_method('nonzero', 'nonzero-method')
+
+ def test_cmp_method(self):
+ self._test_defined_method('cmp', 'cmp-method')
@python2_only
def test_print_statement(self):
@@ -129,6 +99,13 @@ class Python3CheckerTest(testutils.CheckerTestCase):
with self.assertAddsMessages(message):
self.checker.visit_print(node)
+ @python2_only
+ def test_backtick(self):
+ node = test_utils.extract_node('`test`')
+ message = testutils.Message('backtick', node=node)
+ with self.assertAddsMessages(message):
+ self.checker.visit_backquote(node)
+
def test_relative_import(self):
node = test_utils.extract_node('import string #@')
message = testutils.Message('no-absolute-import', node=node)
@@ -223,6 +200,16 @@ class Python3CheckerTest(testutils.CheckerTestCase):
with self.assertAddsMessages(message):
self.checker.visit_callfunc(node)
+ @python2_only
+ def test_implicit_map_evaluation(self):
+ node = test_utils.extract_node('map(str, [1, 2, 3])')
+ discard = node.parent
+ message = testutils.Message('implicit-map-evaluation', node=discard)
+ with self.assertAddsMessages(message):
+ # Use node.parent because extract_node returns the value
+ # of a discard node, not the discard itself.
+ self.checker.visit_discard(discard)
+
def test_not_next_method(self):
arg_node = test_utils.extract_node('x.next(x) #@')
stararg_node = test_utils.extract_node('x.next(*x) #@')
@@ -244,6 +231,68 @@ class Python3CheckerTest(testutils.CheckerTestCase):
with self.assertNoMessages():
self.walk(module)
+ @python2_only
+ def test_parameter_unpacking(self):
+ node = test_utils.extract_node('def func((a, b)):#@\n pass')
+ arg = node.args.args[0]
+ with self.assertAddsMessages(testutils.Message('parameter-unpacking', node=arg)):
+ self.checker.visit_arguments(node.args)
+
+ @python2_only
+ def test_old_raise_syntax(self):
+ node = test_utils.extract_node('raise Exception, "test"')
+ message = testutils.Message('old-raise-syntax', node=node)
+ with self.assertAddsMessages(message):
+ self.checker.visit_raise(node)
+
+ @python2_only
+ def test_raising_string(self):
+ node = test_utils.extract_node('raise "Test"')
+ message = testutils.Message('raising-string', node=node)
+ with self.assertAddsMessages(message):
+ self.checker.visit_raise(node)
+
+ @python2_only
+ def test_checker_disabled_by_default(self):
+ node = test_utils.build_module(textwrap.dedent("""
+ abc = 1l
+ raise Exception, "test"
+ raise "test"
+ `abc`
+ """))
+ with self.assertNoMessages():
+ self.walk(node)
+
+
+@python2_only
+class Python3TokenCheckerTest(testutils.CheckerTestCase):
+
+ CHECKER_CLASS = checker.Python3TokenChecker
+
+ def _test_token_message(self, code, symbolic_message):
+ tokens = testutils.tokenize_str(code)
+ message = testutils.Message(symbolic_message, line=1)
+ with self.assertAddsMessages(message):
+ self.checker.process_tokens(tokens)
+
+ def test_long_suffix(self):
+ for code in ("1l", "1L"):
+ self._test_token_message(code, 'long-suffix')
+
+ def test_old_ne_operator(self):
+ self._test_token_message("1 <> 2", "old-ne-operator")
+
+ def test_old_octal_literal(self):
+ for octal in ("045", "055", "075", "077", "076543"):
+ self._test_token_message(octal, "old-octal-literal")
+
+ # Make sure we are catching only octals.
+ for non_octal in ("45", "00", "085", "08"):
+ tokens = testutils.tokenize_str(non_octal)
+ with self.assertNoMessages():
+ self.checker.process_tokens(tokens)
+
if __name__ == '__main__':
unittest.main()
+ \ No newline at end of file
diff --git a/test/unittest_checker_similar.py b/test/unittest_checker_similar.py
index 8d68b81..4cd48cc 100644
--- a/test/unittest_checker_similar.py
+++ b/test/unittest_checker_similar.py
@@ -1,8 +1,9 @@
-import cStringIO
import sys
from os.path import join, dirname, abspath
import unittest
+import six
+
from pylint.checkers import similar
SIMILAR1 = join(dirname(abspath(__file__)), 'input', 'similar1')
@@ -12,10 +13,10 @@ class SimilarTC(unittest.TestCase):
"""test the similar command line utility"""
def test_ignore_comments(self):
- sys.stdout = cStringIO.StringIO()
+ sys.stdout = six.StringIO()
try:
similar.Run(['--ignore-comments', SIMILAR1, SIMILAR2])
- except SystemExit, ex:
+ except SystemExit as ex:
self.assertEqual(ex.code, 0)
output = sys.stdout.getvalue()
else:
@@ -41,10 +42,10 @@ TOTAL lines=44 duplicates=10 percent=22.73
def test_ignore_docsrings(self):
- sys.stdout = cStringIO.StringIO()
+ sys.stdout = six.StringIO()
try:
similar.Run(['--ignore-docstrings', SIMILAR1, SIMILAR2])
- except SystemExit, ex:
+ except SystemExit as ex:
self.assertEqual(ex.code, 0)
output = sys.stdout.getvalue()
else:
@@ -77,10 +78,10 @@ TOTAL lines=44 duplicates=13 percent=29.55
def test_ignore_imports(self):
- sys.stdout = cStringIO.StringIO()
+ sys.stdout = six.StringIO()
try:
similar.Run(['--ignore-imports', SIMILAR1, SIMILAR2])
- except SystemExit, ex:
+ except SystemExit as ex:
self.assertEqual(ex.code, 0)
output = sys.stdout.getvalue()
else:
@@ -93,10 +94,10 @@ TOTAL lines=44 duplicates=0 percent=0.00
def test_ignore_nothing(self):
- sys.stdout = cStringIO.StringIO()
+ sys.stdout = six.StringIO()
try:
similar.Run([SIMILAR1, SIMILAR2])
- except SystemExit, ex:
+ except SystemExit as ex:
self.assertEqual(ex.code, 0)
output = sys.stdout.getvalue()
else:
@@ -116,10 +117,10 @@ TOTAL lines=44 duplicates=5 percent=11.36
""" % (SIMILAR1, SIMILAR2)).strip())
def test_help(self):
- sys.stdout = cStringIO.StringIO()
+ sys.stdout = six.StringIO()
try:
similar.Run(['--help'])
- except SystemExit, ex:
+ except SystemExit as ex:
self.assertEqual(ex.code, 0)
else:
self.fail('not system exit')
@@ -127,10 +128,10 @@ TOTAL lines=44 duplicates=5 percent=11.36
sys.stdout = sys.__stdout__
def test_no_args(self):
- sys.stdout = cStringIO.StringIO()
+ sys.stdout = six.StringIO()
try:
similar.Run([])
- except SystemExit, ex:
+ except SystemExit as ex:
self.assertEqual(ex.code, 1)
else:
self.fail('not system exit')
diff --git a/test/unittest_checker_spelling.py b/test/unittest_checker_spelling.py
index 98217ff..8b55042 100644
--- a/test/unittest_checker_spelling.py
+++ b/test/unittest_checker_spelling.py
@@ -13,13 +13,15 @@
# this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
"""Unittest for the spelling checker."""
+
import unittest
-import tokenize
-import io
+
from astroid import test_utils
from pylint.checkers import spelling
-from pylint.testutils import CheckerTestCase, Message, set_config
+from pylint.testutils import (
+ CheckerTestCase, Message, set_config, tokenize_str,
+)
# try to create enchant dictionary
try:
@@ -36,10 +38,6 @@ if enchant is not None:
pass
-def tokenize_str(code):
- return list(tokenize.generate_tokens(io.StringIO(code).readline))
-
-
class SpellingCheckerTest(CheckerTestCase):
CHECKER_CLASS = spelling.SpellingChecker
diff --git a/test/unittest_lint.py b/test/unittest_lint.py
index c006406..70ac115 100644
--- a/test/unittest_lint.py
+++ b/test/unittest_lint.py
@@ -13,7 +13,6 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
from contextlib import contextmanager
-import cStringIO
import sys
import os
import tempfile
@@ -22,6 +21,8 @@ from os import getcwd, chdir
from os.path import join, basename, dirname, isdir, abspath, sep
import unittest
+import six
+
from logilab.common.compat import reload
from pylint import config, lint
@@ -464,11 +465,11 @@ class PyLinterTC(unittest.TestCase):
self.linter.set_reporter(TestReporter())
self.linter.check(os.path.join(os.path.dirname(__file__), 'data', 'ascript'))
self.assertEqual(
- ['C: 2: Line too long (175/80)'],
+ ['C: 2: Line too long (175/100)'],
self.linter.reporter.messages)
def test_html_reporter_missing_files(self):
- output = cStringIO.StringIO()
+ output = six.StringIO()
self.linter.set_reporter(html.HTMLReporter(output))
self.linter.set_option('output-format', 'html')
self.linter.check('troppoptop.py')
@@ -477,6 +478,14 @@ class PyLinterTC(unittest.TestCase):
self.assertIn('troppoptop.py', value)
self.assertIn('fatal', value)
+ def test_python3_checker_disabled(self):
+ checker_names = [c.name for c in self.linter.prepare_checkers()]
+ self.assertNotIn('python3', checker_names)
+
+ self.linter.set_option('enable', 'python3')
+ checker_names = [c.name for c in self.linter.prepare_checkers()]
+ self.assertIn('python3', checker_names)
+
class ConfigTC(unittest.TestCase):
@@ -661,7 +670,7 @@ class MessagesStoreTC(unittest.TestCase):
msg, checkerref=False)
def test_list_messages(self):
- sys.stdout = cStringIO.StringIO()
+ sys.stdout = six.StringIO()
try:
self.store.list_messages()
output = sys.stdout.getvalue()
diff --git a/test/unittest_pyreverse_diadefs.py b/test/unittest_pyreverse_diadefs.py
index 0f67e36..5f775aa 100644
--- a/test/unittest_pyreverse_diadefs.py
+++ b/test/unittest_pyreverse_diadefs.py
@@ -20,6 +20,8 @@ unittest for the extensions.diadefslib modules
import unittest
import sys
+import six
+
import astroid
from astroid import MANAGER
from astroid.inspector import Linker
@@ -38,7 +40,7 @@ def _process_classes(classes):
def _process_relations(relations):
"""extract relation indices from a relation list"""
result = []
- for rel_type, rels in relations.iteritems():
+ for rel_type, rels in six.iteritems(relations):
for rel in rels:
result.append( (rel_type, rel.from_object.title,
rel.to_object.title) )
diff --git a/test/unittest_reporting.py b/test/unittest_reporting.py
index 88fb288..ef1de0c 100644
--- a/test/unittest_reporting.py
+++ b/test/unittest_reporting.py
@@ -11,11 +11,11 @@
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-import cStringIO
import os
from os.path import join, dirname, abspath
import unittest
+import six
from pylint.lint import PyLinter
from pylint import checkers
@@ -35,7 +35,7 @@ class PyLinterTC(unittest.TestCase):
os.environ.pop('PYLINTRC', None)
def test_template_option(self):
- output = cStringIO.StringIO()
+ output = six.StringIO()
self.linter.reporter.set_output(output)
self.linter.set_option('msg-template', '{msg_id}:{line:03d}')
self.linter.open()
@@ -48,7 +48,7 @@ class PyLinterTC(unittest.TestCase):
'C0301:002\n')
def test_parseable_output_regression(self):
- output = cStringIO.StringIO()
+ output = six.StringIO()
linter = PyLinter(reporter=ParseableTextReporter())
checkers.initialize(linter)
linter.config.persistent = 0
diff --git a/testutils.py b/testutils.py
index 0b1b967..2f9af4d 100644
--- a/testutils.py
+++ b/testutils.py
@@ -19,15 +19,18 @@ from __future__ import print_function
import collections
import contextlib
import functools
+import os
import sys
import re
import unittest
+import tempfile
+import tokenize
from glob import glob
from os import linesep, getcwd, sep
from os.path import abspath, basename, dirname, isdir, join, splitext
-
+from astroid import test_utils
from pylint import checkers
from pylint.utils import PyLintASTWalker
@@ -92,7 +95,8 @@ class TestReporter(BaseReporter):
__implements____ = IReporter
- def __init__(self):
+ def __init__(self): # pylint: disable=super-init-not-called
+
self.message_ids = {}
self.reset()
self.path_strip_prefix = getcwd() + sep
@@ -134,6 +138,7 @@ class Message(collections.namedtuple('Message',
class UnittestLinter(object):
"""A fake linter class to capture checker messages."""
+ # pylint: disable=unused-argument, no-self-use
def __init__(self):
self._messages = []
@@ -145,7 +150,8 @@ class UnittestLinter(object):
finally:
self._messages = []
- def add_message(self, msg_id, line=None, node=None, args=None, confidence=None):
+ def add_message(self, msg_id, line=None, node=None, args=None,
+ confidence=None):
self._messages.append(Message(msg_id, line, node, args))
def is_message_enabled(self, *unused_args):
@@ -234,7 +240,7 @@ else:
INFO_TEST_RGX = re.compile(r'^func_i\d\d\d\d$')
-def exception_str(self, ex):
+def exception_str(self, ex): # pylint: disable=unused-argument
"""function used to replace default __str__ method of exception instances"""
return 'in %s\n:: %s' % (ex.file, ', '.join(ex.args))
@@ -332,6 +338,7 @@ class LintTestUpdate(LintTestUsingModule):
def cb_test_gen(base_class):
def call(input_dir, msg_dir, module_file, messages_file, dependencies):
+ # pylint: disable=no-init
class LintTC(base_class):
module = module_file.replace('.py', '')
output = messages_file
@@ -368,3 +375,38 @@ def make_tests(input_dir, msg_dir, filter_rgx, callbacks):
if test:
tests.append(test)
return tests
+
+def tokenize_str(code):
+ return list(tokenize.generate_tokens(StringIO(code).readline))
+
+@contextlib.contextmanager
+def create_tempfile(content=None):
+ """Create a new temporary file.
+
+ If *content* parameter is given, then it will be written
+ in the temporary file, before passing it back.
+ This is a context manager and should be used with a *with* statement.
+ """
+ # Can't use tempfile.NamedTemporaryFile here
+ # because on Windows the file must be closed before writing to it,
+ # see http://bugs.python.org/issue14243
+ fd, tmp = tempfile.mkstemp()
+ if content:
+ if sys.version_info >= (3, 0):
+ # erff
+ os.write(fd, bytes(content, 'ascii'))
+ else:
+ os.write(fd, content)
+ try:
+ yield tmp
+ finally:
+ os.close(fd)
+ os.remove(tmp)
+
+@contextlib.contextmanager
+def create_file_backed_module(code):
+ """Create an astroid module for the given code, backed by a real file."""
+ with create_tempfile() as temp:
+ module = test_utils.build_module(code)
+ module.file = temp
+ yield module
diff --git a/utils.py b/utils.py
index bacb75b..fcc7904 100644
--- a/utils.py
+++ b/utils.py
@@ -24,9 +24,11 @@ import re
import sys
import tokenize
import warnings
-
from os.path import dirname, basename, splitext, exists, isdir, join, normpath
+import six
+from six.moves import zip # pylint: disable=redefined-builtin
+
from logilab.common.interface import implements
from logilab.common.textutils import normalize_text
from logilab.common.configuration import rest_format_section
@@ -37,8 +39,6 @@ from astroid.modutils import modpath_from_file, get_module_files, \
file_from_modpath, load_module_from_file
from pylint.interfaces import IRawChecker, ITokenChecker, UNDEFINED
-import six
-from six.moves import zip
class UnknownMessage(Exception):
@@ -94,16 +94,6 @@ class Message(_MsgBase):
cls, msg_id, symbol, msg, msg_id[0], MSG_TYPES[msg_id[0]],
confidence, *location)
- def get_init_args(self):
- location = (
- self.abspath,
- self.path,
- self.module,
- self.obj,
- self.line,
- self.column)
- return (self.msg_id, self.symbol, location, self.msg, self.confidence)
-
def format(self, template):
"""Format the message according to the given template.
@@ -131,11 +121,11 @@ def get_module_and_frameid(node):
obj.reverse()
return module, '.'.join(obj)
-def category_id(id):
- id = id.upper()
- if id in MSG_TYPES:
- return id
- return MSG_TYPES_LONG.get(id)
+def category_id(cid):
+ cid = cid.upper()
+ if cid in MSG_TYPES:
+ return cid
+ return MSG_TYPES_LONG.get(cid)
def tokenize_module(module):
@@ -413,12 +403,15 @@ class MessagesHandlerMixIn(object):
def print_full_documentation(self):
"""output a full documentation in ReST format"""
+ print("Pylint global options and switches")
+ print("----------------------------------")
+ print("")
+ print("Pylint provides global options and switches.")
+ print("")
+
by_checker = {}
for checker in self.get_checkers():
if checker.name == 'master':
- prefix = 'Main '
- print("Options")
- print('-------\n')
if checker.options:
for section, options in checker.options_by_section():
if section is None:
@@ -428,7 +421,7 @@ class MessagesHandlerMixIn(object):
print(title)
print('~' * len(title))
rest_format_section(sys.stdout, None, options)
- print()
+ print("")
else:
try:
by_checker[checker.name][0] += checker.options_and_values()
@@ -438,35 +431,49 @@ class MessagesHandlerMixIn(object):
by_checker[checker.name] = [list(checker.options_and_values()),
dict(checker.msgs),
list(checker.reports)]
+
+ print("Pylint checkers' options and switches")
+ print("-------------------------------------")
+ print("")
+ print("Pylint checkers can provide three set of features:")
+ print("")
+ print("* options that control their execution,")
+ print("* messages that they can raise,")
+ print("* reports that they can generate.")
+ print("")
+ print("Below is a list of all checkers and their features.")
+ print("")
+
for checker, (options, msgs, reports) in six.iteritems(by_checker):
- prefix = ''
- title = '%s checker' % checker
+ title = '%s checker' % (checker.replace("_", " ").title())
print(title)
- print('-' * len(title))
- print()
+ print('~' * len(title))
+ print("")
+ print("Verbatim name of the checker is ``%s``." % checker)
+ print("")
if options:
title = 'Options'
print(title)
- print('~' * len(title))
+ print('^' * len(title))
rest_format_section(sys.stdout, None, options)
- print()
+ print("")
if msgs:
- title = ('%smessages' % prefix).capitalize()
+ title = 'Messages'
print(title)
print('~' * len(title))
for msgid, msg in sorted(six.iteritems(msgs),
key=lambda kv: (_MSG_ORDER.index(kv[0][0]), kv[1])):
msg = build_message_def(checker, msgid, msg)
print(msg.format_help(checkerref=False))
- print()
+ print("")
if reports:
- title = ('%sreports' % prefix).capitalize()
+ title = 'Reports'
print(title)
print('~' * len(title))
for report in reports:
print(':%s: %s' % report[:2])
- print()
- print()
+ print("")
+ print("")
class FileState(object):
@@ -550,7 +557,8 @@ class FileState(object):
except KeyError:
self._module_msgs_state[msg.msgid] = {line: status}
- def handle_ignored_message(self, state_scope, msgid, line, node, args, confidence):
+ def handle_ignored_message(self, state_scope, msgid, line,
+ node, args, confidence): # pylint: disable=unused-argument
"""Report an ignored message.
state_scope is either MSG_STATE_SCOPE_MODULE or MSG_STATE_SCOPE_CONFIG,
@@ -664,10 +672,10 @@ class MessagesStore(object):
for msgid in msgids:
try:
print(self.check_message_id(msgid).format_help(checkerref=True))
- print()
+ print("")
except UnknownMessage as ex:
print(ex)
- print()
+ print("")
continue
def list_messages(self):
@@ -677,7 +685,7 @@ class MessagesStore(object):
if not msg.may_be_emitted():
continue
print(msg.format_help(checkerref=False))
- print()
+ print("")
class ReportsHandlerMixIn(object):