Tom Wardill has proposed merging ~twom/launchpad:lint-e701 into launchpad:master.
Commit message: Remove E701 lint violations Requested reviews: Launchpad code reviewers (launchpad-reviewers) For more details, see: https://code.launchpad.net/~twom/launchpad/+git/launchpad/+merge/406625 -- Your team Launchpad code reviewers is requested to review the proposed merge of ~twom/launchpad:lint-e701 into launchpad:master.
diff --git a/utilities/findimports.py b/utilities/findimports.py new file mode 100755 index 0000000..8276094 --- /dev/null +++ b/utilities/findimports.py @@ -0,0 +1,366 @@ +<<<<<<< utilities/findimports.py +======= +#!/usr/bin/python2 +# +# Copyright 2009 Canonical Ltd. This software is licensed under the +# GNU Affero General Public License version 3 (see the file LICENSE). + +""" +FindImports is a script that processes Python module dependencies. Currently +it can be used for finding unused imports and graphing module dependencies +(with graphviz). FindImports requires Python 2.3. + +Syntax: findimports.py [options] [filename|dirname ...] + +Options: + -h, --help This help message + + -i, --imports Print dependency graph (default action). + -d, --dot Print dependency graph in dot format. + -n, --names Print dependency graph with all imported names. + + -u, --unused Print unused imports. + -a, --all Print all unused imports (use together with -u). + +Copyright (c) 2003, 2004 Marius Gedminas <[email protected]> + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 2 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 675 Mass +Ave, Cambridge, MA 02139, USA. +""" + +from __future__ import absolute_import, print_function + +import compiler +from compiler.visitor import ASTVisitor +import getopt +import linecache +import os +import sys + +import six + + +class ImportFinder(ASTVisitor): + """AST visitor that collects all imported names in its imports attribute. + + For example, the following import statements in the AST tree + + import a, b.c, d as e + from q.w.e import x, y as foo, z + from woof import * + + will cause imports to contain + + a + b.c + d + q.w.e.x + q.w.e.y + q.w.e.z + woof.* + """ + + def __init__(self): + self.imports = [] + + def visitImport(self, node): + for name, imported_as in node.names: + self.imports.append(name) + + def visitFrom(self, node): + for name, imported_as in node.names: + self.imports.append('%s.%s' % (node.modname, name)) + + +class UnusedName(object): + + def __init__(self, name, lineno): + self.name = name + self.lineno = lineno + + +class ImportFinderAndNametracker(ImportFinder): + """ImportFinder that also keeps track on used names.""" + + def __init__(self): + ImportFinder.__init__(self) + self.unused_names = {} + + def visitImport(self, node): + ImportFinder.visitImport(self, node) + for name, imported_as in node.names: + if not imported_as: + imported_as = name + if imported_as != "*": + self.unused_names[imported_as] = UnusedName(imported_as, + node.lineno) + + def visitFrom(self, node): + ImportFinder.visitFrom(self, node) + for name, imported_as in node.names: + if not imported_as: + imported_as = name + if imported_as != "*": + self.unused_names[imported_as] = UnusedName(imported_as, + node.lineno) + + def visitName(self, node): + if node.name in self.unused_names: + del self.unused_names[node.name] + + def visitGetattr(self, node): + full_name = [node.attrname] + parent = node.expr + while isinstance(parent, compiler.ast.Getattr): + full_name.append(parent.attrname) + parent = parent.expr + if isinstance(parent, compiler.ast.Name): + full_name.append(parent.name) + full_name.reverse() + name = "" + for part in full_name: + if name: + name = '%s.%s' % (name, part) + else: + name += part + if name in self.unused_names: + del self.unused_names[name] + for c in node.getChildNodes(): + self.visit(c) + + +def find_imports(filename): + """Find all imported names in a given file.""" + ast = compiler.parseFile(filename) + visitor = ImportFinder() + compiler.walk(ast, visitor) + return visitor.imports + +def find_imports_and_track_names(filename): + """Find all imported names in a given file.""" + ast = compiler.parseFile(filename) + visitor = ImportFinderAndNametracker() + compiler.walk(ast, visitor) + return visitor.imports, visitor.unused_names + + +class Module(object): + + def __init__(self, modname, filename): + self.modname = modname + self.filename = filename + + +class ModuleGraph(object): + + trackUnusedNames = False + all_unused = False + + def __init__(self): + self.modules = {} + self.path = sys.path + self._module_cache = {} + self._warned_about = set() + + def parsePathname(self, pathname): + if os.path.isdir(pathname): + for root, dirs, files in os.walk(pathname): + for fn in files: + # ignore emacsish junk + if fn.endswith('.py') and not fn.startswith('.#'): + self.parseFile(os.path.join(root, fn)) + else: + self.parseFile(pathname) + + def parseFile(self, filename): + modname = self.filenameToModname(filename) + module = Module(modname, filename) + self.modules[modname] = module + if self.trackUnusedNames: + module.imported_names, module.unused_names = \ + find_imports_and_track_names(filename) + else: + module.imported_names = find_imports(filename) + module.unused_names = None + dir = os.path.dirname(filename) + module.imports = set([self.findModuleOfName(name, filename, dir) + for name in module.imported_names]) + + def filenameToModname(self, filename): + for ext in ('.py', '.so', '.dll'): + if filename.endswith(ext): + break + else: + print( + "%s: unknown file name extension" % filename, file=sys.stderr) + longest_prefix_len = 0 + filename = os.path.abspath(filename) + for prefix in self.path: + prefix = os.path.abspath(prefix) + if (filename.startswith(prefix) + and len(prefix) > longest_prefix_len): + longest_prefix_len = len(prefix) + filename = filename[longest_prefix_len:-len('.py')] + if filename.startswith(os.path.sep): + filename = filename[len(os.path.sep):] + modname = ".".join(filename.split(os.path.sep)) + return modname + + def findModuleOfName(self, dotted_name, filename, extrapath=None): + if dotted_name.endswith('.*'): + return dotted_name[:-2] + name = dotted_name + while name: + candidate = self.isModule(name, extrapath) + if candidate: + return candidate + candidate = self.isPackage(name, extrapath) + if candidate: + return candidate + name = name[:name.rfind('.')] + if dotted_name not in self._warned_about: + print( + "%s: could not find %s" % (filename, dotted_name), + file=sys.stderr) + self._warned_about.add(dotted_name) + return dotted_name + + def isModule(self, dotted_name, extrapath=None): + try: + return self._module_cache[(dotted_name, extrapath)] + except KeyError: + pass + if dotted_name in sys.modules: + return dotted_name + filename = dotted_name.replace('.', os.path.sep) + if extrapath: + for ext in ('.py', '.so', '.dll'): + candidate = os.path.join(extrapath, filename) + ext + if os.path.exists(candidate): + modname = self.filenameToModname(candidate) + self._module_cache[(dotted_name, extrapath)] = modname + return modname + try: + return self._module_cache[(dotted_name, None)] + except KeyError: + pass + for dir in self.path: + for ext in ('.py', '.so', '.dll'): + candidate = os.path.join(dir, filename) + ext + if os.path.exists(candidate): + modname = self.filenameToModname(candidate) + self._module_cache[(dotted_name, extrapath)] = modname + self._module_cache[(dotted_name, None)] = modname + return modname + return None + + def isPackage(self, dotted_name, extrapath=None): + candidate = self.isModule(dotted_name + '.__init__', extrapath) + if candidate: + candidate = candidate[:-len(".__init__")] + return candidate + + def listModules(self): + modules = list(self.modules.items()) + modules.sort() + return [module for name, module in modules] + + def printImportedNames(self): + for module in self.listModules(): + print("%s:" % module.modname) + print(" %s" % "\n ".join(module.imported_names)) + + def printImports(self): + for module in self.listModules(): + print("%s:" % module.modname) + imports = list(module.imports) + imports.sort() + print(" %s" % "\n ".join(imports)) + + def printUnusedImports(self): + for module in self.listModules(): + names = [(unused.lineno, unused.name) + for unused in six.itervalues(module.unused_names)] + names.sort() + for lineno, name in names: + if not self.all_unused: + line = linecache.getline(module.filename, lineno) + if '#' in line: + continue # assume there's a comment explaining why it + # is not used + print("%s:%s: %s not used" % (module.filename, lineno, name)) + + def printDot(self): + print("digraph ModuleDependencies {") + print(" node[shape=box];") + allNames = set() + nameDict = {} + for n, module in enumerate(self.listModules()): + module._dot_name = "mod%d" % n + nameDict[module.modname] = module._dot_name + print(" %s[label=\"%s\"];" % (module._dot_name, module.modname)) + for name in module.imports: + if name not in self.modules: + allNames.add(name) + print(" node[style=dotted];") + names = list(allNames) + names.sort() + for n, name in enumerate(names): + nameDict[name] = id = "extmod%d" % n + print(" %s[label=\"%s\"];" % (id, name)) + for module in self.modules.values(): + for other in module.imports: + print(" %s -> %s;" % (nameDict[module.modname], + nameDict[other])) + print("}") + + +def main(argv=sys.argv): + progname = os.path.basename(argv[0]) + helptext = __doc__.strip().replace('findimports.py', progname) + g = ModuleGraph() + action = g.printImports + try: + opts, args = getopt.getopt(argv[1:], 'duniah', + ['dot', 'unused', 'all', 'names', 'imports', + 'help']) + except getopt.error as e: + print("%s: %s" % (progname, e), file=sys.stderr) + print("Try %s --help." % progname, file=sys.stderr) + return 1 + for k, v in opts: + if k in ('-d', '--dot'): + action = g.printDot + elif k in ('-u', '--unused'): + action = g.printUnusedImports + elif k in ('-a', '--all'): + g.all_unused = True + elif k in ('-n', '--names'): + action = g.printImportedNames + elif k in ('-i', '--imports'): + action = g.printImports + elif k in ('-h', '--help'): + print(helptext) + return 0 + g.trackUnusedNames = (action == g.printUnusedImports) + if not args: + args = ['.'] + for fn in args: + g.parsePathname(fn) + action() + return 0 + +if __name__ == '__main__': + sys.exit(main()) + +>>>>>>> utilities/findimports.py
_______________________________________________ Mailing list: https://launchpad.net/~launchpad-reviewers Post to : [email protected] Unsubscribe : https://launchpad.net/~launchpad-reviewers More help : https://help.launchpad.net/ListHelp

