Skip to content

Instantly share code, notes, and snippets.

@ironpythonbot
Created December 9, 2014 17:59
Show Gist options
  • Save ironpythonbot/6385a4e2c22c1ddd8424 to your computer and use it in GitHub Desktop.
Save ironpythonbot/6385a4e2c22c1ddd8424 to your computer and use it in GitHub Desktop.
CodePlex Issue #32420 Plain Text Attachments
"""
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