| 
									
										
										
										
											2013-05-15 16:14:11 +02:00
										 |  |  | #!/usr/bin/env python | 
					
						
							| 
									
										
										
										
											2013-05-15 13:17:33 +02:00
										 |  |  | ############################################################################# | 
					
						
							|  |  |  | ## | 
					
						
							| 
									
										
										
										
											2014-01-07 13:27:11 +01:00
										 |  |  | ## Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). | 
					
						
							| 
									
										
										
										
											2013-05-15 13:17:33 +02:00
										 |  |  | ## Contact: http://www.qt-project.org/legal | 
					
						
							|  |  |  | ## | 
					
						
							|  |  |  | ## This file is part of Qt Creator. | 
					
						
							|  |  |  | ## | 
					
						
							|  |  |  | ## Commercial License Usage | 
					
						
							|  |  |  | ## Licensees holding valid commercial Qt licenses may use this file in | 
					
						
							|  |  |  | ## accordance with the commercial license agreement provided with the | 
					
						
							|  |  |  | ## Software or, alternatively, in accordance with the terms contained in | 
					
						
							|  |  |  | ## a written agreement between you and Digia.  For licensing terms and | 
					
						
							|  |  |  | ## conditions see http://qt.digia.com/licensing.  For further information | 
					
						
							|  |  |  | ## use the contact form at http://qt.digia.com/contact-us. | 
					
						
							|  |  |  | ## | 
					
						
							|  |  |  | ## GNU Lesser General Public License Usage | 
					
						
							|  |  |  | ## Alternatively, this file may be used under the terms of the GNU Lesser | 
					
						
							|  |  |  | ## General Public License version 2.1 as published by the Free Software | 
					
						
							|  |  |  | ## Foundation and appearing in the file LICENSE.LGPL included in the | 
					
						
							|  |  |  | ## packaging of this file.  Please review the following information to | 
					
						
							|  |  |  | ## ensure the GNU Lesser General Public License version 2.1 requirements | 
					
						
							|  |  |  | ## will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | 
					
						
							|  |  |  | ## | 
					
						
							|  |  |  | ## In addition, as a special exception, Digia gives you certain additional | 
					
						
							|  |  |  | ## rights.  These rights are described in the Digia Qt LGPL Exception | 
					
						
							|  |  |  | ## version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | 
					
						
							|  |  |  | ## | 
					
						
							|  |  |  | ############################################################################# | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-28 11:14:20 +02:00
										 |  |  | import os | 
					
						
							|  |  |  | import sys | 
					
						
							|  |  |  | import tokenize | 
					
						
							|  |  |  | from optparse import OptionParser | 
					
						
							| 
									
										
										
										
											2012-10-23 15:15:45 +02:00
										 |  |  | from toolfunctions import checkDirectory | 
					
						
							|  |  |  | from toolfunctions import getFileContent | 
					
						
							| 
									
										
										
										
											2012-09-28 11:14:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | objMap = None | 
					
						
							| 
									
										
										
										
											2013-04-08 11:18:43 +02:00
										 |  |  | lastToken = [None, None] | 
					
						
							|  |  |  | stopTokens = ('OP', 'NAME', 'NUMBER', 'ENDMARKER') | 
					
						
							| 
									
										
										
										
											2012-09-28 11:14:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | def parseCommandLine(): | 
					
						
							| 
									
										
										
										
											2013-07-18 09:26:56 +02:00
										 |  |  |     global directory, onlyRemovable | 
					
						
							| 
									
										
										
										
											2012-09-28 11:14:20 +02:00
										 |  |  |     parser = OptionParser("\n%prog [OPTIONS] [DIRECTORY]") | 
					
						
							|  |  |  |     parser.add_option("-o", "--only-removable", dest="onlyRemovable", | 
					
						
							|  |  |  |                       action="store_true", default=False, | 
					
						
							|  |  |  |                       help="list removable objects only") | 
					
						
							|  |  |  |     (options, args) = parser.parse_args() | 
					
						
							|  |  |  |     if len(args) == 0: | 
					
						
							|  |  |  |         directory = os.path.abspath(".") | 
					
						
							|  |  |  |     elif len(args) == 1: | 
					
						
							|  |  |  |         directory = os.path.abspath(args[0]) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         print "\nERROR: Too many arguments\n" | 
					
						
							|  |  |  |         parser.print_help() | 
					
						
							|  |  |  |         sys.exit(1) | 
					
						
							|  |  |  |     onlyRemovable = options.onlyRemovable | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def collectObjects(): | 
					
						
							|  |  |  |     global objMap | 
					
						
							|  |  |  |     data = getFileContent(objMap) | 
					
						
							|  |  |  |     return map(lambda x: x.strip().split("\t", 1)[0], data.strip().splitlines()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-08 11:18:43 +02:00
										 |  |  | def handleStringsWithTrailingBackSlash(origStr): | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         while True: | 
					
						
							|  |  |  |             index = origStr.index("\\\n") | 
					
						
							|  |  |  |             origStr = origStr[:index] + origStr[index+2:].lstrip() | 
					
						
							|  |  |  |     except: | 
					
						
							|  |  |  |         return origStr | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-28 11:14:20 +02:00
										 |  |  | def handle_token(tokenType, token, (startRow, startCol), (endRow, endCol), line): | 
					
						
							| 
									
										
										
										
											2013-04-08 11:18:43 +02:00
										 |  |  |     global useCounts, lastToken, stopTokens | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-28 11:14:20 +02:00
										 |  |  |     if tokenize.tok_name[tokenType] == 'STRING': | 
					
						
							| 
									
										
										
										
											2013-04-08 11:18:43 +02:00
										 |  |  |         # concatenate strings followed directly by other strings | 
					
						
							|  |  |  |         if lastToken[0] == 'STRING': | 
					
						
							|  |  |  |             token = "'" + lastToken[1][1:-1] + str(token)[1:-1] + "'" | 
					
						
							|  |  |  |         # store the new string as lastToken after removing potential trailing backslashes | 
					
						
							|  |  |  |         # (including their following indentation) | 
					
						
							|  |  |  |         lastToken = ['STRING' , handleStringsWithTrailingBackSlash(str(token))] | 
					
						
							|  |  |  |     # if a stop token occurs check the potential string before it | 
					
						
							|  |  |  |     elif tokenize.tok_name[tokenType] in stopTokens: | 
					
						
							|  |  |  |         if lastToken[0] == 'STRING': | 
					
						
							|  |  |  |             for obj in useCounts: | 
					
						
							|  |  |  |                 useCounts[obj] += lastToken[1].count("'%s'" % obj) | 
					
						
							|  |  |  |                 useCounts[obj] += lastToken[1].count('"%s"' % obj) | 
					
						
							|  |  |  |         # store the stop token as lastToken | 
					
						
							|  |  |  |         lastToken = [tokenize.tok_name[tokenType], str(token)] | 
					
						
							| 
									
										
										
										
											2012-09-28 11:14:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-18 09:26:56 +02:00
										 |  |  | def handleDataFiles(openFile, separator): | 
					
						
							|  |  |  |     global useCounts | 
					
						
							|  |  |  |     # ignore header line | 
					
						
							|  |  |  |     openFile.readline() | 
					
						
							|  |  |  |     for line in openFile: | 
					
						
							|  |  |  |         currentTokens = line.split(separator) | 
					
						
							|  |  |  |         for token in currentTokens: | 
					
						
							|  |  |  |             stripped = token.strip().strip('"') | 
					
						
							|  |  |  |             if stripped in useCounts: | 
					
						
							|  |  |  |                 useCounts[stripped] = useCounts[stripped] + 1 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-28 11:14:20 +02:00
										 |  |  | def findUsages(): | 
					
						
							|  |  |  |     global directory, objMap | 
					
						
							| 
									
										
										
										
											2013-07-18 09:26:56 +02:00
										 |  |  |     suffixes = (".py", ".csv", ".tsv") | 
					
						
							| 
									
										
										
										
											2012-09-28 11:14:20 +02:00
										 |  |  |     for root, dirnames, filenames in os.walk(directory): | 
					
						
							| 
									
										
										
										
											2013-07-18 09:26:56 +02:00
										 |  |  |         for filename in filter(lambda x: x.endswith(suffixes), filenames): | 
					
						
							| 
									
										
										
										
											2012-09-28 11:14:20 +02:00
										 |  |  |             currentFile = open(os.path.join(root, filename)) | 
					
						
							| 
									
										
										
										
											2013-07-18 09:26:56 +02:00
										 |  |  |             if filename.endswith(".py"): | 
					
						
							|  |  |  |                 tokenize.tokenize(currentFile.readline, handle_token) | 
					
						
							|  |  |  |             elif filename.endswith(".csv"): | 
					
						
							|  |  |  |                 handleDataFiles(currentFile, ",") | 
					
						
							|  |  |  |             elif filename.endswith(".tsv"): | 
					
						
							|  |  |  |                 handleDataFiles(currentFile, "\t") | 
					
						
							| 
									
										
										
										
											2012-09-28 11:14:20 +02:00
										 |  |  |             currentFile.close() | 
					
						
							|  |  |  |     currentFile = open(objMap) | 
					
						
							|  |  |  |     tokenize.tokenize(currentFile.readline, handle_token) | 
					
						
							|  |  |  |     currentFile.close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def printResult(): | 
					
						
							|  |  |  |     global useCounts, onlyRemovable | 
					
						
							|  |  |  |     print | 
					
						
							|  |  |  |     if onlyRemovable: | 
					
						
							|  |  |  |         if min(useCounts.values()) > 0: | 
					
						
							|  |  |  |             print "All objects are used once at least.\n" | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  |         print "Unused objects:\n" | 
					
						
							|  |  |  |         for obj in filter(lambda x: useCounts[x] == 0, useCounts): | 
					
						
							|  |  |  |             print "%s" % obj | 
					
						
							|  |  |  |         return True | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         length = max(map(len, useCounts.keys())) | 
					
						
							|  |  |  |         outFormat = "%%%ds %%3d" % length | 
					
						
							|  |  |  |         for obj,useCount in useCounts.iteritems(): | 
					
						
							|  |  |  |             print outFormat % (obj, useCount) | 
					
						
							|  |  |  |         print | 
					
						
							|  |  |  |     return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def main(): | 
					
						
							| 
									
										
										
										
											2012-10-23 15:15:45 +02:00
										 |  |  |     global useCounts, objMap | 
					
						
							|  |  |  |     objMap = checkDirectory(directory) | 
					
						
							| 
									
										
										
										
											2012-09-28 11:14:20 +02:00
										 |  |  |     useCounts = dict.fromkeys(collectObjects(), 0) | 
					
						
							|  |  |  |     findUsages() | 
					
						
							|  |  |  |     atLeastOneRemovable = printResult() | 
					
						
							|  |  |  |     if atLeastOneRemovable: | 
					
						
							|  |  |  |         print "\nAfter removing the listed objects you should re-run this tool" | 
					
						
							|  |  |  |         print "to find objects that might have been used only by these objects.\n" | 
					
						
							|  |  |  |     return 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     parseCommandLine() | 
					
						
							|  |  |  |     sys.exit(main()) |