Merge ~twom/launchpad:lint-e701 into launchpad:master

Proposed by Tom Wardill
Status: Rejected
Rejected by: Colin Watson
Proposed branch: ~twom/launchpad:lint-e701
Merge into: launchpad:master
Diff against target: 372 lines (+366/-0) (has conflicts)
1 file modified
utilities/findimports.py (+366/-0)
Conflict in utilities/findimports.py
Reviewer Review Type Date Requested Status
Colin Watson (community) Disapprove
Review via email: mp+406625@code.launchpad.net

Commit message

Remove E701 lint violations

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) wrote :

This was superseded by removing findimports entirely (https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/406491).

review: Disapprove

Unmerged commits

436c7f5... by Tom Wardill

Remove E701 violations

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/utilities/findimports.py b/utilities/findimports.py
2new file mode 100755
3index 0000000..8276094
4--- /dev/null
5+++ b/utilities/findimports.py
6@@ -0,0 +1,366 @@
7+<<<<<<< utilities/findimports.py
8+=======
9+#!/usr/bin/python2
10+#
11+# Copyright 2009 Canonical Ltd. This software is licensed under the
12+# GNU Affero General Public License version 3 (see the file LICENSE).
13+
14+"""
15+FindImports is a script that processes Python module dependencies. Currently
16+it can be used for finding unused imports and graphing module dependencies
17+(with graphviz). FindImports requires Python 2.3.
18+
19+Syntax: findimports.py [options] [filename|dirname ...]
20+
21+Options:
22+ -h, --help This help message
23+
24+ -i, --imports Print dependency graph (default action).
25+ -d, --dot Print dependency graph in dot format.
26+ -n, --names Print dependency graph with all imported names.
27+
28+ -u, --unused Print unused imports.
29+ -a, --all Print all unused imports (use together with -u).
30+
31+Copyright (c) 2003, 2004 Marius Gedminas <marius@pov.lt>
32+
33+This program is free software; you can redistribute it and/or modify it under
34+the terms of the GNU General Public License as published by the Free Software
35+Foundation; either version 2 of the License, or (at your option) any later
36+version.
37+
38+This program is distributed in the hope that it will be useful, but WITHOUT ANY
39+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
40+PARTICULAR PURPOSE. See the GNU General Public License for more details.
41+
42+You should have received a copy of the GNU General Public License along with
43+this program; if not, write to the Free Software Foundation, Inc., 675 Mass
44+Ave, Cambridge, MA 02139, USA.
45+"""
46+
47+from __future__ import absolute_import, print_function
48+
49+import compiler
50+from compiler.visitor import ASTVisitor
51+import getopt
52+import linecache
53+import os
54+import sys
55+
56+import six
57+
58+
59+class ImportFinder(ASTVisitor):
60+ """AST visitor that collects all imported names in its imports attribute.
61+
62+ For example, the following import statements in the AST tree
63+
64+ import a, b.c, d as e
65+ from q.w.e import x, y as foo, z
66+ from woof import *
67+
68+ will cause imports to contain
69+
70+ a
71+ b.c
72+ d
73+ q.w.e.x
74+ q.w.e.y
75+ q.w.e.z
76+ woof.*
77+ """
78+
79+ def __init__(self):
80+ self.imports = []
81+
82+ def visitImport(self, node):
83+ for name, imported_as in node.names:
84+ self.imports.append(name)
85+
86+ def visitFrom(self, node):
87+ for name, imported_as in node.names:
88+ self.imports.append('%s.%s' % (node.modname, name))
89+
90+
91+class UnusedName(object):
92+
93+ def __init__(self, name, lineno):
94+ self.name = name
95+ self.lineno = lineno
96+
97+
98+class ImportFinderAndNametracker(ImportFinder):
99+ """ImportFinder that also keeps track on used names."""
100+
101+ def __init__(self):
102+ ImportFinder.__init__(self)
103+ self.unused_names = {}
104+
105+ def visitImport(self, node):
106+ ImportFinder.visitImport(self, node)
107+ for name, imported_as in node.names:
108+ if not imported_as:
109+ imported_as = name
110+ if imported_as != "*":
111+ self.unused_names[imported_as] = UnusedName(imported_as,
112+ node.lineno)
113+
114+ def visitFrom(self, node):
115+ ImportFinder.visitFrom(self, node)
116+ for name, imported_as in node.names:
117+ if not imported_as:
118+ imported_as = name
119+ if imported_as != "*":
120+ self.unused_names[imported_as] = UnusedName(imported_as,
121+ node.lineno)
122+
123+ def visitName(self, node):
124+ if node.name in self.unused_names:
125+ del self.unused_names[node.name]
126+
127+ def visitGetattr(self, node):
128+ full_name = [node.attrname]
129+ parent = node.expr
130+ while isinstance(parent, compiler.ast.Getattr):
131+ full_name.append(parent.attrname)
132+ parent = parent.expr
133+ if isinstance(parent, compiler.ast.Name):
134+ full_name.append(parent.name)
135+ full_name.reverse()
136+ name = ""
137+ for part in full_name:
138+ if name:
139+ name = '%s.%s' % (name, part)
140+ else:
141+ name += part
142+ if name in self.unused_names:
143+ del self.unused_names[name]
144+ for c in node.getChildNodes():
145+ self.visit(c)
146+
147+
148+def find_imports(filename):
149+ """Find all imported names in a given file."""
150+ ast = compiler.parseFile(filename)
151+ visitor = ImportFinder()
152+ compiler.walk(ast, visitor)
153+ return visitor.imports
154+
155+def find_imports_and_track_names(filename):
156+ """Find all imported names in a given file."""
157+ ast = compiler.parseFile(filename)
158+ visitor = ImportFinderAndNametracker()
159+ compiler.walk(ast, visitor)
160+ return visitor.imports, visitor.unused_names
161+
162+
163+class Module(object):
164+
165+ def __init__(self, modname, filename):
166+ self.modname = modname
167+ self.filename = filename
168+
169+
170+class ModuleGraph(object):
171+
172+ trackUnusedNames = False
173+ all_unused = False
174+
175+ def __init__(self):
176+ self.modules = {}
177+ self.path = sys.path
178+ self._module_cache = {}
179+ self._warned_about = set()
180+
181+ def parsePathname(self, pathname):
182+ if os.path.isdir(pathname):
183+ for root, dirs, files in os.walk(pathname):
184+ for fn in files:
185+ # ignore emacsish junk
186+ if fn.endswith('.py') and not fn.startswith('.#'):
187+ self.parseFile(os.path.join(root, fn))
188+ else:
189+ self.parseFile(pathname)
190+
191+ def parseFile(self, filename):
192+ modname = self.filenameToModname(filename)
193+ module = Module(modname, filename)
194+ self.modules[modname] = module
195+ if self.trackUnusedNames:
196+ module.imported_names, module.unused_names = \
197+ find_imports_and_track_names(filename)
198+ else:
199+ module.imported_names = find_imports(filename)
200+ module.unused_names = None
201+ dir = os.path.dirname(filename)
202+ module.imports = set([self.findModuleOfName(name, filename, dir)
203+ for name in module.imported_names])
204+
205+ def filenameToModname(self, filename):
206+ for ext in ('.py', '.so', '.dll'):
207+ if filename.endswith(ext):
208+ break
209+ else:
210+ print(
211+ "%s: unknown file name extension" % filename, file=sys.stderr)
212+ longest_prefix_len = 0
213+ filename = os.path.abspath(filename)
214+ for prefix in self.path:
215+ prefix = os.path.abspath(prefix)
216+ if (filename.startswith(prefix)
217+ and len(prefix) > longest_prefix_len):
218+ longest_prefix_len = len(prefix)
219+ filename = filename[longest_prefix_len:-len('.py')]
220+ if filename.startswith(os.path.sep):
221+ filename = filename[len(os.path.sep):]
222+ modname = ".".join(filename.split(os.path.sep))
223+ return modname
224+
225+ def findModuleOfName(self, dotted_name, filename, extrapath=None):
226+ if dotted_name.endswith('.*'):
227+ return dotted_name[:-2]
228+ name = dotted_name
229+ while name:
230+ candidate = self.isModule(name, extrapath)
231+ if candidate:
232+ return candidate
233+ candidate = self.isPackage(name, extrapath)
234+ if candidate:
235+ return candidate
236+ name = name[:name.rfind('.')]
237+ if dotted_name not in self._warned_about:
238+ print(
239+ "%s: could not find %s" % (filename, dotted_name),
240+ file=sys.stderr)
241+ self._warned_about.add(dotted_name)
242+ return dotted_name
243+
244+ def isModule(self, dotted_name, extrapath=None):
245+ try:
246+ return self._module_cache[(dotted_name, extrapath)]
247+ except KeyError:
248+ pass
249+ if dotted_name in sys.modules:
250+ return dotted_name
251+ filename = dotted_name.replace('.', os.path.sep)
252+ if extrapath:
253+ for ext in ('.py', '.so', '.dll'):
254+ candidate = os.path.join(extrapath, filename) + ext
255+ if os.path.exists(candidate):
256+ modname = self.filenameToModname(candidate)
257+ self._module_cache[(dotted_name, extrapath)] = modname
258+ return modname
259+ try:
260+ return self._module_cache[(dotted_name, None)]
261+ except KeyError:
262+ pass
263+ for dir in self.path:
264+ for ext in ('.py', '.so', '.dll'):
265+ candidate = os.path.join(dir, filename) + ext
266+ if os.path.exists(candidate):
267+ modname = self.filenameToModname(candidate)
268+ self._module_cache[(dotted_name, extrapath)] = modname
269+ self._module_cache[(dotted_name, None)] = modname
270+ return modname
271+ return None
272+
273+ def isPackage(self, dotted_name, extrapath=None):
274+ candidate = self.isModule(dotted_name + '.__init__', extrapath)
275+ if candidate:
276+ candidate = candidate[:-len(".__init__")]
277+ return candidate
278+
279+ def listModules(self):
280+ modules = list(self.modules.items())
281+ modules.sort()
282+ return [module for name, module in modules]
283+
284+ def printImportedNames(self):
285+ for module in self.listModules():
286+ print("%s:" % module.modname)
287+ print(" %s" % "\n ".join(module.imported_names))
288+
289+ def printImports(self):
290+ for module in self.listModules():
291+ print("%s:" % module.modname)
292+ imports = list(module.imports)
293+ imports.sort()
294+ print(" %s" % "\n ".join(imports))
295+
296+ def printUnusedImports(self):
297+ for module in self.listModules():
298+ names = [(unused.lineno, unused.name)
299+ for unused in six.itervalues(module.unused_names)]
300+ names.sort()
301+ for lineno, name in names:
302+ if not self.all_unused:
303+ line = linecache.getline(module.filename, lineno)
304+ if '#' in line:
305+ continue # assume there's a comment explaining why it
306+ # is not used
307+ print("%s:%s: %s not used" % (module.filename, lineno, name))
308+
309+ def printDot(self):
310+ print("digraph ModuleDependencies {")
311+ print(" node[shape=box];")
312+ allNames = set()
313+ nameDict = {}
314+ for n, module in enumerate(self.listModules()):
315+ module._dot_name = "mod%d" % n
316+ nameDict[module.modname] = module._dot_name
317+ print(" %s[label=\"%s\"];" % (module._dot_name, module.modname))
318+ for name in module.imports:
319+ if name not in self.modules:
320+ allNames.add(name)
321+ print(" node[style=dotted];")
322+ names = list(allNames)
323+ names.sort()
324+ for n, name in enumerate(names):
325+ nameDict[name] = id = "extmod%d" % n
326+ print(" %s[label=\"%s\"];" % (id, name))
327+ for module in self.modules.values():
328+ for other in module.imports:
329+ print(" %s -> %s;" % (nameDict[module.modname],
330+ nameDict[other]))
331+ print("}")
332+
333+
334+def main(argv=sys.argv):
335+ progname = os.path.basename(argv[0])
336+ helptext = __doc__.strip().replace('findimports.py', progname)
337+ g = ModuleGraph()
338+ action = g.printImports
339+ try:
340+ opts, args = getopt.getopt(argv[1:], 'duniah',
341+ ['dot', 'unused', 'all', 'names', 'imports',
342+ 'help'])
343+ except getopt.error as e:
344+ print("%s: %s" % (progname, e), file=sys.stderr)
345+ print("Try %s --help." % progname, file=sys.stderr)
346+ return 1
347+ for k, v in opts:
348+ if k in ('-d', '--dot'):
349+ action = g.printDot
350+ elif k in ('-u', '--unused'):
351+ action = g.printUnusedImports
352+ elif k in ('-a', '--all'):
353+ g.all_unused = True
354+ elif k in ('-n', '--names'):
355+ action = g.printImportedNames
356+ elif k in ('-i', '--imports'):
357+ action = g.printImports
358+ elif k in ('-h', '--help'):
359+ print(helptext)
360+ return 0
361+ g.trackUnusedNames = (action == g.printUnusedImports)
362+ if not args:
363+ args = ['.']
364+ for fn in args:
365+ g.parsePathname(fn)
366+ action()
367+ return 0
368+
369+if __name__ == '__main__':
370+ sys.exit(main())
371+
372+>>>>>>> utilities/findimports.py

Subscribers

People subscribed via source and target branches

to status/vote changes: