Get root directory and full name of module from .py file for import
I am looking for an easy and quick way to find the package root and fully qualified module name from a .py file path.
I want the user to select the .py and import it without breaking. Import a module if part of the package is likely to break. Thus, I want to automatically add the directory where the package root is in sys.path (if it doesn't already exist) and then import the module with the fully qualified module name.
I am not running anywhere from the same directory or from this script, so I cannot use __file__
something like that either. Also, I haven't imported the module yet, so I can't (as far as I know) check the module object because it doesn't exist.
This is a working version, but I am interested in finding easier / faster solutions.
def splitPathFull(path):
folders=[]
while 1:
path,folder=os.path.split(path)
if folder!="":
folders.append(folder)
else:
if path!="":
folders.append(path)
break
folders.reverse()
return folders
def getPackageRootAndModuleNameFromFilePath(filePath):
"""
It recursively looks up until it finds a folder without __init__.py and uses that as the root of the package
the root of the package.
"""
folder = os.path.dirname(filePath)
if not os.path.exists( folder ):
raise RuntimeError( "Location does not exist: {0}".format(folder) )
if not filePath.endswith(".py"):
return None
moduleName = os.path.splitext( os.path.basename(filePath) )[0] # filename without extension
#
# If there a __init__.py in the folder:
# Find the root module folder by recursively going up until there no more __init__.py
# Else:
# It a standalone module/python script.
#
foundScriptRoot = False
fullModuleName = None
rootPackagePath = None
if not os.path.exists( os.path.join(folder, "__init__.py" ) ):
rootPackagePath = folder
fullModuleName = moduleName
foundScriptRoot = True
# It not in a Python package but a seperate ".py" script
# Thus append it directory name to sys path (if not in there) and import the .py as a module
else:
startFolder = folder
moduleList = []
if moduleName != "__init__":
moduleList.append(moduleName)
amountUp = 0
while os.path.exists( folder ) and foundScriptRoot == False:
moduleList.append ( os.path.basename(folder) )
folder = os.path.dirname(folder)
amountUp += 1
if not os.path.exists( os.path.join(folder, "__init__.py" ) ):
foundScriptRoot = True
splitPath = splitPathFull(startFolder)
rootPackagePath = os.path.join( *splitPath[:-amountUp] )
moduleList.reverse()
fullModuleName = ".".join(moduleList)
if fullModuleName == None or rootPackagePath == None or foundScriptRoot == False:
raise RuntimeError( "Couldn't resolve python package root python path and full module name for: {0}".format(filePath) )
return [rootPackagePath, fullModuleName]
def importModuleFromFilepath(filePath, reloadModule=True):
"""
Imports a module by it filePath.
It adds the root folder to sys.path if it not already in there.
Then it imports the module with the full package/module name and returns the imported module as object.
"""
rootPythonPath, fullModuleName = getPackageRootAndModuleNameFromFilePath(filePath)
# Append rootPythonPath to sys.path if not in sys.path
if rootPythonPath not in sys.path:
sys.path.append(rootPythonPath)
# Import full (module.module.package) name
mod = __import__( fullModuleName, {}, {}, [fullModuleName] )
if reloadModule:
reload(mod)
return mod
source to share
This is not possible due to namespace packages - there is simply no way to decide if the correct package is for baz.py
foo.bar
or just bar
with the following file structure:
foo/ bar/ __init__.py baz.py
source to share