summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSylvain Th?nault <sylvain.thenault@logilab.fr>2009-08-26 19:54:32 +0200
committerSylvain Th?nault <sylvain.thenault@logilab.fr>2009-08-26 19:54:32 +0200
commit3c2cad45b8b2770875f5f0c9298bfca415a1f2a2 (patch)
treef8dadec826c3e0beb65090d3e10e4b300dadf9a4
parent083dc9b481e4d4d86de6b07ed0a9a38e43a74a05 (diff)
downloadpylint-3c2cad45b8b2770875f5f0c9298bfca415a1f2a2.tar.gz
d-t-w, cleanup
-rw-r--r--checkers/classes.py79
-rw-r--r--lint.py199
-rw-r--r--setup.py2
-rw-r--r--utils.py44
4 files changed, 161 insertions, 163 deletions
diff --git a/checkers/classes.py b/checkers/classes.py
index a5e460f..b8bec95 100644
--- a/checkers/classes.py
+++ b/checkers/classes.py
@@ -17,7 +17,6 @@
"""
from __future__ import generators
-from logilab.common.compat import set
from logilab import astng
from logilab.astng.infutils import YES, Instance, are_exclusive
@@ -40,12 +39,12 @@ MSGS = {
'W0201': ('Attribute %r 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
'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',
'Used when a method which should have the bound instance as \
first argument has no argument defined.'),
@@ -60,7 +59,7 @@ MSGS = {
'C0203': ('Metaclass method should have "mcs" as first argument', # E0214
'Used when a metaclass method has an attribute different the \
"mcs" as first argument.'),
-
+
'W0211': ('Static method with %r as first argument',
'Used when a static method has "self" or "cls" as first argument.'
),
@@ -68,7 +67,7 @@ MSGS = {
'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',
'Used when a class claims to implement an interface which is not \
a class.'),
@@ -88,8 +87,8 @@ MSGS = {
'F0220': ('failed to resolve interfaces implemented by %s (%s)', # W0224
'Used when a PyLint as failed to find interfaces implemented by \
a class'),
-
-
+
+
'W0231': ('__init__ method from base class %r is not called',
'Used when an ancestor class method has an __init__ method \
which is not called by a derived class.'),
@@ -99,20 +98,20 @@ MSGS = {
'W0233': ('__init__ method from a non direct base class %r is called',
'Used when an __init__ method is called on a class which is not \
in the direct ancestors for the analysed class.'),
-
+
}
class ClassChecker(BaseChecker):
- """checks for :
- * methods without self as first argument
- * overridden methods signature
- * access only to existant members via self
- * attributes not defined in the __init__ method
- * supported interfaces implementation
- * unreachable code
+ """checks for :
+ * methods without self as first argument
+ * overridden methods signature
+ * access only to existant members via self
+ * attributes not defined in the __init__ method
+ * supported interfaces implementation
+ * unreachable code
"""
-
+
__implements__ = (IASTNGChecker,)
# configuration section name
@@ -138,7 +137,7 @@ class ClassChecker(BaseChecker):
separated by a comma. This is used for instance to not check methods defines \
in Zope\'s Interface base class.'}
),
-
+
('defining-attr-methods',
{'default' : ('__init__', '__new__', 'setUp'),
'type' : 'csv',
@@ -154,10 +153,10 @@ instance attributes.'}
self._accessed = []
self._first_attrs = []
self._meth_could_be_func = None
-
+
def visit_class(self, node):
"""init visit variable _accessed and check interfaces
- """
+ """
self._accessed.append({})
self._check_bases_classes(node)
self._check_interfaces(node)
@@ -167,7 +166,7 @@ instance attributes.'}
node.local_attr('__init__')
except astng.NotFoundError:
self.add_message('W0232', args=node, node=node)
-
+
def leave_class(self, cnode):
"""close a class node:
check that instance attributes are defined in __init__ and check
@@ -199,7 +198,7 @@ instance attributes.'}
accessed = self._accessed.pop()
if cnode.type != 'metaclass':
self._check_accessed_members(cnode, accessed)
-
+
def visit_function(self, node):
"""check method arguments, overriding"""
# ignore actual functions
@@ -207,7 +206,7 @@ instance attributes.'}
return
klass = node.parent.frame()
self._meth_could_be_func = True
- # check first argument is self if this is actually a method
+ # check first argument is self if this is actually a method
self._check_first_arg_for_type(node, klass.type == 'metaclass')
if node.name == '__init__':
self._check_init(node)
@@ -236,10 +235,10 @@ instance attributes.'}
self.add_message('E0202', args=overridden.name, node=node)
except astng.NotFoundError:
pass
-
+
def leave_function(self, node):
"""on method node, check if this method couldn't be a function
-
+
ignore class, static and abstract methods, initializer,
methods overridden from a parent class and any
kind of method defined in an interface for this warning
@@ -254,7 +253,7 @@ instance attributes.'}
overrides_a_method(class_node, node.name))
and class_node.type != 'interface'):
self.add_message('R0201', node=node)
-
+
def visit_getattr(self, node):
"""check if the getattr is an access to a class member
if so, register it. Also check for access to protected
@@ -263,7 +262,7 @@ instance attributes.'}
"""
attrname = node.attrname
if self._first_attrs and isinstance(node.expr, astng.Name) and \
- node.expr.name == self._first_attrs[-1]:
+ node.expr.name == self._first_attrs[-1]:
self._accessed[-1].setdefault(attrname, []).append(node)
elif attrname[0] == '_' and not attrname == '_' and not (
attrname.startswith('__') and attrname.endswith('__')):
@@ -281,11 +280,11 @@ instance attributes.'}
if klass is None or not (callee == klass.name or
callee in klass.basenames
or (isinstance(node.expr, astng.CallFunc)
- and isinstance(node.expr.func, astng.Name)
+ and isinstance(node.expr.func, astng.Name)
and node.expr.func.name == 'super')):
self.add_message('W0212', node=node, args=attrname)
-
-
+
+
def visit_name(self, node):
"""check if the name handle an access to a class member
if so, register it
@@ -293,7 +292,7 @@ instance attributes.'}
if self._first_attrs and (node.name == self._first_attrs[-1] or
not self._first_attrs[-1]):
self._meth_could_be_func = False
-
+
def _check_accessed_members(self, node, accessed):
"""check that accessed members are defined"""
# XXX refactor, probably much simpler now that E0201 is in type checker
@@ -316,7 +315,7 @@ instance attributes.'}
pass
# is it an instance attribute ?
try:
- defstmts = node.instance_attr(attr)
+ defstmts = node.instance_attr(attr)
except astng.NotFoundError:
pass
else:
@@ -331,10 +330,10 @@ instance attributes.'}
and not are_exclusive(_node.statement(), defstmt, ('AttributeError', 'Exception', 'BaseException')):
self.add_message('E0203', node=_node,
args=(attr, lno))
-
+
def _check_first_arg_for_type(self, node, metaclass=0):
"""check the name of first argument, expect:
-
+
* 'self' for a regular method
* 'cls' for a class method
* 'mcs' for a metaclass
@@ -372,14 +371,14 @@ instance attributes.'}
"""
for method in node.methods():
owner = method.parent.frame()
- if owner is node:
+ if owner is node:
continue
# owner is not this class, it must be a parent class
# check that the ancestor's method is not abstract
if method.is_abstract(pass_is_abstract=False):
self.add_message('W0223', node=node,
args=(method.name, owner.name))
-
+
def _check_interfaces(self, node):
"""check that the given class node really implements declared
interfaces
@@ -432,7 +431,7 @@ instance attributes.'}
"""check that the __init__ method call super or ancestors'__init__
method
"""
- klass_node = node.parent.frame()
+ klass_node = node.parent.frame()
to_call = _ancestors_to_call(klass_node)
for stmt in node.nodes_of_class(astng.CallFunc):
expr = stmt.func
@@ -461,7 +460,7 @@ instance attributes.'}
def _check_signature(self, method1, refmethod, class_type):
"""check that the signature of the two given methods match
-
+
class_type is in 'class', 'interface'
"""
if not (isinstance(method1, astng.Function)
@@ -476,7 +475,7 @@ instance attributes.'}
elif len(method1.args.defaults) < len(refmethod.args.defaults):
self.add_message('W0222', args=class_type, node=method1)
-
+
def _ancestors_to_call(klass_node, method='__init__'):
"""return a dictionary where keys are the list of base classes providing
the queried method, and so that should/may be called from the method node
@@ -489,7 +488,7 @@ def _ancestors_to_call(klass_node, method='__init__'):
except astng.NotFoundError:
continue
return to_call
-
+
def node_method(node, method_name):
"""get astng for <method_name> on the given class node, ensuring it
@@ -499,7 +498,7 @@ def node_method(node, method_name):
if isinstance(n, astng.Function):
return n
raise astng.NotFoundError(method_name)
-
+
def register(linter):
"""required method to auto register this checker """
linter.register_checker(ClassChecker(linter))
diff --git a/lint.py b/lint.py
index 1de4782..c4a6894 100644
--- a/lint.py
+++ b/lint.py
@@ -19,7 +19,7 @@
Check that a module satisfy a coding standard (and more !).
%prog --help
-
+
Display this help message and exit.
%prog --help-msg <msg-id>[,<msg-id>]
@@ -38,7 +38,7 @@ import tokenize
from logilab.common.configuration import UnsupportedAction, OptionsManagerMixIn, check_csv
from logilab.common.modutils import load_module_from_name
from logilab.common.interface import implements
-from logilab.common.textutils import get_csv
+from logilab.common.textutils import splitstrip
from logilab.common.fileutils import norm_open
from logilab.common.ureports import Table, Text
from logilab.common.__pkginfo__ import version as common_version
@@ -67,7 +67,7 @@ REPORTER_OPT_MAP = {'text': TextReporter,
'html': HTMLReporter,}
# Python Linter class #########################################################
-
+
MSGS = {
'F0001': ('%s',
'Used when an error occured preventing the analysis of a \
@@ -82,15 +82,15 @@ MSGS = {
'F0004': ('unexpected infered value %s',
'Used to indicate that some value of an unexpected type has been \
infered.'),
-
+
'I0001': ('Unable to run raw checkers on built-in module %s',
'Used to inform that a built-in module has not been checked \
using the raw checkers.'),
-
+
'I0010': ('Unable to consider inline option %r',
'Used when an inline option is either badly formatted or can\'t \
be used inside modules.'),
-
+
'I0011': ('Locally disabling %s',
'Used when an inline option disables a message or a messages \
category.'),
@@ -100,7 +100,7 @@ MSGS = {
'I0013': ('Ignoring entire file',
'Used to inform that the file will not be checked'),
-
+
'E0001': ('%s',
'Used when a syntax error is raised for a module.'),
@@ -112,54 +112,54 @@ MSGS = {
class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn,
BaseRawChecker):
- """lint Python modules using external checkers.
-
- This is the main checker controlling the other ones and the reports
- generation. It is itself both a raw checker and an astng checker in order
- to:
- * handle message activation / deactivation at the module level
+ """lint Python modules using external checkers.
+
+ This is the main checker controlling the other ones and the reports
+ generation. It is itself both a raw checker and an astng checker in order
+ to:
+ * handle message activation / deactivation at the module level
* handle some basic but necessary stats'data (number of classes, methods...)
"""
__implements__ = (ILinter, IRawChecker, IASTNGChecker)
-
+
name = 'master'
priority = 0
msgs = MSGS
may_be_disabled = False
-
+
options = (('ignore',
{'type' : 'csv', 'metavar' : '<file>',
'dest' : 'black_list', 'default' : ('CVS',),
'help' : 'Add <file or directory> to the black list. It \
should be a base name, not a path. You may set this option multiple times.'}),
-
+
('enable-checker',
{'type' : 'csv', 'metavar': '<checker ids>',
'group': 'Messages control',
'help' : 'Enable only checker(s) with the given id(s).\
This option conflicts with the disable-checker option'}),
-
+
('disable-checker',
{'type' : 'csv', 'metavar': '<checker ids>',
'group': 'Messages control',
'help' : 'Enable all checker(s) except those with the \
given id(s).\
This option conflicts with the enable-checker option'}),
-
+
('persistent',
{'default': True, 'type' : 'yn', 'metavar' : '<y_or_n>',
'help' : 'Pickle collected data for later comparisons.'}),
-
+
('cache-size',
{'default': 500, 'type' : 'int', 'metavar': '<size>',
'help' : 'Set the cache size for astng objects.'}),
-
+
('load-plugins',
{'type' : 'csv', 'metavar' : '<modules>', 'default' : (),
'help' : 'List of plugins (as comma separated values of \
python modules names) to load, usually to register additional checkers.'}),
-
+
('output-format',
{'default': 'text', 'type': 'choice', 'metavar' : '<format>',
'choices': ('text', 'parseable', 'msvs', 'colorized', 'html'),
@@ -173,32 +173,32 @@ python modules names) to load, usually to register additional checkers.'}),
'short': 'i',
'group': 'Reports',
'help' : 'Include message\'s id in output'}),
-
+
('files-output',
{'default': 0, 'type' : 'yn', 'metavar' : '<y_or_n>',
'group': 'Reports',
'help' : 'Put messages in a separate file for each module / \
package specified on the command line instead of printing them on stdout. \
Reports (if any) will be written in a file name "pylint_global.[txt|html]".'}),
-
+
('reports',
{'default': 1, 'type' : 'yn', 'metavar' : '<y_or_n>',
'short': 'r',
'group': 'Reports',
'help' : 'Tells wether to display a full report or only the\
messages'}),
-
+
('evaluation',
{'type' : 'string', 'metavar' : '<python_expression>',
'group': 'Reports',
'default': '10.0 - ((float(5 * error + warning + refactor + \
-convention) / statement) * 10)',
+convention) / statement) * 10)',
'help' : 'Python expression which should return a note less \
than 10 (10 is the highest note). You have access to the variables errors \
warning, statement which respectivly contain the number of errors / warnings\
messages and the total number of statements analyzed. This is used by the \
global evaluation report (R0004).'}),
-
+
('comment',
{'default': 0, 'type' : 'yn', 'metavar' : '<y_or_n>',
'group': 'Reports',
@@ -209,12 +209,12 @@ This is used by the global evaluation report (R0004).'}),
{'type' : 'csv', 'metavar': '<rpt ids>',
'group': 'Reports',
'help' : 'Enable the report(s) with the given id(s).'}),
-
+
('disable-report',
{'type' : 'csv', 'metavar': '<rpt ids>',
'group': 'Reports',
'help' : 'Disable the report(s) with the given id(s).'}),
-
+
('enable-msg-cat',
{'type' : 'string', 'metavar': '<msg cats>',
'group': 'Messages control',
@@ -224,23 +224,23 @@ This is used by the global evaluation report (R0004).'}),
{'type' : 'string', 'metavar': '<msg cats>', 'default': 'I',
'group': 'Messages control',
'help' : 'Disable all messages in the listed categories (IRCWEF).'}),
-
+
('enable-msg',
{'type' : 'csv', 'metavar': '<msg ids>',
'group': 'Messages control',
'help' : 'Enable the message(s) with the given id(s).'}),
-
+
('disable-msg',
{'type' : 'csv', 'metavar': '<msg ids>',
'group': 'Messages control',
'help' : 'Disable the message(s) with the given id(s).'}),
)
-
+
option_groups = (
('Messages control', 'Options controling analysis messages'),
('Reports', 'Options related to output formating and reporting'),
)
-
+
def __init__(self, options=(), reporter=None, option_groups=(),
pylintrc=None):
# some stuff has to be done before ancestors initialization...
@@ -248,7 +248,7 @@ This is used by the global evaluation report (R0004).'}),
# checkers / reporter / astng manager
self.reporter = None
self._checkers = {}
- self._ignore_file = False
+ self._ignore_file = False
# visit variables
self.base_name = None
self.base_file = None
@@ -287,7 +287,7 @@ This is used by the global evaluation report (R0004).'}),
self._dynamic_plugins = []
self.load_provider_defaults()
self.set_reporter(reporter or TextReporter(sys.stdout))
-
+
def load_plugin_modules(self, modnames):
"""take a list of module names which are pylint plugins and load
and register them
@@ -298,12 +298,12 @@ This is used by the global evaluation report (R0004).'}),
self._dynamic_plugins.append(modname)
module = load_module_from_name(modname)
module.register(self)
-
+
def set_reporter(self, reporter):
"""set the reporter used to display messages and reports"""
self.reporter = reporter
reporter.linter = self
-
+
def set_option(self, opt_name, value, action=None, opt_dict=None):
"""overridden from configuration.OptionsProviderMixin to handle some
special options
@@ -329,10 +329,11 @@ This is used by the global evaluation report (R0004).'}),
try:
BaseRawChecker.set_option(self, opt_name, value, action, opt_dict)
except UnsupportedAction:
- print >>sys.stderr, 'option %s can\'t be read from config file' % opt_name
+ print >> sys.stderr, 'option %s can\'t be read from config file' % \
+ opt_name
+
+ # checkers manipulation methods ############################################
- # checkers manipulation methods ###########################################
-
def register_checker(self, checker):
"""register a new checker
@@ -347,7 +348,7 @@ This is used by the global evaluation report (R0004).'}),
if hasattr(checker, 'msgs'):
self.register_messages(checker)
checker.load_defaults()
-
+
def enable_checkers(self, listed, enabled):
"""only enable/disable checkers from the given list"""
if enabled: # if we are activating a checker; deactivate them all first
@@ -361,7 +362,7 @@ This is used by the global evaluation report (R0004).'}),
except KeyError:
raise Exception('no checker named %s' % checkerid)
checker.enable(enabled)
-
+
def disable_noerror_checkers(self):
"""disable all checkers without error messages, and the
'miscellaneous' checker which can be safely deactivated in debug
@@ -380,11 +381,11 @@ This is used by the global evaluation report (R0004).'}),
break
else:
checker.enable(False)
-
+
# block level option handling #############################################
#
# see func_block_disable_msg.py test case for expected behaviour
-
+
def process_tokens(self, tokens):
"""process tokens from the current module to search for module/block
level options
@@ -408,25 +409,25 @@ This is used by the global evaluation report (R0004).'}),
opt, value = match.group(1).split('=', 1)
except ValueError:
self.add_message('I0010', args=match.group(1).strip(),
- line=start[0])
+ line=start[0])
continue
opt = opt.strip()
#line_num = start[0]
if opt in self._options_methods and not opt.endswith('-report'):
meth = self._options_methods[opt]
- for msgid in get_csv(value):
+ for msgid in splitstrip(value):
try:
meth(msgid, 'module', start[0])
except UnknownMessage:
self.add_message('E0012', args=msgid, line=start[0])
else:
self.add_message('E0011', args=opt, line=start[0])
-
+
def collect_block_lines(self, node, msg_state):
"""walk ast to collect block level options line numbers"""
# recurse on children (depth first)
for child in node.get_children():
- self.collect_block_lines(child, msg_state)
+ self.collect_block_lines(child, msg_state)
first = node.fromlineno
last = node.tolineno
# first child line number used to distinguate between disable-msg
@@ -443,7 +444,7 @@ This is used by the global evaluation report (R0004).'}),
# E1102 should be disabled from line 1 to 6 while E1101 from line 5 to 6
#
# this is necessary to disable locally messages applying to class /
- # function using their fromlineno
+ # function using their fromlineno
if isinstance(node, (nodes.Module, nodes.Class, nodes.Function)) and node.body:
firstchildlineno = node.body[0].fromlineno
else:
@@ -455,7 +456,7 @@ This is used by the global evaluation report (R0004).'}),
state = True
# set state for all lines for this block
first, last = node.block_range(lineno)
- for line in xrange(first, last+1):
+ for line in xrange(first, last+1):
# do not override existing entries
if not line in self._module_msgs_state.get(msgid, ()):
if line in lines: # state change in the same block
@@ -465,8 +466,8 @@ This is used by the global evaluation report (R0004).'}),
except KeyError:
self._module_msgs_state[msgid] = {line: state}
del lines[lineno]
-
-
+
+
# code checking methods ###################################################
def check(self, files_or_modules):
@@ -523,7 +524,7 @@ This is used by the global evaluation report (R0004).'}),
"""
if not modname and filepath is None:
return
- self.current_name = modname
+ self.current_name = modname
self.current_file = filepath or modname
self.stats['by_module'][modname] = {}
self.stats['by_module'][modname]['statement'] = 0
@@ -535,7 +536,7 @@ This is used by the global evaluation report (R0004).'}),
if modname:
self._module_msgs_state = {}
self._module_msg_cats_state = {}
-
+
def get_astng(self, filepath, modname):
"""return a astng representation for a module"""
try:
@@ -549,7 +550,7 @@ This is used by the global evaluation report (R0004).'}),
# import traceback
# traceback.print_exc()
self.add_message('F0002', args=(ex.__class__, ex))
-
+
def check_astng_module(self, astng, checkers):
"""check a module from its astng representation, real work"""
@@ -576,7 +577,7 @@ This is used by the global evaluation report (R0004).'}),
self.astng_events(astng, [checker for checker in checkers
if implements(checker, IASTNGChecker)])
return True
-
+
def astng_events(self, astng, checkers, _reversed_checkers=None):
"""generate event to astng checkers according to the current astng
node and recurse on its children
@@ -594,10 +595,10 @@ This is used by the global evaluation report (R0004).'}),
self.astng_events(child, checkers, _reversed_checkers)
for checker in _reversed_checkers:
checker.leave(astng)
-
+
# IASTNGChecker interface #################################################
-
+
def open(self):
"""initialize counters"""
self.stats = { 'by_module' : {},
@@ -606,10 +607,10 @@ This is used by the global evaluation report (R0004).'}),
}
for msg_cat in MSG_TYPES.values():
self.stats[msg_cat] = 0
-
+
def close(self):
"""close the whole package /module, it's time to make reports !
-
+
if persistent run, pickle results for later comparison
"""
if self.base_name is not None:
@@ -620,9 +621,9 @@ This is used by the global evaluation report (R0004).'}),
# save results if persistent run
if self.config.persistent:
config.save_results(self.stats, self.base_name)
-
+
# specific reports ########################################################
-
+
def report_evaluation(self, sect, stats, old_stats):
"""make the global evaluation report"""
# check with at least check 1 statements (usually 0 when there is a
@@ -674,7 +675,7 @@ def report_messages_by_module_stats(sect, stats, _):
if len(stats['by_module']) == 1:
# don't print this report when we are analysing a single module
raise EmptyReport()
- by_mod = {}
+ by_mod = {}
for m_type in ('fatal', 'error', 'warning', 'refactor', 'convention'):
total = stats[m_type]
for module in stats['by_module'].keys():
@@ -683,7 +684,7 @@ def report_messages_by_module_stats(sect, stats, _):
percent = 0
else:
percent = float((mod_total)*100) / total
- by_mod.setdefault(module, {})[m_type] = percent
+ by_mod.setdefault(module, {})[m_type] = percent
sorted_result = []
for module, mod_info in by_mod.items():
sorted_result.append((mod_info['error'],
@@ -719,7 +720,7 @@ except AttributeError:
def preprocess_options(args, search_for):
"""look for some options (keys of <search_for>) which have to be processed
before others
-
+
values of <search_for> are callback functions to call when the option is
found
"""
@@ -742,10 +743,10 @@ def preprocess_options(args, search_for):
i += 1
else:
i += 1
-
+
class Run:
"""helper class to use as main for pylint :
-
+
run(*sys.argv[1:])
"""
LinterClass = PyLinter
@@ -753,7 +754,7 @@ class Run:
('Commands', 'Options which are actually commands. Options in this \
group are mutually exclusive.'),
)
-
+
def __init__(self, args, reporter=None):
self._rcfile = None
self._plugins = []
@@ -767,7 +768,7 @@ group are mutually exclusive.'),
{'action' : 'callback', 'callback' : lambda *args: 1,
'type': 'string', 'metavar': '<file>',
'help' : 'Specify a configuration file.'}),
-
+
('init-hook',
{'action' : 'callback', 'type' : 'string', 'metavar': '<code>',
'callback' : cb_init_hook,
@@ -793,19 +794,19 @@ exit. The value may be a comma separated list of message ids.'''}),
'help' : '''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.'''}),
-
+
('generate-man',
{'action' : 'callback', 'callback' : self.cb_generate_manpage,
'group': 'Commands',
'help' : "Generate pylint's man page.",'hide': 'True'}),
-
+
('errors-only',
{'action' : 'callback', 'callback' : self.cb_error_mode,
- 'short': 'e',
+ 'short': 'e',
'help' : '''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'''}),
-
+
('profile',
{'type' : 'yn', 'metavar' : '<y_or_n>',
'default': False,
@@ -821,26 +822,26 @@ are done by default'''}),
# add some help section
linter.add_help_section('Environment variables', config.ENV_HELP)
linter.add_help_section('Output', '''
-Using the default text output, the message format is :
- MESSAGE_TYPE: LINE_NUM:[OBJECT:] MESSAGE
-There are 5 kind of message types :
- * (C) convention, for programming standard violation
- * (R) refactor, for bad code smell
- * (W) warning, for python specific problems
- * (E) error, for probable bugs in the code
+Using the default text output, the message format is :
+ MESSAGE_TYPE: LINE_NUM:[OBJECT:] MESSAGE
+There are 5 kind of message types :
+ * (C) convention, for programming standard violation
+ * (R) refactor, for bad code smell
+ * (W) warning, for python specific problems
+ * (E) error, for probable bugs in the code
* (F) fatal, if an error occured which prevented pylint from doing further \
-processing.
+processing.
''')
linter.add_help_section('Output status code', '''
-Pylint should leave with following status code:
- * 0 if everything went fine
- * 1 if some fatal message issued
- * 2 if some error message issued
- * 4 if some warning message issued
- * 8 if some refactor message issued
- * 16 if some convention message issued
- * 32 on usage error
-
+Pylint should leave with following status code:
+ * 0 if everything went fine
+ * 1 if some fatal message issued
+ * 2 if some error message issued
+ * 4 if some warning message issued
+ * 8 if some refactor message issued
+ * 16 if some convention message issued
+ * 32 on usage error
+
status 1 to 16 will be bit-ORed so you can know which different categories has
been issued by analysing pylint output status code
''')
@@ -850,7 +851,7 @@ been issued by analysing pylint output status code
# is there some additional plugins in the file configuration, in
config_parser = linter._config_parser
if config_parser.has_option('MASTER', 'load-plugins'):
- plugins = get_csv(config_parser.get('MASTER', 'load-plugins'))
+ plugins = splitstrip(config_parser.get('MASTER', 'load-plugins'))
linter.load_plugin_modules(plugins)
# now we can load file config and command line, plugins (which can
# provide options) have been registered
@@ -885,14 +886,14 @@ been issued by analysing pylint output status code
def cb_set_rcfile(self, name, value):
"""callback for option preprocessing (ie before optik parsing)"""
self._rcfile = value
-
+
def cb_add_plugins(self, name, value):
"""callback for option preprocessing (ie before optik parsing)"""
- self._plugins.extend(get_csv(value))
-
+ self._plugins.extend(splitstrip(value))
+
def cb_error_mode(self, *args, **kwargs):
"""error mode:
- * checkers without error messages are disabled
+ * checkers without error messages are disabled
* for others, only the ERROR messages are displayed
* disable reports
* do not save execution information
@@ -901,23 +902,23 @@ been issued by analysing pylint output status code
self.linter.set_option('disable-msg-cat', 'WCRFI')
self.linter.set_option('reports', False)
self.linter.set_option('persistent', False)
-
+
def cb_generate_config(self, *args, **kwargs):
"""optik callback for sample config file generation"""
self.linter.generate_config(skipsections=('COMMANDS',))
sys.exit(0)
-
+
def cb_generate_manpage(self, *args, **kwargs):
"""optik callback for sample config file generation"""
from pylint import __pkginfo__
self.linter.generate_manpage(__pkginfo__)
sys.exit(0)
-
+
def cb_help_message(self, option, opt_name, value, parser):
"""optik callback for printing some help about a particular message"""
- self.linter.help_message(get_csv(value))
+ self.linter.help_message(splitstrip(value))
sys.exit(0)
-
+
def cb_list_messages(self, option, opt_name, value, parser):
"""optik callback for printing available messages"""
self.linter.list_messages()
diff --git a/setup.py b/setup.py
index 7ceba7c..4ed750a 100644
--- a/setup.py
+++ b/setup.py
@@ -19,11 +19,9 @@ try:
if os.environ.get('NO_SETUPTOOLS'):
raise ImportError()
from setuptools import setup
- from setuptools.command import install_lib
USE_SETUPTOOLS = 1
except ImportError:
from distutils.core import setup
- from distutils.command import install_lib
USE_SETUPTOOLS = 0
#assert USE_SETUPTOOLS
diff --git a/utils.py b/utils.py
index 3257e84..a91d8c6 100644
--- a/utils.py
+++ b/utils.py
@@ -94,12 +94,12 @@ class Message:
self.msg = msg
self.descr = descr
self.checker = checker
-
+
class MessagesHandlerMixIn:
"""a mix-in class containing all the messages related methods for the main
lint class
"""
-
+
def __init__(self):
# dictionary of registered messages
self._messages = {}
@@ -108,7 +108,7 @@ class MessagesHandlerMixIn:
self._msg_cats_state = {}
self._module_msg_cats_state = None
self.msg_status = 0
-
+
def register_messages(self, checker):
"""register a dictionary of messages
@@ -154,14 +154,14 @@ class MessagesHandlerMixIn:
self._module_msgs_state[msg.msgid] = {line: False}
if msg_id != 'I0011':
self.add_message('I0011', line=line, args=msg.msgid)
-
+
else:
msgs = self._msgs_state
msgs[msg.msgid] = False
# sync configuration object
self.config.disable_msg = [mid for mid, val in msgs.items()
- if not val]
-
+ if not val]
+
def enable_message(self, msg_id, scope='package', line=None):
"""reenable message of the given id"""
assert scope in ('package', 'module')
@@ -177,7 +177,7 @@ class MessagesHandlerMixIn:
else:
msgs = self._msgs_state
msgs[msg.msgid] = True
- # sync configuration object
+ # sync configuration object
self.config.enable_msg = [mid for mid, val in msgs.items() if val]
def _cat_ids(self, categories):
@@ -186,7 +186,7 @@ class MessagesHandlerMixIn:
if not catid in MSG_TYPES:
raise Exception('Unknown category identifier %s' % catid)
yield catid
-
+
def disable_message_category(self, categories, scope='package', line=None):
"""don't output message in the given category"""
assert scope in ('package', 'module')
@@ -196,7 +196,7 @@ class MessagesHandlerMixIn:
self._module_msg_cats_state[catid] = False
else:
self._msg_cats_state[catid] = False
-
+
def enable_message_category(self, categories, scope='package', line=None):
"""reenable message of the given category"""
assert scope in ('package', 'module')
@@ -206,7 +206,7 @@ class MessagesHandlerMixIn:
self._module_msg_cats_state[catid] = True
else:
self._msg_cats_state[catid] = True
-
+
def check_message_id(self, msg_id):
"""raise UnknownMessage if the message id is not defined"""
msg_id = msg_id.upper()
@@ -231,12 +231,12 @@ class MessagesHandlerMixIn:
return self._module_msgs_state[msg_id][line]
except (KeyError, TypeError):
return self._msgs_state.get(msg_id, True)
-
+
def add_message(self, msg_id, line=None, node=None, args=None):
"""add the message corresponding to the given id.
If provided, msg is expanded using args
-
+
astng checkers should provide the node argument, raw checkers should
provide the line argument.
"""
@@ -244,7 +244,7 @@ class MessagesHandlerMixIn:
line = node.fromlineno
# should this message be displayed
if not self.is_message_enabled(msg_id, line):
- return
+ return
# update stats
msg_cat = MSG_TYPES[msg_id[0]]
self.msg_status |= MSG_TYPES_STATUS[msg_id[0]]
@@ -278,7 +278,7 @@ class MessagesHandlerMixIn:
print ex
print
continue
-
+
def list_messages(self):
"""output a full documentation in ReST format"""
for checker in sort_checkers(self._checkers.values()):
@@ -324,7 +324,7 @@ class MessagesHandlerMixIn:
print ':%s: %s' % report[:2]
print
print
-
+
class ReportsHandlerMixIn:
"""a mix-in class containing all the reports and stats manipulation
@@ -333,10 +333,10 @@ class ReportsHandlerMixIn:
def __init__(self):
self._reports = {}
self._reports_state = {}
-
+
def register_report(self, r_id, r_title, r_cb, checker):
"""register a report
-
+
r_id is the unique identifier for the report
r_title the report's title
r_cb the method to call to make the report
@@ -344,23 +344,23 @@ class ReportsHandlerMixIn:
"""
r_id = r_id.upper()
self._reports.setdefault(checker, []).append( (r_id, r_title, r_cb) )
-
+
def enable_report(self, r_id):
"""disable the report of the given id"""
r_id = r_id.upper()
self._reports_state[r_id] = True
-
+
def disable_report(self, r_id):
"""disable the report of the given id"""
r_id = r_id.upper()
self._reports_state[r_id] = False
-
+
def is_report_enabled(self, r_id):
"""return true if the report associated to the given identifier is
enabled
"""
return self._reports_state.get(r_id, True)
-
+
def make_reports(self, stats, old_stats):
"""render registered reports"""
if self.config.files_output:
@@ -382,7 +382,7 @@ class ReportsHandlerMixIn:
report_sect.report_id = r_id
sect.append(report_sect)
self.reporter.display_results(sect)
-
+
def add_stats(self, **kwargs):
"""add some stats entries to the statistic dictionary
raise an AssertionError if there is a key conflict