Merge ~cjwatson/launchpad:remove-findimports into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: b38c1c4ca6c7f0a918f5331a4e3111855c023d30
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:remove-findimports
Merge into: launchpad:master
Diff against target: 367 lines (+0/-361)
1 file modified
dev/null (+0/-361)
Reviewer Review Type Date Requested Status
Cristian Gonzalez (community) Approve
Review via email: mp+406491@code.launchpad.net

Commit message

Remove utilities/findimports.py

Description of the change

It's just a lightly-modified old version of the `findimports` module on PyPI; we could upgrade it to a more current version and gain Python 3 support, but it isn't used by anything in our tree, so it doesn't seem worth the effort. If you need it then you should just install it from PyPI.

To post a comment you must log in.
Revision history for this message
Cristian Gonzalez (cristiangsp) wrote :

In love with all red MPs...

review: Approve

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

Subscribers

People subscribed via source and target branches

to status/vote changes: