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