diff options
author | Brett Cannon <brett@python.org> | 2014-08-29 11:16:29 -0400 |
---|---|---|
committer | Brett Cannon <brett@python.org> | 2014-08-29 11:16:29 -0400 |
commit | 29bb89fe46c7b65df777e626cac5611961a9d325 (patch) | |
tree | ac54ba8376a81bc20498fa2da6db810d06be9ac6 | |
parent | 41bd5fd5d79925b41e72add687ede9a065f6de10 (diff) | |
download | pylint-git-29bb89fe46c7b65df777e626cac5611961a9d325.tar.gz |
Modernize to the point of working for Python 2.7 still
--HG--
branch : python_6
-rw-r--r-- | __pkginfo__.py | 6 | ||||
-rw-r--r-- | checkers/base.py | 20 | ||||
-rw-r--r-- | checkers/classes.py | 13 | ||||
-rw-r--r-- | checkers/exceptions.py | 6 | ||||
-rw-r--r-- | checkers/format.py | 9 | ||||
-rw-r--r-- | checkers/imports.py | 14 | ||||
-rw-r--r-- | checkers/logging.py | 2 | ||||
-rw-r--r-- | checkers/misc.py | 2 | ||||
-rw-r--r-- | checkers/newstyle.py | 2 | ||||
-rw-r--r-- | checkers/similar.py | 24 | ||||
-rw-r--r-- | checkers/strings.py | 8 | ||||
-rw-r--r-- | checkers/utils.py | 6 | ||||
-rw-r--r-- | checkers/variables.py | 17 | ||||
-rw-r--r-- | config.py | 7 | ||||
-rwxr-xr-x | epylint.py | 7 | ||||
-rw-r--r-- | gui.py | 5 | ||||
-rw-r--r-- | lint.py | 19 | ||||
-rw-r--r-- | reporters/__init__.py | 3 | ||||
-rw-r--r-- | reporters/text.py | 3 | ||||
-rw-r--r-- | setup.py | 12 | ||||
-rw-r--r-- | testutils.py | 14 | ||||
-rw-r--r-- | tox.ini | 4 | ||||
-rw-r--r-- | utils.py | 30 |
23 files changed, 124 insertions, 109 deletions
diff --git a/__pkginfo__.py b/__pkginfo__.py index 757bfe94b..255c6c1f8 100644 --- a/__pkginfo__.py +++ b/__pkginfo__.py @@ -22,11 +22,9 @@ modname = distname = 'pylint' numversion = (1, 3, 0) version = '.'.join([str(num) for num in numversion]) +install_requires = ['logilab-common >= 0.53.0', 'astroid >= 1.2', 'six'] if sys.version_info < (2, 6): - install_requires = ['logilab-common >= 0.53.0', 'astroid >= 1.2', - 'StringFormat'] -else: - install_requires = ['logilab-common >= 0.53.0', 'astroid >= 1.2'] + install_requires.append('StringFormat') license = 'GPL' description = "python code static checker" diff --git a/checkers/base.py b/checkers/base.py index 298ac5f55..b5e96fb05 100644 --- a/checkers/base.py +++ b/checkers/base.py @@ -43,6 +43,8 @@ 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]+$') @@ -175,7 +177,7 @@ def decorated_with_abc(func): if func.decorators: for node in func.decorators.nodes: try: - infered = node.infer().next() + infered = next(node.infer()) except InferenceError: continue if infered and infered.qname() in ABC_METHODS: @@ -380,7 +382,7 @@ class BasicErrorChecker(_BasicChecker): abc.ABCMeta as metaclass. """ try: - infered = node.func.infer().next() + infered = next(node.func.infer()) except astroid.InferenceError: return if not isinstance(infered, astroid.Class): @@ -653,7 +655,7 @@ functions, methods # ordinary_args[i].name == call.args[i].name. if len(ordinary_args) != len(call.args): return - for i in xrange(len(ordinary_args)): + for i in range(len(ordinary_args)): if not isinstance(call.args[i], astroid.Name): return if node.args.args[i].name != call.args[i].name: @@ -674,7 +676,7 @@ functions, methods # check for dangerous default values as arguments for default in node.args.defaults: try: - value = default.infer().next() + value = next(default.infer()) except astroid.InferenceError: continue @@ -840,7 +842,7 @@ functions, methods # Try to see if we have iter(). if isinstance(node.args[0], astroid.CallFunc): try: - func = node.args[0].func.infer().next() + func = next(node.args[0].func.infer()) except InferenceError: return if (getattr(func, 'name', None) == 'iter' and @@ -893,7 +895,7 @@ _NAME_TYPES = { def _create_naming_options(): name_options = [] - for name_type, (rgx, human_readable_name) in _NAME_TYPES.iteritems(): + for name_type, (rgx, human_readable_name) in six.iteritems(_NAME_TYPES): name_type = name_type.replace('_', '-') name_options.append(( '%s-rgx' % (name_type,), @@ -968,12 +970,12 @@ class NameChecker(_BasicChecker): self._bad_names = {} def leave_module(self, node): - for category, all_groups in self._bad_names.iteritems(): + for category, all_groups in six.iteritems(self._bad_names): if len(all_groups) < 2: continue groups = collections.defaultdict(list) min_warnings = sys.maxint - for group in all_groups.itervalues(): + for group in six.itervalues(all_groups): groups[len(group)].append(group) min_warnings = min(len(group), min_warnings) if len(groups[min_warnings]) > 1: @@ -988,7 +990,7 @@ class NameChecker(_BasicChecker): @check_messages('blacklisted-name', 'invalid-name') def visit_class(self, node): self._check_name('class', node.name, node) - for attr, anodes in node.instance_attrs.iteritems(): + for attr, anodes in six.iteritems(node.instance_attrs): if not list(node.instance_attr_ancestors(attr)): self._check_name('attr', attr, anodes[0]) diff --git a/checkers/classes.py b/checkers/classes.py index aa0555876..d9b758122 100644 --- a/checkers/classes.py +++ b/checkers/classes.py @@ -28,6 +28,7 @@ from pylint.checkers import BaseChecker from pylint.checkers.utils import ( PYMETHODS, overrides_a_method, check_messages, is_attr_private, is_attr_protected, node_frame_class, safe_infer, is_builtin_object) +import six if sys.version_info >= (3, 0): NEXT_METHOD = '__next__' @@ -298,7 +299,7 @@ a metaclass class method.'} return defining_methods = self.config.defining_attr_methods current_module = cnode.root() - for attr, nodes in cnode.instance_attrs.iteritems(): + for attr, nodes in six.iteritems(cnode.instance_attrs): # skip nodes which are not in the current module and it may screw up # the output, while it's not worth it nodes = [n for n in nodes if not @@ -612,7 +613,7 @@ a metaclass class method.'} 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 - for attr, nodes in accessed.iteritems(): + for attr, nodes in six.iteritems(accessed): # deactivate "except doesn't do anything", that's expected # pylint: disable=W0704 try: @@ -624,7 +625,7 @@ a metaclass class method.'} pass # is it an instance attribute of a parent class ? try: - node.instance_attr_ancestors(attr).next() + next(node.instance_attr_ancestors(attr)) # yes, stop here continue except StopIteration: @@ -816,7 +817,7 @@ a metaclass class method.'} expr.expr.func.name == 'super': return try: - klass = expr.expr.infer().next() + klass = next(expr.expr.infer()) if klass is YES: continue # The infered klass can be super(), which was @@ -838,7 +839,7 @@ a metaclass class method.'} node=expr, args=klass.name) except astroid.InferenceError: continue - for klass, method in not_called_yet.iteritems(): + for klass, method in six.iteritems(not_called_yet): if klass.name == 'object' or method.parent.name == 'object': continue self.add_message('super-init-not-called', args=klass.name, node=node) @@ -880,7 +881,7 @@ def _ancestors_to_call(klass_node, method='__init__'): to_call = {} for base_node in klass_node.ancestors(recurs=False): try: - to_call[base_node] = base_node.igetattr(method).next() + to_call[base_node] = next(base_node.igetattr(method)) except astroid.InferenceError: continue return to_call diff --git a/checkers/exceptions.py b/checkers/exceptions.py index 9b1a071d2..f9b78bbe2 100644 --- a/checkers/exceptions.py +++ b/checkers/exceptions.py @@ -67,7 +67,7 @@ def infer_bases(klass): """ for base in klass.bases: try: - inferit = base.infer().next() + inferit = next(base.infer()) except astroid.InferenceError: continue if inferit is YES: @@ -178,7 +178,7 @@ class ExceptionsChecker(BaseChecker): return if PY3K and node.cause: try: - cause = node.cause.infer().next() + cause = next(node.cause.infer()) except astroid.InferenceError: pass else: @@ -197,7 +197,7 @@ class ExceptionsChecker(BaseChecker): return else: try: - value = unpack_infer(expr).next() + value = next(unpack_infer(expr)) except astroid.InferenceError: return self._check_raise_value(node, value) diff --git a/checkers/format.py b/checkers/format.py index 34da6530a..073eadf6f 100644 --- a/checkers/format.py +++ b/checkers/format.py @@ -24,6 +24,9 @@ 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 +import six +from six.moves import zip if not hasattr(tokenize, 'NL'): raise ValueError("tokenize.NL doesn't exist -- tokenize module too old") @@ -506,7 +509,7 @@ class FormatChecker(BaseTokenChecker): keyword_token = tokens[start][1] line_num = tokens[start][2][0] - for i in xrange(start, len(tokens) - 1): + for i in range(start, len(tokens) - 1): token = tokens[i] # If we hit a newline, then assume any parens were for continuation. @@ -812,7 +815,7 @@ class FormatChecker(BaseTokenChecker): for indent_pos, state, offsets in self._current_line.retained_warnings: block_type = offsets[tokens.start_col(indent_pos)] - hints = dict((k, v) for k, v in offsets.iteritems() + hints = dict((k, v) for k, v in six.iteritems(offsets) if v != block_type) if single_line_block_stmt and block_type == WITH_BODY: self._add_continuation_message(state, hints, tokens, indent_pos) @@ -886,7 +889,7 @@ class FormatChecker(BaseTokenChecker): tolineno = node.tolineno assert tolineno, node lines = [] - for line in xrange(line, tolineno + 1): + for line in range(line, tolineno + 1): self._visited_lines[line] = 1 try: lines.append(self._lines[line].rstrip()) diff --git a/checkers/imports.py b/checkers/imports.py index a861ce63f..4369b027a 100644 --- a/checkers/imports.py +++ b/checkers/imports.py @@ -28,6 +28,8 @@ 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): """ @@ -106,14 +108,14 @@ def dependencies_graph(filename, dep_info): done = {} printer = DotBackend(filename[:-4], rankdir='LR') printer.emit('URL="." node[shape="box"]') - for modname, dependencies in sorted(dep_info.iteritems()): + for modname, dependencies in sorted(six.iteritems(dep_info)): done[modname] = 1 printer.emit_node(modname) for modname in dependencies: if modname not in done: done[modname] = 1 printer.emit_node(modname) - for depmodname, dependencies in sorted(dep_info.iteritems()): + for depmodname, dependencies in sorted(six.iteritems(dep_info)): for modname in dependencies: printer.emit_edge(modname, depmodname) printer.generate(filename) @@ -281,7 +283,7 @@ given file (report RP0402 must not be disabled)'} def get_imported_module(self, modnode, importnode, modname): try: return importnode.do_import_module(modname) - except astroid.InferenceError, ex: + except astroid.InferenceError as ex: if str(ex) != modname: args = '%r (%s)' % (modname, ex) else: @@ -353,7 +355,7 @@ given file (report RP0402 must not be disabled)'} def report_external_dependencies(self, sect, _, dummy): """return a verbatim layout for displaying dependencies""" - dep_info = make_tree_defs(self._external_dependencies_info().iteritems()) + dep_info = make_tree_defs(six.iteritems(self._external_dependencies_info())) if not dep_info: raise EmptyReport() tree_str = repr_tree_defs(dep_info) @@ -385,7 +387,7 @@ given file (report RP0402 must not be disabled)'} if self.__ext_dep_info is None: package = self.linter.current_name self.__ext_dep_info = result = {} - for importee, importers in self.stats['dependencies'].iteritems(): + for importee, importers in six.iteritems(self.stats['dependencies']): if not importee.startswith(package): result[importee] = importers return self.__ext_dep_info @@ -397,7 +399,7 @@ given file (report RP0402 must not be disabled)'} if self.__int_dep_info is None: package = self.linter.current_name self.__int_dep_info = result = {} - for importee, importers in self.stats['dependencies'].iteritems(): + for importee, importers in six.iteritems(self.stats['dependencies']): if importee.startswith(package): result[importee] = importers return self.__int_dep_info diff --git a/checkers/logging.py b/checkers/logging.py index d82d74b58..a5b38d884 100644 --- a/checkers/logging.py +++ b/checkers/logging.py @@ -178,7 +178,7 @@ class LoggingChecker(checkers.BaseChecker): # Keyword checking on logging strings is complicated by # special keywords - out of scope. return - except utils.UnsupportedFormatCharacter, ex: + except utils.UnsupportedFormatCharacter as ex: char = format_string[ex.index] self.add_message('logging-unsupported-format', node=node, args=(char, ord(char), ex.index)) diff --git a/checkers/misc.py b/checkers/misc.py index b27b86ae6..1511e60a7 100644 --- a/checkers/misc.py +++ b/checkers/misc.py @@ -73,7 +73,7 @@ class EncodingChecker(BaseChecker): def _check_encoding(self, lineno, line, file_encoding): try: return unicode(line, file_encoding) - except UnicodeDecodeError, ex: + except UnicodeDecodeError as ex: self.add_message('invalid-encoded-data', line=lineno, args=(file_encoding, ex.args[2])) diff --git a/checkers/newstyle.py b/checkers/newstyle.py index 1946f94ff..335b05291 100644 --- a/checkers/newstyle.py +++ b/checkers/newstyle.py @@ -133,7 +133,7 @@ class NewStyleConflictChecker(BaseChecker): continue try: - supcls = (call.args and call.args[0].infer().next() + supcls = (call.args and next(call.args[0].infer()) or None) except astroid.InferenceError: continue diff --git a/checkers/similar.py b/checkers/similar.py index e5c930038..f563a1e1a 100644 --- a/checkers/similar.py +++ b/checkers/similar.py @@ -16,6 +16,7 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. """a similarities / code duplication command line tool and pylint checker """ +from __future__ import print_function import sys from itertools import izip @@ -23,6 +24,7 @@ from logilab.common.ureports import Table from pylint.interfaces import IRawChecker from pylint.checkers import BaseChecker, table_lines_from_stats +import six class Similar(object): @@ -69,7 +71,7 @@ class Similar(object): else: duplicate.append(set([(lineset1, idx1), (lineset2, idx2)])) sims = [] - for num, ensembles in no_duplicates.iteritems(): + for num, ensembles in six.iteritems(no_duplicates): for couples in ensembles: sims.append((num, couples)) sims.sort() @@ -80,19 +82,19 @@ class Similar(object): """display computed similarities on stdout""" nb_lignes_dupliquees = 0 for num, couples in sims: - print - print num, "similar lines in", len(couples), "files" + print() + print(num, "similar lines in", len(couples), "files") couples = sorted(couples) for lineset, idx in couples: - print "==%s:%s" % (lineset.name, idx) + print("==%s:%s" % (lineset.name, idx)) # pylint: disable=W0631 for line in lineset._real_lines[idx:idx+num]: - print " ", line.rstrip() + print(" ", line.rstrip()) nb_lignes_dupliquees += num * (len(couples)-1) nb_total_lignes = sum([len(lineset) for lineset in self.linesets]) - print "TOTAL lines=%s duplicates=%s percent=%.2f" \ + print("TOTAL lines=%s duplicates=%s percent=%.2f" \ % (nb_total_lignes, nb_lignes_dupliquees, - nb_lignes_dupliquees*100. / nb_total_lignes) + nb_lignes_dupliquees*100. / nb_total_lignes)) def _find_common(self, lineset1, lineset2): """find similarities in the two given linesets""" @@ -323,10 +325,10 @@ def register(linter): def usage(status=0): """display command line usage information""" - print "finds copy pasted blocks in a set of files" - print - print 'Usage: symilar [-d|--duplicates min_duplicated_lines] \ -[-i|--ignore-comments] [--ignore-docstrings] [--ignore-imports] file1...' + print("finds copy pasted blocks in a set of files") + print() + print('Usage: symilar [-d|--duplicates min_duplicated_lines] \ +[-i|--ignore-comments] [--ignore-docstrings] [--ignore-imports] file1...') sys.exit(status) def Run(argv=None): diff --git a/checkers/strings.py b/checkers/strings.py index 40995f6ee..f517c4c44 100644 --- a/checkers/strings.py +++ b/checkers/strings.py @@ -237,7 +237,7 @@ class StringFormatChecker(BaseChecker): try: required_keys, required_num_args = \ utils.parse_format_string(format_string) - except utils.UnsupportedFormatCharacter, e: + except utils.UnsupportedFormatCharacter as e: c = format_string[e.index] self.add_message('bad-format-character', node=node, args=(c, ord(c), e.index)) @@ -346,7 +346,7 @@ class StringMethodsChecker(BaseChecker): if not isinstance(node.func.expr, astroid.Const): return try: - strnode = func.bound.infer().next() + strnode = next(func.bound.infer()) except astroid.InferenceError: return if not isinstance(strnode, astroid.Const): @@ -435,7 +435,7 @@ class StringMethodsChecker(BaseChecker): if argname in (astroid.YES, None): continue try: - argument = argname.infer().next() + argument = next(argname.infer()) except astroid.InferenceError: continue if not specifiers or argument is astroid.YES: @@ -492,7 +492,7 @@ class StringMethodsChecker(BaseChecker): break try: - previous = previous.infer().next() + previous = next(previous.infer()) except astroid.InferenceError: # can't check further if we can't infer it break diff --git a/checkers/utils.py b/checkers/utils.py index af1440de1..55bcdd418 100644 --- a/checkers/utils.py +++ b/checkers/utils.py @@ -87,11 +87,11 @@ def safe_infer(node): """ try: inferit = node.infer() - value = inferit.next() + value = next(inferit) except astroid.InferenceError: return try: - inferit.next() + next(inferit) return # None if there is ambiguity on the inferred node except astroid.InferenceError: return # there is some kind of ambiguity @@ -471,7 +471,7 @@ def has_known_bases(klass): pass try: for base in klass.bases: - result = base.infer().next() + result = next(base.infer()) # TODO: check for A->B->A->B pattern in class structure too? if not isinstance(result, astroid.Class) or result is klass or not has_known_bases(result): klass._all_bases_known = False diff --git a/checkers/variables.py b/checkers/variables.py index 71b4be404..bcb5bb271 100644 --- a/checkers/variables.py +++ b/checkers/variables.py @@ -33,6 +33,7 @@ from pylint.checkers.utils import ( is_defined_before, is_error, is_func_default, is_func_decorator, assign_parent, check_messages, is_inside_except, clobber_in_except, get_all_elements, has_known_bases) +import six SPECIAL_OBJ = re.compile("^_{2}[a-z]+_{2}$") @@ -45,7 +46,7 @@ def in_for_else_branch(parent, stmt): def overridden_method(klass, name): """get overridden method if any""" try: - parent = klass.local_attr_ancestors(name).next() + parent = next(klass.local_attr_ancestors(name)) except (StopIteration, KeyError): return None try: @@ -145,7 +146,7 @@ def _fix_dot_imports(not_consumed): """ # TODO: this should be improved in issue astroid #46 names = {} - for name, stmts in not_consumed.iteritems(): + for name, stmts in six.iteritems(not_consumed): if any(isinstance(stmt, astroid.AssName) and isinstance(stmt.ass_type(), astroid.AugAssign) for stmt in stmts): @@ -296,7 +297,7 @@ builtins. Remember that you should avoid to define new builtins when possible.' checks globals doesn't overrides builtins """ self._to_consume = [(copy(node.locals), {}, 'module')] - for name, stmts in node.locals.iteritems(): + for name, stmts in six.iteritems(node.locals): if is_builtin(name) and not is_inside_except(stmts[0]): # do not print Redefining builtin for additional builtins self.add_message('redefined-builtin', args=name, node=stmts[0]) @@ -311,11 +312,11 @@ builtins. Remember that you should avoid to define new builtins when possible.' not_consumed = self._to_consume.pop()[0] # attempt to check for __all__ if defined if '__all__' in node.locals: - assigned = node.igetattr('__all__').next() + assigned = next(node.igetattr('__all__')) if assigned is not astroid.YES: for elt in getattr(assigned, 'elts', ()): try: - elt_name = elt.infer().next() + elt_name = next(elt.infer()) except astroid.InferenceError: continue @@ -503,7 +504,7 @@ builtins. Remember that you should avoid to define new builtins when possible.' for nonlocal_stmt in node.nodes_of_class(astroid.Nonlocal): nonlocal_names.update(set(nonlocal_stmt.names)) - for name, stmts in not_consumed.iteritems(): + for name, stmts in six.iteritems(not_consumed): # ignore some special names specified by user configuration if authorized_rgx.match(name): continue @@ -829,7 +830,7 @@ builtins. Remember that you should avoid to define new builtins when possible.' for name, _ in node.names: parts = name.split('.') try: - module = node.infer_name_module(parts[0]).next() + module = next(node.infer_name_module(parts[0])) except astroid.ResolveError: continue self._check_module_attrs(node, module, parts[1:]) @@ -916,7 +917,7 @@ builtins. Remember that you should avoid to define new builtins when possible.' module = None break try: - module = module.getattr(name)[0].infer().next() + module = next(module.getattr(name)[0].infer()) if module is astroid.YES: return None except astroid.NotFoundError: @@ -17,6 +17,7 @@ * pylint.d (PYLINTHOME) """ from __future__ import with_statement +from __future__ import print_function import pickle import os @@ -66,13 +67,13 @@ def save_results(results, base): try: os.mkdir(PYLINT_HOME) except OSError: - print >> sys.stderr, 'Unable to create directory %s' % PYLINT_HOME + print('Unable to create directory %s' % PYLINT_HOME, file=sys.stderr) data_file = get_pdata_path(base, 1) try: with open(data_file, _PICK_DUMP) as stream: pickle.dump(results, stream) - except (IOError, OSError), ex: - print >> sys.stderr, 'Unable to create file %s: %s' % (data_file, ex) + except (IOError, OSError) as ex: + print('Unable to create file %s: %s' % (data_file, ex), file=sys.stderr) # location of the configuration file ########################################## diff --git a/epylint.py b/epylint.py index beae481b8..b34ef631c 100755 --- a/epylint.py +++ b/epylint.py @@ -45,6 +45,7 @@ For example: You may also use py_run to run pylint with desired options and get back (or not) its output. """ +from __future__ import print_function import sys, os import os.path as osp @@ -102,7 +103,7 @@ def lint(filename, options=None): parts = line.split(":") if parts and parts[0] == child_path: line = ":".join([filename] + parts[1:]) - print line, + print(line, end=' ') process.wait() return process.returncode @@ -162,10 +163,10 @@ def py_run(command_options='', return_std=False, stdout=None, stderr=None, def Run(): if len(sys.argv) == 1: - print "Usage: %s <filename> [options]" % sys.argv[0] + print("Usage: %s <filename> [options]" % sys.argv[0]) sys.exit(1) elif not osp.exists(sys.argv[1]): - print "%s does not exist" % sys.argv[1] + print("%s does not exist" % sys.argv[1]) sys.exit(1) else: sys.exit(lint(sys.argv[1], sys.argv[2:])) @@ -14,6 +14,7 @@ # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. """Tkinker gui for pylint""" +from __future__ import print_function import os import sys @@ -86,7 +87,7 @@ class BasicStream(object): """finalize what the contents of the dict should look like before output""" for item in self.outdict: num_empty = self.outdict[item].count('') - for _ in xrange(num_empty): + for _ in range(num_empty): self.outdict[item].remove('') if self.outdict[item]: self.outdict[item].pop(0) @@ -503,7 +504,7 @@ def lint_thread(module, reporter, gui): def Run(args): """launch pylint gui from args""" if args: - print 'USAGE: pylint-gui\n launch a simple pylint gui using Tk' + print('USAGE: pylint-gui\n launch a simple pylint gui using Tk') sys.exit(1) gui = LintGui() gui.mainloop() @@ -60,6 +60,7 @@ from pylint.reporters import initialize as reporters_initialize from pylint import config from pylint.__pkginfo__ import version +import six @@ -449,7 +450,7 @@ class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn, checker.load_defaults() def disable_noerror_messages(self): - for msgcat, msgids in self.msgs_store._msgs_by_category.iteritems(): + for msgcat, msgids in six.iteritems(self.msgs_store._msgs_by_category): if msgcat == 'E': for msgid in msgids: self.enable(msgid) @@ -459,7 +460,7 @@ class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn, def disable_reporters(self): """disable all reporters""" - for reporters in self._reports.itervalues(): + for reporters in six.itervalues(self._reports): for report_id, _title, _cb in reporters: self.disable_report(report_id) @@ -524,7 +525,7 @@ class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn, def get_checkers(self): """return all available checkers as a list""" - return [self] + [c for checkers in self._checkers.itervalues() + return [self] + [c for checkers in six.itervalues(self._checkers) for c in checkers if c is not self] def prepare_checkers(self): @@ -639,7 +640,7 @@ class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn, self.current_file = filepath or modname self.stats['by_module'][modname] = {} self.stats['by_module'][modname]['statement'] = 0 - for msg_cat in MSG_TYPES.itervalues(): + for msg_cat in six.itervalues(MSG_TYPES): self.stats['by_module'][modname][msg_cat] = 0 def get_ast(self, filepath, modname): @@ -691,7 +692,7 @@ class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn, self.stats = {'by_module' : {}, 'by_msg' : {}, } - for msg_cat in MSG_TYPES.itervalues(): + for msg_cat in six.itervalues(MSG_TYPES): self.stats[msg_cat] = 0 def close(self): @@ -764,7 +765,7 @@ def report_messages_stats(sect, stats, _): # don't print this report when we didn't detected any errors raise EmptyReport() in_order = sorted([(value, msg_id) - for msg_id, value in stats['by_msg'].iteritems() + for msg_id, value in six.iteritems(stats['by_msg']) if not msg_id.startswith('I')]) in_order.reverse() lines = ('message id', 'occurrences') @@ -780,7 +781,7 @@ def report_messages_by_module_stats(sect, stats, _): by_mod = {} for m_type in ('fatal', 'error', 'warning', 'refactor', 'convention'): total = stats[m_type] - for module in stats['by_module'].iterkeys(): + for module in six.iterkeys(stats['by_module']): mod_total = stats['by_module'][module][m_type] if total == 0: percent = 0 @@ -788,7 +789,7 @@ def report_messages_by_module_stats(sect, stats, _): percent = float((mod_total)*100) / total by_mod.setdefault(module, {})[m_type] = percent sorted_result = [] - for module, mod_info in by_mod.iteritems(): + for module, mod_info in six.iteritems(by_mod): sorted_result.append((mod_info['error'], mod_info['warning'], mod_info['refactor'], @@ -1077,7 +1078,7 @@ group are mutually exclusive.'), def cb_init_hook(optname, value): """exec arbitrary code to set sys.path for instance""" - exec value + exec(value) if __name__ == '__main__': diff --git a/reporters/__init__.py b/reporters/__init__.py index 429155a43..8be5d3ab6 100644 --- a/reporters/__init__.py +++ b/reporters/__init__.py @@ -12,6 +12,7 @@ # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. """utilities methods and classes for reporters""" +from __future__ import print_function import sys import locale @@ -90,7 +91,7 @@ class BaseReporter(object): def writeln(self, string=''): """write a line in the output buffer""" - print >> self.out, self.encode(string) + print(self.encode(string), file=self.out) def display_results(self, layout): """display results encapsulated in the layout tree""" diff --git a/reporters/text.py b/reporters/text.py index 48b51556d..b7bbebb80 100644 --- a/reporters/text.py +++ b/reporters/text.py @@ -16,6 +16,7 @@ :text: the default one grouping messages by module :colorized: an ANSI colorized text reporter """ +from __future__ import print_function import warnings @@ -60,7 +61,7 @@ class TextReporter(BaseReporter): def _display(self, layout): """launch layouts display""" - print >> self.out + print(file=self.out) TextWriter().format(layout, self.out) @@ -20,6 +20,7 @@ # with pylint. If not, see <http://www.gnu.org/licenses/>. """Generic Setup script, takes package info from __pkginfo__.py file. """ +from __future__ import print_function __docformat__ = "restructuredtext en" import os @@ -38,14 +39,7 @@ except ImportError: from distutils.command import install_lib USE_SETUPTOOLS = 0 -try: - # pylint: disable=no-name-in-module - # python3 - from distutils.command.build_py import build_py_2to3 as build_py -except ImportError: - # pylint: disable=no-name-in-module - # python2.x - from distutils.command.build_py import build_py +from distutils.command.build_py import build_py sys.modules.pop('__pkginfo__', None) # import optional features @@ -144,7 +138,7 @@ class MyInstallLib(install_lib.install_lib): # process manually python file in include_dirs (test data) # pylint: disable=no-name-in-module from distutils.util import run_2to3 - print('running 2to3 on', dest) + print(('running 2to3 on', dest)) run_2to3([dest]) # override this since pip/easy_install attempt to byte compile test data diff --git a/testutils.py b/testutils.py index 86539acb4..6a437c6e7 100644 --- a/testutils.py +++ b/testutils.py @@ -14,6 +14,7 @@ # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. """functional/non regression tests for pylint""" +from __future__ import print_function import collections import contextlib @@ -34,6 +35,7 @@ from pylint.utils import PyLintASTWalker from pylint.reporters import BaseReporter from pylint.interfaces import IReporter from pylint.lint import PyLinter +import six # Utils @@ -113,7 +115,7 @@ class TestReporter(BaseReporter): def finalize(self): self.messages.sort() for msg in self.messages: - print >> self.out, msg + print(msg, file=self.out) result = self.out.getvalue() self.reset() return result @@ -148,7 +150,7 @@ class UnittestLinter(object): return True def add_stats(self, **kwargs): - for name, value in kwargs.iteritems(): + for name, value in six.iteritems(kwargs): self.stats[name] = value return self.stats @@ -161,7 +163,7 @@ def set_config(**kwargs): def _Wrapper(fun): @functools.wraps(fun) def _Forward(self): - for key, value in kwargs.iteritems(): + for key, value in six.iteritems(kwargs): setattr(self.checker.config, key, value) if isinstance(self, CheckerTestCase): # reopen checker in case, it may be interested in configuration change @@ -180,7 +182,7 @@ class CheckerTestCase(unittest.TestCase): def setUp(self): self.linter = UnittestLinter() self.checker = self.CHECKER_CLASS(self.linter) # pylint: disable=not-callable - for key, value in self.CONFIG.iteritems(): + for key, value in six.iteritems(self.CONFIG): setattr(self.checker.config, key, value) self.checker.open() @@ -277,11 +279,11 @@ class LintTestUsingModule(unittest.TestCase): self.linter.disable('I') try: self.linter.check(tocheck) - except Exception, ex: + except Exception as ex: # need finalization to restore a correct state self.linter.reporter.finalize() ex.file = tocheck - print ex + print(ex) ex.__str__ = exception_str raise self._check_result(self.linter.reporter.finalize()) @@ -1,10 +1,12 @@ [tox] # official list is #envlist = py27, py32, py33, py34 -envlist = py27, py33 +#envlist = py27, py33 +envlist = py27, py34 [testenv] deps = logilab-common hg+https://bitbucket.org/logilab/astroid/ + six commands = python -Wi -m unittest discover -s {envsitepackagesdir}/pylint/test/ -p *test_*.py changedir = {toxworkdir} @@ -37,6 +37,8 @@ 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): @@ -54,7 +56,7 @@ MSG_TYPES = { 'E' : 'error', 'F' : 'fatal' } -MSG_TYPES_LONG = {v: k for k, v in MSG_TYPES.iteritems()} +MSG_TYPES_LONG = {v: k for k, v in six.iteritems(MSG_TYPES)} MSG_TYPES_STATUS = { 'I' : 0, @@ -265,7 +267,7 @@ class MessagesHandlerMixIn(object): msgs = self._msgs_state msgs[msg.msgid] = False # sync configuration object - self.config.disable_msg = [mid for mid, val in msgs.iteritems() + self.config.disable_msg = [mid for mid, val in six.iteritems(msgs) if not val] def enable(self, msgid, scope='package', line=None, ignore_unknown=False): @@ -303,7 +305,7 @@ class MessagesHandlerMixIn(object): msgs = self._msgs_state msgs[msg.msgid] = True # sync configuration object - self.config.enable = [mid for mid, val in msgs.iteritems() if val] + self.config.enable = [mid for mid, val in six.iteritems(msgs) if val] def get_message_state_scope(self, msgid, line=None, confidence=UNDEFINED): """Returns the scope at which a message was enabled/disabled.""" @@ -425,7 +427,7 @@ class MessagesHandlerMixIn(object): by_checker[checker.name] = [list(checker.options_and_values()), dict(checker.msgs), list(checker.reports)] - for checker, (options, msgs, reports) in by_checker.iteritems(): + for checker, (options, msgs, reports) in six.iteritems(by_checker): prefix = '' title = '%s checker' % checker print(title) @@ -441,7 +443,7 @@ class MessagesHandlerMixIn(object): title = ('%smessages' % prefix).capitalize() print(title) print('~' * len(title)) - for msgid, msg in sorted(msgs.iteritems(), + 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)) @@ -468,7 +470,7 @@ class FileState(object): def collect_block_lines(self, msgs_store, module_node): """Walk the AST to collect block level options line numbers.""" - for msg, lines in self._module_msgs_state.iteritems(): + for msg, lines in six.iteritems(self._module_msgs_state): self._raw_module_msgs_state[msg] = lines.copy() orig_state = self._module_msgs_state.copy() self._module_msgs_state = {} @@ -502,7 +504,7 @@ class FileState(object): firstchildlineno = node.body[0].fromlineno else: firstchildlineno = last - for msgid, lines in msg_state.iteritems(): + for msgid, lines in six.iteritems(msg_state): for lineno, state in lines.items(): original_lineno = lineno if first <= lineno <= last: @@ -515,7 +517,7 @@ class FileState(object): else: first_ = lineno last_ = last - for line in xrange(first_, last_+1): + for line in range(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 @@ -552,8 +554,8 @@ class FileState(object): pass def iter_spurious_suppression_messages(self, msgs_store): - for warning, lines in self._raw_module_msgs_state.iteritems(): - for line, enable in lines.iteritems(): + for warning, lines in six.iteritems(self._raw_module_msgs_state): + for line, enable in six.iteritems(lines): if not enable and (warning, line) not in self._ignored_msgs: yield 'useless-suppression', line, \ (msgs_store.get_msg_display_string(warning),) @@ -584,7 +586,7 @@ class MessagesStore(object): @property def messages(self): """The list of all active messages.""" - return self._messages.itervalues() + return six.itervalues(self._messages) def add_renamed_message(self, old_id, old_symbol, new_symbol): """Register the old ID and symbol for a warning that was renamed. @@ -606,7 +608,7 @@ class MessagesStore(object): are the checker id and the two last the message id in this checker """ chkid = None - for msgid, msg_tuple in checker.msgs.iteritems(): + for msgid, msg_tuple in six.iteritems(checker.msgs): msg = build_message_def(checker, msgid, msg_tuple) assert msg.symbol not in self._messages, \ 'Message symbol %r is already defined' % msg.symbol @@ -659,7 +661,7 @@ class MessagesStore(object): def list_messages(self): """output full messages list documentation in ReST format""" - msgs = sorted(self._messages.itervalues(), key=lambda msg: msg.msgid) + msgs = sorted(six.itervalues(self._messages), key=lambda msg: msg.msgid) for msg in msgs: if not msg.may_be_emitted(): continue @@ -729,7 +731,7 @@ class ReportsHandlerMixIn(object): """add some stats entries to the statistic dictionary raise an AssertionError if there is a key conflict """ - for key, value in kwargs.iteritems(): + for key, value in six.iteritems(kwargs): if key[-1] == '_': key = key[:-1] assert key not in self.stats |