summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README3
-rw-r--r--checkers/base.py26
-rw-r--r--checkers/classes.py21
-rw-r--r--checkers/design_analysis.py12
-rw-r--r--checkers/exceptions.py19
-rw-r--r--checkers/format.py14
-rw-r--r--checkers/imports.py8
-rw-r--r--checkers/logging.py5
-rw-r--r--checkers/misc.py1
-rw-r--r--checkers/newstyle.py4
-rw-r--r--checkers/similar.py1
-rw-r--r--checkers/string_format.py9
-rw-r--r--checkers/typecheck.py10
-rw-r--r--checkers/variables.py16
-rw-r--r--doc/FAQ.txt9
-rw-r--r--doc/manual.txt2
-rw-r--r--lint.py21
-rw-r--r--reporters/__init__.py22
-rw-r--r--reporters/guireporter.py6
-rw-r--r--reporters/html.py5
-rw-r--r--reporters/text.py15
-rw-r--r--test/input/func_docstring.py13
-rw-r--r--test/unittest_lint.py30
-rw-r--r--utils.py49
24 files changed, 278 insertions, 43 deletions
diff --git a/README b/README
index 8123091..0072dce 100644
--- a/README
+++ b/README
@@ -65,7 +65,8 @@ order doesn't matter...
* Wolfgang Grafen, Axel Muller, Fabio Zadrozny, Pierre Rouleau,
Maarten ter Huurne, Mirko Friedenhagen (among others):
bug reports, feedback, feature requests...
-* Martin Pool (Google): warnings for anomalous backslashes
+* Martin Pool (Google): warnings for anomalous backslashes, symbolic names
+ for messages (like 'unused')
* All the Logilab's team: daily use, bug reports, feature requests
* Other people have contributed by their feedback, if I've forgotten
you, send me a note !
diff --git a/checkers/base.py b/checkers/base.py
index c08168d..08ea0cc 100644
--- a/checkers/base.py
+++ b/checkers/base.py
@@ -124,27 +124,35 @@ class _BasicChecker(BaseChecker):
class BasicErrorChecker(_BasicChecker):
msgs = {
'E0100': ('__init__ method is a generator',
+ 'init-is-generator',
'Used when the special class method __init__ is turned into a '
'generator by a yield in its body.'),
'E0101': ('Explicit return in __init__',
+ 'return-in-init',
'Used when the special class method __init__ has an explicit \
return value.'),
'E0102': ('%s already defined line %s',
+ 'function-redefined',
'Used when a function / class / method is redefined.'),
'E0103': ('%r not properly in loop',
+ 'not-in-loop',
'Used when break or continue keywords are used outside a loop.'),
'E0104': ('Return outside function',
+ 'return-outside-function',
'Used when a "return" statement is found outside a function or '
'method.'),
'E0105': ('Yield outside function',
+ 'yield-outside-function',
'Used when a "yield" statement is found outside a function or '
'method.'),
'E0106': ('Return with argument inside generator',
+ 'return-arg-in-generator',
'Used when a "return" statement with an argument is found '
'outside in a generator function or method (e.g. with some '
'"yield" statements).'),
'E0107': ("Use of the non-existent %s operator",
+ 'nonexistent-operator',
"Used when you attempt to use the C-style pre-increment or"
"pre-decrement operator -- and ++, which doesn't exist in Python."),
}
@@ -242,54 +250,67 @@ functions, methods
name = 'basic'
msgs = {
'W0101': ('Unreachable code',
+ 'unreachable',
'Used when there is some code behind a "return" or "raise" \
statement, which will never be accessed.'),
'W0102': ('Dangerous default value %s as argument',
+ 'dangerous-default-value',
'Used when a mutable value as list or dictionary is detected in \
a default value for an argument.'),
'W0104': ('Statement seems to have no effect',
+ 'pointless-statement',
'Used when a statement doesn\'t have (or at least seems to) \
any effect.'),
'W0105': ('String statement has no effect',
+ 'pointless-string-statement',
'Used when a string is used as a statement (which of course \
has no effect). This is a particular case of W0104 with its \
own message so you can easily disable it if you\'re using \
those strings as documentation, instead of comments.'),
'W0106': ('Expression "%s" is assigned to nothing',
+ 'expression-not-assigned',
'Used when an expression that is not a function call is assigned\
to nothing. Probably something else was intended.'),
'W0108': ('Lambda may not be necessary',
+ 'unnecessary-lambda',
'Used when the body of a lambda expression is a function call \
on the same argument list as the lambda itself; such lambda \
expressions are in all but a few cases replaceable with the \
function being called in the body of the lambda.'),
'W0109': ("Duplicate key %r in dictionary",
+ 'duplicate-key',
"Used when a dictionary expression binds the same key multiple \
times."),
'W0122': ('Use of the exec statement',
+ 'exec-statement',
'Used when you use the "exec" statement, to discourage its \
usage. That doesn\'t mean you can not use it !'),
'W0141': ('Used builtin function %r',
+ 'bad-builtin',
'Used when a black listed builtin function is used (see the '
'bad-function option). Usual black listed functions are the ones '
'like map, or filter , where Python offers now some cleaner '
'alternative like list comprehension.'),
'W0142': ('Used * or ** magic',
+ 'star-args',
'Used when a function or method is called using `*args` or '
'`**kwargs` to dispatch arguments. This doesn\'t improve '
'readability and should be used with care.'),
'W0150': ("%s statement in finally block may swallow exception",
+ 'lost-exception',
"Used when a break or a return statement is found inside the \
finally clause of a try...finally block: the exceptions raised \
in the try clause will be silently swallowed instead of being \
re-raised."),
'W0199': ('Assert called on a 2-uple. Did you mean \'assert x,y\'?',
+ 'assert-on-tuple',
'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.'),
'C0121': ('Missing required attribute "%s"', # W0103
+ 'missing-module-attribute',
'Used when an attribute required for modules is missing.'),
}
@@ -558,9 +579,11 @@ functions, methods
class NameChecker(_BasicChecker):
msgs = {
'C0102': ('Black listed name "%s"',
+ 'blacklisted-name',
'Used when the name is listed in the black list (unauthorized \
names).'),
'C0103': ('Invalid name "%s" (should match %s)',
+ 'invalid-name',
'Used when the name doesn\'t match the regular expression \
associated to its type (constant, variable, class...).'),
@@ -709,10 +732,12 @@ class NameChecker(_BasicChecker):
class DocStringChecker(_BasicChecker):
msgs = {
'C0111': ('Missing docstring', # W0131
+ 'missing-docstring',
'Used when a module, function, class or method has no docstring.\
Some special methods like __init__ doesn\'t necessary require a \
docstring.'),
'C0112': ('Empty docstring', # W0132
+ 'empty-docstring',
'Used when a module, function, class or method has an empty \
docstring (it would be too easy ;).'),
}
@@ -768,6 +793,7 @@ class DocStringChecker(_BasicChecker):
class PassChecker(_BasicChecker):
"""check is the pass statement is really necessary"""
msgs = {'W0107': ('Unnecessary pass statement',
+ 'unnecessary-pass',
'Used when a "pass" statement that can be avoided is '
'encountered.)'),
}
diff --git a/checkers/classes.py b/checkers/classes.py
index 96fc183..6383001 100644
--- a/checkers/classes.py
+++ b/checkers/classes.py
@@ -38,87 +38,108 @@ def class_is_abstract(node):
MSGS = {
'F0202': ('Unable to check methods signature (%s / %s)',
+ 'method-check-failed',
'Used when PyLint has been unable to check methods signature \
compatibility for an unexpected reason. Please report this kind \
if you don\'t make sense of it.'),
'E0202': ('An attribute affected in %s line %s hide this method',
+ 'method-hidden',
'Used when a class defines a method which is hidden by an '
'instance attribute from an ancestor class or set by some '
'client code.'),
'E0203': ('Access to member %r before its definition line %s',
+ 'access-member-before-definition',
'Used when an instance member is accessed before it\'s actually\
assigned.'),
'W0201': ('Attribute %r defined outside __init__',
+ 'attribute-defined-outside-init',
'Used when an instance attribute is defined outside the __init__\
method.'),
'W0212': ('Access to a protected member %s of a client class', # E0214
+ 'protected-access',
'Used when a protected member (i.e. class member with a name \
beginning with an underscore) is access outside the class or a \
descendant of the class where it\'s defined.'),
'E0211': ('Method has no argument',
+ 'no-method-argument',
'Used when a method which should have the bound instance as \
first argument has no argument defined.'),
'E0213': ('Method should have "self" as first argument',
+ 'no-self-argument',
'Used when a method has an attribute different the "self" as\
first argument. This is considered as an error since this is\
a so common convention that you shouldn\'t break it!'),
'C0202': ('Class method %s should have %s as first argument', # E0212
+ 'bad-classmethod-argument',
'Used when a class method has a first argument named differently '
'than the value specified in valid-classmethod-first-arg option '
'(default to "cls"), recommended to easily differentiate them '
'from regular instance methods.'),
'C0203': ('Metaclass method %s should have %s as first argument', # E0214
+ 'bad-mcs-method-argument',
'Used when a metaclass method has a first agument named '
'differently than the value specified in valid-classmethod-first'
'-arg option (default to "cls"), recommended to easily '
'differentiate them from regular instance methods.'),
'C0204': ('Metaclass class method %s should have %s as first argument',
+ 'bad-mcs-classmethod-argument',
'Used when a metaclass class method has a first argument named '
'differently than the value specified in valid-metaclass-'
'classmethod-first-arg option (default to "mcs"), recommended to '
'easily differentiate them from regular instance methods.'),
'W0211': ('Static method with %r as first argument',
+ 'bad-staticmethod-argument',
'Used when a static method has "self" or a value specified in '
'valid-classmethod-first-arg option or '
'valid-metaclass-classmethod-first-arg option as first argument.'
),
'R0201': ('Method could be a function',
+ 'no-self-use',
'Used when a method doesn\'t use its bound instance, and so could\
be written as a function.'
),
'E0221': ('Interface resolved to %s is not a class',
+ 'interface-is-not-class',
'Used when a class claims to implement an interface which is not \
a class.'),
'E0222': ('Missing method %r from %s interface',
+ 'missing-interface-method',
'Used when a method declared in an interface is missing from a \
class implementing this interface'),
'W0221': ('Arguments number differs from %s method',
+ 'arguments-differ',
'Used when a method has a different number of arguments than in \
the implemented interface or in an overridden method.'),
'W0222': ('Signature differs from %s method',
+ 'signature-differs',
'Used when a method signature is different than in the \
implemented interface or in an overridden method.'),
'W0223': ('Method %r is abstract in class %r but is not overridden',
+ 'abstract-method',
'Used when an abstract method (i.e. raise NotImplementedError) is \
not overridden in concrete class.'
),
'F0220': ('failed to resolve interfaces implemented by %s (%s)', # W0224
+ 'unresolved-interface',
'Used when a PyLint as failed to find interfaces implemented by \
a class'),
'W0231': ('__init__ method from base class %r is not called',
+ 'super-init-not-called',
'Used when an ancestor class method has an __init__ method \
which is not called by a derived class.'),
'W0232': ('Class has no __init__ method',
+ 'no-init',
'Used when a class has no __init__ method, neither its parent \
classes.'),
'W0233': ('__init__ method from a non direct base class %r is called',
+ 'non-parent-init-called',
'Used when an __init__ method is called on a class which is not \
in the direct ancestors for the analysed class.'),
diff --git a/checkers/design_analysis.py b/checkers/design_analysis.py
index 659f191..2d40e8d 100644
--- a/checkers/design_analysis.py
+++ b/checkers/design_analysis.py
@@ -43,38 +43,50 @@ def class_is_abstract(klass):
MSGS = {
'R0901': ('Too many ancestors (%s/%s)',
+ 'too-many-ancestors',
'Used when class has too many parent classes, try to reduce \
this to get a more simple (and so easier to use) class.'),
'R0902': ('Too many instance attributes (%s/%s)',
+ 'too-many-instance-attributes',
'Used when class has too many instance attributes, try to reduce \
this to get a more simple (and so easier to use) class.'),
'R0903': ('Too few public methods (%s/%s)',
+ 'too-few-public-methods',
'Used when class has too few public methods, so be sure it\'s \
really worth it.'),
'R0904': ('Too many public methods (%s/%s)',
+ 'too-many-public-methods',
'Used when class has too many public methods, try to reduce \
this to get a more simple (and so easier to use) class.'),
'R0911': ('Too many return statements (%s/%s)',
+ 'too-many-return-statements',
'Used when a function or method has too many return statement, \
making it hard to follow.'),
'R0912': ('Too many branches (%s/%s)',
+ 'too-many-branches',
'Used when a function or method has too many branches, \
making it hard to follow.'),
'R0913': ('Too many arguments (%s/%s)',
+ 'too-many-arguments',
'Used when a function or method takes too many arguments.'),
'R0914': ('Too many local variables (%s/%s)',
+ 'too-many-locals',
'Used when a function or method has too many local variables.'),
'R0915': ('Too many statements (%s/%s)',
+ 'too-many-statements',
'Used when a function or method has too many statements. You \
should then split it in smaller functions / methods.'),
'R0921': ('Abstract class not referenced',
+ 'abstract-class-not-used',
'Used when an abstract class is not used as ancestor anywhere.'),
'R0922': ('Abstract class is only referenced %s times',
+ 'abstract-class-little-used',
'Used when an abstract class is used less than X times as \
ancestor.'),
'R0923': ('Interface not implemented',
+ 'interface-not-implemented',
'Used when an interface class is not implemented anywhere.'),
}
diff --git a/checkers/exceptions.py b/checkers/exceptions.py
index e94fa65..db0ebcc 100644
--- a/checkers/exceptions.py
+++ b/checkers/exceptions.py
@@ -29,36 +29,45 @@ from pylint.interfaces import IASTNGChecker
OVERGENERAL_EXCEPTIONS = ('Exception',)
MSGS = {
- 'E0701': (
- 'Bad except clauses order (%s)',
- '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.'),
+ 'E0701': ('Bad except clauses order (%s)',
+ 'bad-except-order',
+ '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',
+ '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).'),
'E0710': ('Raising a new style class which doesn\'t inherit from BaseException',
+ 'raising-non-exception',
'Used when a new style class which doesn\'t inherit from \
BaseException is raised.'),
'E0711': ('NotImplemented raised - should raise NotImplementedError',
+ 'notimplemented-raised',
'Used when NotImplemented is raised instead of \
NotImplementedError'),
'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 \
catch.'),
'W0703': ('Catching too general exception %s',
+ 'broad-except',
'Used when an except catches a too general exception, \
possibly burying unrelated errors.'),
'W0704': ('Except doesn\'t do anything',
+ 'pointless-except',
'Used when an except clause does nothing but "pass" and there is\
no "else" clause.'),
'W0710': ('Exception doesn\'t inherit from standard "Exception" class',
+ 'nonstandard-exception',
'Used when a custom exception class is raised but doesn\'t \
inherit from the builtin "Exception" class.'),
'W0711': ('Exception to catch is the result of a binary "%s" operation',
+ 'binary-op-exception',
'Used when the exception to catch is of the form \
"except A or B:". If intending to catch multiple, \
rewrite as "except (A, B):"'),
diff --git a/checkers/format.py b/checkers/format.py
index 85eb667..67b6952 100644
--- a/checkers/format.py
+++ b/checkers/format.py
@@ -37,28 +37,37 @@ from pylint.checkers.utils import check_messages
MSGS = {
'C0301': ('Line too long (%s/%s)',
+ 'line-too-long',
'Used when a line is longer than a given number of characters.'),
'C0302': ('Too many lines in module (%s)', # was W0302
+ 'too-many-lines',
'Used when a module has too much lines, reducing its readability.'
),
'W0311': ('Bad indentation. Found %s %s, expected %s',
+ 'bad-indentation',
'Used when an unexpected number of indentation\'s tabulations or '
'spaces has been found.'),
'W0312': ('Found indentation with %ss instead of %ss',
+ 'mixed-indentation',
'Used when there are some mixed tabs and spaces in a module.'),
'W0301': ('Unnecessary semicolon', # was W0106
+ 'unnecessary-semicolon',
'Used when a statement is ended by a semi-colon (";"), which \
isn\'t necessary (that\'s python, not C ;).'),
'C0321': ('More than one statement on a single line',
+ 'multiple-statements',
'Used when more than on statement are found on the same line.'),
'C0322': ('Operator not preceded by a space\n%s',
+ 'no-space-before-operator',
'Used when one of the following operator (!= | <= | == | >= | < '
'| > | = | \\+= | -= | \\*= | /= | %) is not preceded by a space.'),
'C0323': ('Operator not followed by a space\n%s',
+ 'no-space-after-operator',
'Used when one of the following operator (!= | <= | == | >= | < '
'| > | = | \\+= | -= | \\*= | /= | %) is not followed by a space.'),
'C0324': ('Comma not followed by a space\n%s',
+ 'no-space-after-comma',
'Used when a comma (",") is not followed by a space.'),
}
@@ -66,13 +75,16 @@ if sys.version_info < (3, 0):
MSGS.update({
'W0331': ('Use of the <> operator',
+ 'old-ne-operator',
'Used when the deprecated "<>" operator is used instead \
of "!=".'),
'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"'),
'W0333': ('Use of the `` operator',
+ 'backtick',
'Used when the deprecated "``" (backtick) operator is used '
'instead of the str() function.'),
})
@@ -365,10 +377,12 @@ class StringConstantChecker(BaseRawChecker):
msgs = {
'W1401': ('Anomalous backslash in string: \'%s\'. '
'String constant might be missing an r prefix.',
+ 'anomalous-backslash-in-string',
'Used when a backslash is in a literal string but not as an '
'escape.'),
'W1402': ('Anomalous Unicode escape in byte string: \'%s\'. '
'String constant might be missing an r or u prefix.',
+ 'anomalous-unicode-escape-in-string',
'Used when an escape like \\u is encountered in a byte '
'string where it has no effect.'),
}
diff --git a/checkers/imports.py b/checkers/imports.py
index aba10d2..2b61cf4 100644
--- a/checkers/imports.py
+++ b/checkers/imports.py
@@ -128,24 +128,32 @@ def make_graph(filename, dep_info, sect, gtype):
MSGS = {
'F0401': ('Unable to import %s',
+ 'import-error',
'Used when pylint has been unable to import a module.'),
'R0401': ('Cyclic import (%s)',
+ 'cyclic-import',
'Used when a cyclic import between two or more modules is \
detected.'),
'W0401': ('Wildcard import %s',
+ 'wildcard-import',
'Used when `from module import *` is detected.'),
'W0402': ('Uses of a deprecated module %r',
+ 'deprecated-module',
'Used a module marked as deprecated is imported.'),
'W0403': ('Relative import %r, should be %r',
+ 'relative-import',
'Used when an import relative to the package directory is \
detected.'),
'W0404': ('Reimport %r (imported line %s)',
+ 'reimported',
'Used when a module is reimported multiple times.'),
'W0406': ('Module import itself',
+ 'import-self',
'Used when a module is importing itself.'),
'W0410': ('__future__ import is not the first non docstring statement',
+ 'misplaced-future',
'Python 2.5 and greater require __future__ import to be the \
first non docstring statement in the module.'),
}
diff --git a/checkers/logging.py b/checkers/logging.py
index 89899b6..9092b6c 100644
--- a/checkers/logging.py
+++ b/checkers/logging.py
@@ -22,6 +22,7 @@ from pylint.checkers import utils
MSGS = {
'W1201': ('Specify string format arguments as logging function parameters',
+ 'logging-not-lazy',
'Used when a logging statement has a call form of '
'"logging.<logging method>(format_string % (format_args...))". '
'Such calls should leave string interpolation to the logging '
@@ -32,14 +33,18 @@ MSGS = {
'logged. For more, see '
'http://www.python.org/dev/peps/pep-0282/.'),
'E1200': ('Unsupported logging format character %r (%#02x) at index %d',
+ 'logging-unsupported-format',
'Used when an unsupported format character is used in a logging\
statement format string.'),
'E1201': ('Logging format string ends in middle of conversion specifier',
+ 'logging-format-truncated',
'Used when a logging statement format string terminates before\
the end of a conversion specifier.'),
'E1205': ('Too many arguments for logging format string',
+ 'logging-too-many-args',
'Used when a logging format string is given too few arguments.'),
'E1206': ('Not enough arguments for logging format string',
+ 'logging-too-few-args',
'Used when a logging format string is given too many arguments'),
}
diff --git a/checkers/misc.py b/checkers/misc.py
index e8918a0..18d7586 100644
--- a/checkers/misc.py
+++ b/checkers/misc.py
@@ -25,6 +25,7 @@ from pylint.checkers import BaseChecker
MSGS = {
'W0511': ('%s',
+ 'fixme',
'Used when a warning note as FIXME or XXX is detected.'),
}
diff --git a/checkers/newstyle.py b/checkers/newstyle.py
index 7bb146d..edadad8 100644
--- a/checkers/newstyle.py
+++ b/checkers/newstyle.py
@@ -24,13 +24,17 @@ from pylint.checkers.utils import check_messages
MSGS = {
'E1001': ('Use of __slots__ on an old style class',
+ 'slots-on-old-class',
'Used when an old style class uses the __slots__ attribute.'),
'E1002': ('Use of super on an old style class',
+ 'super-on-old-class',
'Used when an old style class uses the super builtin.'),
'E1003': ('Bad first argument %r given to super class',
+ 'bad-super-call',
'Used when another argument than the current class is given as \
first argument of the super builtin.'),
'W1001': ('Use of "property" on an old style class',
+ 'property-on-old-class',
'Used when PyLint detect the use of the builtin "property" \
on an old style class while this is relying on new style \
classes features'),
diff --git a/checkers/similar.py b/checkers/similar.py
index 8f88593..ed4f614 100644
--- a/checkers/similar.py
+++ b/checkers/similar.py
@@ -197,6 +197,7 @@ class LineSet:
MSGS = {'R0801': ('Similar lines in %s files\n%s',
+ 'duplicate-code',
'Indicates that a set of similar lines has been detected \
among multiple file. This usually means that the code should \
be refactored to avoid this duplication.')}
diff --git a/checkers/string_format.py b/checkers/string_format.py
index 62ccf51..d21ad3e 100644
--- a/checkers/string_format.py
+++ b/checkers/string_format.py
@@ -24,34 +24,43 @@ from pylint.checkers import utils
MSGS = {
'E1300': ("Unsupported format character %r (%#02x) at index %d",
+ "bad-format-character",
"Used when a unsupported format character is used in a format\
string."),
'E1301': ("Format string ends in middle of conversion specifier",
+ "truncated-format-string",
"Used when a format string terminates before the end of a \
conversion specifier."),
'E1302': ("Mixing named and unnamed conversion specifiers in format string",
+ "mixed-format-string",
"Used when a format string contains both named (e.g. '%(foo)d') \
and unnamed (e.g. '%d') conversion specifiers. This is also \
used when a named conversion specifier contains * for the \
minimum field width and/or precision."),
'E1303': ("Expected mapping for format string, not %s",
+ "format-needs-mapping",
"Used when a format string that uses named conversion specifiers \
is used with an argument that is not a mapping."),
'W1300': ("Format string dictionary key should be a string, not %s",
+ "bad-format-string-key",
"Used when a format string that uses named conversion specifiers \
is used with a dictionary whose keys are not all strings."),
'W1301': ("Unused key %r in format string dictionary",
+ "unused-format-string-key",
"Used when a format string that uses named conversion specifiers \
is used with a dictionary that conWtains keys not required by the \
format string."),
'E1304': ("Missing key %r in format string dictionary",
+ "missing-format-string-key",
"Used when a format string that uses named conversion specifiers \
is used with a dictionary that doesn't contain all the keys \
required by the format string."),
'E1305': ("Too many arguments for format string",
+ "too-many-format-args",
"Used when a format string that uses unnamed conversion \
specifiers is given too few arguments."),
'E1306': ("Not enough arguments for format string",
+ "too-few-format-args",
"Used when a format string that uses unnamed conversion \
specifiers is given too many arguments"),
}
diff --git a/checkers/typecheck.py b/checkers/typecheck.py
index db2f493..23bb7aa 100644
--- a/checkers/typecheck.py
+++ b/checkers/typecheck.py
@@ -28,33 +28,43 @@ from pylint.checkers.utils import safe_infer, is_super, check_messages
MSGS = {
'E1101': ('%s %r has no %r member',
+ 'no-member',
'Used when a variable is accessed for an unexistent member.'),
'E1102': ('%s is not callable',
+ 'not-callable',
'Used when an object being called has been inferred to a non \
callable object'),
'E1103': ('%s %r has no %r member (but some types could not be inferred)',
+ 'maybe-no-member',
'Used when a variable is accessed for an unexistent member, but \
astng was not able to interpret all possible types of this \
variable.'),
'E1111': ('Assigning to function call which doesn\'t return',
+ 'assignment-from-no-return',
'Used when an assignment is done on a function call but the \
inferred function doesn\'t return anything.'),
'W1111': ('Assigning to function call which only returns None',
+ 'assignment-from-none',
'Used when an assignment is done on a function call but the \
inferred function returns nothing but None.'),
'E1120': ('No value passed for parameter %s in function call',
+ 'no-value-for-parameter',
'Used when a function call passes too few arguments.'),
'E1121': ('Too many positional arguments for function call',
+ 'too-many-function-args',
'Used when a function call passes too many positional \
arguments.'),
'E1122': ('Duplicate keyword argument %r in function call',
+ 'duplicate-keyword-arg',
'Used when a function call passes the same keyword argument \
multiple times.'),
'E1123': ('Passing unexpected keyword argument %r in function call',
+ 'unexpected-keyword-arg',
'Used when a function call passes a keyword argument that \
doesn\'t correspond to one of the function\'s parameter names.'),
'E1124': ('Multiple values passed for parameter %r in function call',
+ 'redundant-keyword-arg',
'Used when a function call would result in assigning multiple \
values to a function parameter, one value from a positional \
argument and one from a keyword argument.'),
diff --git a/checkers/variables.py b/checkers/variables.py
index 3826ab9..e3812fc 100644
--- a/checkers/variables.py
+++ b/checkers/variables.py
@@ -54,48 +54,64 @@ def overridden_method(klass, name):
MSGS = {
'E0601': ('Using variable %r before assignment',
+ 'used-before-assignment',
'Used when a local variable is accessed before it\'s \
assignment.'),
'E0602': ('Undefined variable %r',
+ 'undefined-variable',
'Used when an undefined variable is accessed.'),
'E0603': ('Undefined variable name %r in __all__',
+ 'undefined-all-variable',
'Used when an undefined variable name is referenced in __all__.'),
'E0611': ('No name %r in module %r',
+ 'no-name-in-module',
'Used when a name cannot be found in a module.'),
'W0601': ('Global variable %r undefined at the module level',
+ 'global-variable-undefined',
'Used when a variable is defined through the "global" statement \
but the variable is not defined in the module scope.'),
'W0602': ('Using global for %r but no assignment is done',
+ 'global-variable-not-assigned',
'Used when a variable is defined through the "global" statement \
but no assignment to this variable is done.'),
'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 \
usage. That doesn\'t mean you can not use it !'),
'W0604': ('Using the global statement at the module level', # W0103
+ 'global-at-module-level',
'Used when you use the "global" statement at the module level \
since it has no effect'),
'W0611': ('Unused import %s',
+ 'unused-import',
'Used when an imported module or variable is not used.'),
'W0612': ('Unused variable %r',
+ 'unused-variable',
'Used when a variable is defined but not used.'),
'W0613': ('Unused argument %r',
+ 'unused-argument',
'Used when a function or method argument is not used.'),
'W0614': ('Unused import %s from wildcard import',
+ 'unused-wildcard-import',
'Used when an imported module or variable is not used from a \
\'from X import *\' style import.'),
'W0621': ('Redefining name %r from outer scope (line %s)',
+ 'redefined-outer-name',
'Used when a variable\'s name hide a name defined in the outer \
scope.'),
'W0622': ('Redefining built-in %r',
+ 'redefined-builtin',
'Used when a variable or function override a built-in.'),
'W0623': ('Redefining name %r from %s in exception handler',
+ 'redefine-in-handler',
'Used when an exception handler assigns the exception \
to an existing name'),
'W0631': ('Using possibly undefined loop variable %r',
+ 'undefined-loop-variable',
'Used when an loop variable (i.e. defined by a for loop or \
a list comprehension or a generator expression) is used outside \
the loop.'),
diff --git a/doc/FAQ.txt b/doc/FAQ.txt
index 56b17c2..992190a 100644
--- a/doc/FAQ.txt
+++ b/doc/FAQ.txt
@@ -243,6 +243,15 @@ It means that if you need to disable a lot of messages, you can use tricks like:
E0202, # I have a good reason, trust me
C0302 # that's it
+4.6 Do I have to remember all these numbers?
+--------------------------------------------
+
+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.
+
5. Classes and Inheritance
==========================
diff --git a/doc/manual.txt b/doc/manual.txt
index 17be0a0..98f6065 100644
--- a/doc/manual.txt
+++ b/doc/manual.txt
@@ -313,6 +313,8 @@ Other useful global options include:
--html=y_or_n Use HTML as output format instead of text.
--list-msgs Generate pylint's messages.
--full-documentation Generate pylint's full documentation, in reST format.
+--include_ids=y_or_n Show numeric ids of messages (like 'C0301')
+--symbols=y_or_n Show symbolic ids of messsages (like 'line-too-long')
.. _features: features.html
diff --git a/lint.py b/lint.py
index 6342577..86fd7a4 100644
--- a/lint.py
+++ b/lint.py
@@ -84,47 +84,59 @@ def _get_python_path(filepath):
MSGS = {
'F0001': ('%s',
+ 'fatal',
'Used when an error occurred preventing the analysis of a \
module (unable to find it for instance).'),
'F0002': ('%s: %s',
+ 'astng-error',
'Used when an unexpected error occurred while building the ASTNG \
representation. This is usually accompanied by a traceback. \
Please report such errors !'),
'F0003': ('ignored builtin module %s',
+ 'ignored-builtin-module',
'Used to indicate that the user asked to analyze a builtin module\
which has been skipped.'),
'F0004': ('unexpected inferred value %s',
+ 'unexpected-inferred-value',
'Used to indicate that some value of an unexpected type has been \
inferred.'),
'F0010': ('error while code parsing: %s',
+ 'parse-error',
'Used when an exception occured while building the ASTNG \
representation which could be handled by astng.'),
-
'I0001': ('Unable to run raw checkers on built-in module %s',
+ 'raw-checker-failed',
'Used to inform that a built-in module has not been checked \
using the raw checkers.'),
'I0010': ('Unable to consider inline option %r',
+ 'bad-inline-option',
'Used when an inline option is either badly formatted or can\'t \
be used inside modules.'),
'I0011': ('Locally disabling %s',
+ 'locally-disabled',
'Used when an inline option disables a message or a messages \
category.'),
'I0012': ('Locally enabling %s',
+ 'locally-enabled',
'Used when an inline option enables a message or a messages \
category.'),
'I0013': ('Ignoring entire file',
+ 'file-ignored',
'Used to inform that the file will not be checked'),
'E0001': ('%s',
+ 'syntax-error',
'Used when a syntax error is raised for a module.'),
'E0011': ('Unrecognized file option %r',
+ 'unrecognized-inline-option',
'Used when an unknown inline option is encountered.'),
'E0012': ('Bad option value %r',
+ 'bad-option-value',
'Used when a bad value for an inline option is encountered.'),
}
@@ -184,6 +196,12 @@ python modules names) to load, usually to register additional checkers.'}),
'group': 'Reports',
'help' : 'Include message\'s id in output'}),
+ ('symbols',
+ {'type' : 'yn', 'metavar' : '<y_or_n>', 'default' : 0,
+ 'short': 's',
+ 'group': 'Reports',
+ 'help' : 'Include symbolic ids of messages in output'}),
+
('files-output',
{'default': 0, 'type' : 'yn', 'metavar' : '<y_or_n>',
'group': 'Reports', 'level': 1,
@@ -483,6 +501,7 @@ This is used by the global evaluation report (RP0004).'}),
name.
"""
self.reporter.include_ids = self.config.include_ids
+ self.reporter.symbols = self.config.symbols
if not isinstance(files_or_modules, (list, tuple)):
files_or_modules = (files_or_modules,)
walker = PyLintASTWalker(self)
diff --git a/reporters/__init__.py b/reporters/__init__.py
index 8f54820..8be46b7 100644
--- a/reporters/__init__.py
+++ b/reporters/__init__.py
@@ -31,18 +31,38 @@ class EmptyReport(Exception):
"""raised when a report is empty and so should not be displayed"""
class BaseReporter:
- """base class for reporters"""
+ """base class for reporters
+
+ symbols: show short symbolic names for messages.
+ """
extension = ''
def __init__(self, output=None):
self.linter = None
self.include_ids = None
+ self.symbols = None
self.section = 0
self.out = None
self.out_encoding = None
self.set_output(output)
+ def make_sigle(self, msg_id):
+ """generate a short prefix for a message.
+
+ The sigle can include the id, the symbol, or both, or it can just be
+ the message class.
+ """
+ if self.include_ids:
+ sigle = msg_id
+ else:
+ sigle = msg_id[0]
+ if self.symbols:
+ symbol = self.linter.check_message_id(msg_id).symbol
+ if symbol:
+ sigle += '(%s)' % symbol
+ return sigle
+
def set_output(self, output=None):
"""set output stream"""
self.out = output or sys.stdout
diff --git a/reporters/guireporter.py b/reporters/guireporter.py
index 4e98fef..8fe6d53 100644
--- a/reporters/guireporter.py
+++ b/reporters/guireporter.py
@@ -22,11 +22,7 @@ class GUIReporter(BaseReporter):
def add_message(self, msg_id, location, msg):
"""manage message of different type and in the context of path"""
module, obj, line, col_offset = location[1:]
- if self.include_ids:
- sigle = msg_id
- else:
- sigle = msg_id[0]
-
+ sigle = self.make_sigle(msg_id)
full_msg = [sigle, module, obj, str(line), msg]
self.msgs += [[sigle, module, obj, str(line)]]
self.gui.msg_queue.put(full_msg)
diff --git a/reporters/html.py b/reporters/html.py
index 56efcd6..cac08b2 100644
--- a/reporters/html.py
+++ b/reporters/html.py
@@ -36,10 +36,7 @@ class HTMLReporter(BaseReporter):
def add_message(self, msg_id, location, msg):
"""manage message of different type and in the context of path"""
module, obj, line, col_offset = location[1:]
- if self.include_ids:
- sigle = msg_id
- else:
- sigle = msg_id[0]
+ sigle = self.make_sigle(msg_id)
self.msgs += [sigle, module, obj, str(line), str(col_offset), escape(msg)]
def set_output(self, output=None):
diff --git a/reporters/text.py b/reporters/text.py
index dd4d536..7b30c84 100644
--- a/reporters/text.py
+++ b/reporters/text.py
@@ -56,10 +56,7 @@ class TextReporter(BaseReporter):
self.writeln('************* %s' % module)
if obj:
obj = ':%s' % obj
- if self.include_ids:
- sigle = msg_id
- else:
- sigle = msg_id[0]
+ sigle = self.make_sigle(msg_id)
self.writeln('%s:%3s,%s%s: %s' % (sigle, line, col_offset, obj, msg))
def _display(self, layout):
@@ -88,10 +85,7 @@ class ParseableTextReporter(TextReporter):
path, _, obj, line, _ = location
if obj:
obj = ', %s' % obj
- if self.include_ids:
- sigle = msg_id
- else:
- sigle = msg_id[0]
+ sigle = self.make_sigle(msg_id)
if self._prefix:
path = path.replace(self._prefix, '')
self.writeln(self.line_format % locals())
@@ -145,10 +139,7 @@ class ColorizedTextReporter(TextReporter):
self._modules[module] = 1
if obj:
obj = ':%s' % obj
- if self.include_ids:
- sigle = msg_id
- else:
- sigle = msg_id[0]
+ sigle = self.make_sigle(msg_id)
color, style = self._get_decoration(sigle)
msg = colorize_ansi(msg, color, style)
sigle = colorize_ansi(sigle, color, style)
diff --git a/test/input/func_docstring.py b/test/input/func_docstring.py
index 2a92c87..01cd9e7 100644
--- a/test/input/func_docstring.py
+++ b/test/input/func_docstring.py
@@ -20,7 +20,7 @@ class AAAA:
## class BBBB:
## # missing docstring
## pass
-
+
## class CCCC:
## """yeah !"""
## def method1(self):
@@ -29,20 +29,23 @@ class AAAA:
## def method2(self):
## """ yeah !"""
## pass
-
+
def method1(self):
pass
-
+
def method2(self):
""" yeah !"""
pass
def __init__(self):
pass
-
+
class DDDD(AAAA):
"""yeah !"""
def __init__(self):
AAAA.__init__(self)
-
+
+# pylint: disable=missing-docstring
+def function4():
+ pass
diff --git a/test/unittest_lint.py b/test/unittest_lint.py
index 2ccc7c9..03e2d35 100644
--- a/test/unittest_lint.py
+++ b/test/unittest_lint.py
@@ -177,6 +177,36 @@ class PyLinterTC(TestCase):
self.assert_(linter.is_message_enabled('E1101', 75))
self.assert_(linter.is_message_enabled('E1101', 77))
+ def test_enable_by_symbol(self):
+ """messages can be controlled by symbolic names.
+
+ The state is consistent across symbols and numbers.
+ """
+ linter = self.linter
+ linter.open()
+ linter.set_current_module('toto')
+ self.assertTrue(linter.is_message_enabled('W0101'))
+ self.assertTrue(linter.is_message_enabled('unreachable'))
+ self.assertTrue(linter.is_message_enabled('W0102'))
+ self.assertTrue(linter.is_message_enabled('dangerous-default-value'))
+ linter.disable('unreachable', scope='package')
+ linter.disable('dangerous-default-value', scope='module', line=1)
+ self.assertFalse(linter.is_message_enabled('W0101'))
+ self.assertFalse(linter.is_message_enabled('unreachable'))
+ self.assertFalse(linter.is_message_enabled('W0102', 1))
+ self.assertFalse(linter.is_message_enabled('dangerous-default-value', 1))
+ linter.set_current_module('tutu')
+ self.assertFalse(linter.is_message_enabled('W0101'))
+ self.assertFalse(linter.is_message_enabled('unreachable'))
+ self.assertTrue(linter.is_message_enabled('W0102'))
+ self.assertTrue(linter.is_message_enabled('dangerous-default-value'))
+ linter.enable('unreachable', scope='package')
+ linter.enable('dangerous-default-value', scope='module', line=1)
+ self.assertTrue(linter.is_message_enabled('W0101'))
+ self.assertTrue(linter.is_message_enabled('unreachable'))
+ self.assertTrue(linter.is_message_enabled('W0102', 1))
+ self.assertTrue(linter.is_message_enabled('dangerous-default-value', 1))
+
def test_list_messages(self):
sys.stdout = StringIO()
try:
diff --git a/utils.py b/utils.py
index 9a1949a..09f3e22 100644
--- a/utils.py
+++ b/utils.py
@@ -19,6 +19,7 @@ main pylint class
"""
import sys
+from warnings import warn
from os import linesep
from os.path import dirname, basename, splitext, exists, isdir, join, normpath
@@ -93,7 +94,7 @@ def category_id(id):
class Message:
- def __init__(self, checker, msgid, msg, descr):
+ def __init__(self, checker, msgid, msg, descr, symbol):
assert len(msgid) == 5, 'Invalid message id %s' % msgid
assert msgid[0] in MSG_TYPES, \
'Bad message type %s in %r' % (msgid[0], msgid)
@@ -101,6 +102,7 @@ class Message:
self.msg = msg
self.descr = descr
self.checker = checker
+ self.symbol = symbol
class MessagesHandlerMixIn:
"""a mix-in class containing all the messages related methods for the main
@@ -110,6 +112,8 @@ class MessagesHandlerMixIn:
def __init__(self):
# dictionary of registered messages
self._messages = {}
+ # dictionary from string symbolic id to Message object.
+ self._messages_by_symbol = {}
self._msgs_state = {}
self._module_msgs_state = {} # None
self._msgs_by_category = {}
@@ -126,14 +130,27 @@ class MessagesHandlerMixIn:
"""
msgs_dict = checker.msgs
chkid = None
- for msgid, (msg, msgdescr) in msgs_dict.iteritems():
+ for msgid, msg_tuple in msgs_dict.iteritems():
+ if len(msg_tuple) == 3:
+ (msg, msgsymbol, msgdescr) = msg_tuple
+ assert msgsymbol not in self._messages_by_symbol, \
+ 'Message symbol %r is already defined' % msgsymbol
+ else:
+ # messages should have a symbol, but for backward compatibility
+ # they may not.
+ (msg, msgdescr) = msg_tuple
+ warn("[pylint 0.26] description of message %s doesn't include "
+ "a symbolic name" % msgid, DeprecationWarning)
+ msgsymbol = None
# avoid duplicate / malformed ids
assert msgid not in self._messages, \
'Message id %r is already defined' % msgid
assert chkid is None or chkid == msgid[1:3], \
'Inconsistent checker part in message id %r' % msgid
chkid = msgid[1:3]
- self._messages[msgid] = Message(checker, msgid, msg, msgdescr)
+ msg = Message(checker, msgid, msg, msgdescr, msgsymbol)
+ self._messages[msgid] = msg
+ self._messages_by_symbol[msgsymbol] = msg
self._msgs_by_category.setdefault(msgid[0], []).append(msgid)
def get_message_help(self, msgid, checkerref=False):
@@ -144,10 +161,14 @@ class MessagesHandlerMixIn:
desc += ' This message belongs to the %s checker.' % \
msg.checker.name
title = msg.msg
+ if msg.symbol:
+ symbol_part = ' (%s)' % msg.symbol
+ else:
+ symbol_part = ''
if title != '%s':
title = title.splitlines()[0]
- return ':%s: *%s*\n%s' % (msg.msgid, title, desc)
- return ':%s:\n%s' % (msg.msgid, desc)
+ return ':%s%s: *%s*\n%s' % (msg.msgid, symbol_part, title, desc)
+ return ':%s%s:\n%s' % (msg.msgid, symbol_part, desc)
def disable(self, msgid, scope='package', line=None):
"""don't output message of the given id"""
@@ -168,7 +189,7 @@ class MessagesHandlerMixIn:
if msgid.lower().startswith('rp'):
self.disable_report(msgid)
return
- # msgid is a msgid.
+ # msgid is a symbolic or numeric msgid.
msg = self.check_message_id(msgid)
if scope == 'module':
assert line > 0
@@ -205,7 +226,7 @@ class MessagesHandlerMixIn:
if msgid.lower().startswith('rp'):
self.enable_report(msgid)
return
- # msgid is a msgid.
+ # msgid is a symbolic or numeric msgid.
msg = self.check_message_id(msgid)
if scope == 'module':
assert line > 0
@@ -221,7 +242,14 @@ class MessagesHandlerMixIn:
self.config.enable = [mid for mid, val in msgs.iteritems() if val]
def check_message_id(self, msgid):
- """raise UnknownMessage if the message id is not defined"""
+ """returns the Message object for this message.
+
+ msgid may be either a numeric or symbolic id.
+
+ Raises UnknownMessage if the message id is not defined.
+ """
+ if msgid in self._messages_by_symbol:
+ return self._messages_by_symbol[msgid]
msgid = msgid.upper()
try:
return self._messages[msgid]
@@ -231,7 +259,11 @@ class MessagesHandlerMixIn:
def is_message_enabled(self, msgid, line=None):
"""return true if the message associated to the given message id is
enabled
+
+ msgid may be either a numeric or symbolic message id.
"""
+ if msgid in self._messages_by_symbol:
+ msgid = self._messages_by_symbol[msgid].msgid
if line is None:
return self._msgs_state.get(msgid, True)
try:
@@ -525,4 +557,3 @@ class PyLintASTWalker(object):
self.walk(child)
for cb in self.leave_events.get(cid, ()):
cb(astng)
-