Created
December 9, 2014 17:59
-
-
Save ironpythonbot/6385a4e2c22c1ddd8424 to your computer and use it in GitHub Desktop.
CodePlex Issue #32420 Plain Text Attachments
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Find source library files for module dependencies of some set of source files. | |
Author: Dominique de Waleffe <ddewaleffe@gmail.com> | |
Copyright 2012 Dominique de Waleffe | |
For inclusion in IronPython | |
Licensed under the term of Apache license V2.0 as found in http://www.apache.org/licenses/LICENSE-2.0 | |
""" | |
#TODO: As this explores all the source files, it does redundant work that the compiler should be able to do. | |
#TODO: ast module seems to fail in some cases. (eg. trying to parse subprocess.py, random.py). See issue 32446. | |
import ast | |
from os.path import exists, join, abspath,splitext,basename | |
import traceback, sys | |
import re | |
import ast as _ast, re as _re | |
def secondBestGuess(filename): | |
"""This tries to determine the imports by matching lines against re's | |
It will surely match too many things in some cases....(like import lines in triple quotes string) | |
It will surely miss some other | |
But may do a good job in many of the normal cases... | |
""" | |
modules=[] | |
f=open(filename) | |
for line in f: | |
# try: import mod as alias,... | |
m1=re.match(r"^\s*import\s+(\w+\s*(\sas\s+\w+)?\s*(,\s*\w+\s*(\sas\s+\w+)?\s*)*)\W*(#.*)?$",line) | |
if m1: | |
#print "importing:", m1.group(1) | |
modules+= [ m.split(' as ')[0].split('.')[0].strip() for m in m1.group(1).split(',') ] | |
else: | |
# try: from mod import xxx | |
m2= re.match(r"^\s*from\s+([_a-zA-Z0-9.]+)\s+import.*$",line) | |
if m2: | |
#print "fromImport:", m2.group(1) | |
modules.append(m2.group(1).split('.')[0]) | |
f.close() | |
res={} | |
for m in set(modules): | |
res[m]=search_file(moduleFilename(m)) | |
return res | |
def search_file(filename): | |
def search_file2(filename, search_path): | |
"""Given a search path(as a list!), find file | |
[this function mostly copied from python cookbook :-)] | |
""" | |
file_found = 0 | |
for path in search_path: | |
if exists(join(path, filename)): | |
file_found = 1 | |
break | |
if file_found: | |
return abspath(join(path, filename)) | |
else: | |
return None | |
return search_file2(filename,sys.path) | |
def moduleFilename(moduleName): | |
"""Return file name for module name (which may have been composite: eg os.path)""" | |
return moduleName.split('.')[0]+".py" | |
def moduleName(fileName): | |
(_,module)=splitext(basename(fileName)) | |
return module | |
class GetImport(ast.NodeVisitor): | |
def __init__(self,fn): | |
self.importedModules={} | |
self.filename=fn | |
def visit_Import(self,node): | |
for m in node.names: | |
self.importedModules[m.name]=search_file(moduleFilename(m.name)) | |
# print "Import node", [a.name for a in node.names] | |
def visit_ImportFrom(self,node): | |
self.importedModules[node.module]=search_file(moduleFilename(node.module)) | |
# print "ImportFrom node: module=",node.module | |
def importedModules(self): | |
return self.importedModules | |
def direct_deps(self): | |
try: | |
f=open(self.filename) | |
src=f.read() | |
#print "Parsing file:", self.filename | |
root=ast.parse(src,self.filename) | |
#print "Visiting nodes..." | |
self.visit(root) | |
except Exception,e : | |
print "WARNING: Got exception while analyzing file"+self.filename | |
print "Maybe some dependencies will be missing in compiled executable!" | |
traceback.print_exc() | |
print "Using alternative method, less robust" | |
self.importedModules=secondBestGuess(self.filename) | |
# | |
finally: | |
f.close() | |
return self.importedModules | |
def direct_dependencies(fn): | |
return GetImport(fn).direct_deps() | |
def all_dependencies(files): | |
"""Intended to be called from pyc with base list of source files""" | |
def all_dependencies_int(modulesSofar,modulesTodo): | |
"""Recursively build a list of files corresponding to modules that are source dependencies for all modulesTodo""" | |
if modulesTodo == []: | |
# no more to process, we're done | |
return modulesSofar | |
(currentModule,currentFile)=modulesTodo[0] | |
stillTodo=modulesTodo[1:] | |
if currentModule in modulesSofar or currentFile is None: | |
# this module has already been processed or | |
# was not found (native module: file name will be None) | |
# ignore this one and process the rest... | |
return all_dependencies_int(modulesSofar,stillTodo) | |
# get direct dependency modules of current | |
moreModules=direct_dependencies(currentFile).items() | |
# continue processing | |
modulesSofar[currentModule]=currentFile | |
return all_dependencies_int(modulesSofar,stillTodo+moreModules) | |
return all_dependencies_int({},map(lambda f: (moduleName(f),f), files)).values() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment