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

Subscribers

People subscribed via source and target branches

to status/vote changes: