简体   繁体   中英

Get package root and full module name from .py file for importing

I'm looking for a simple and fast way to find the root of the package and the full module name from a path to .py file.

I want the user to choose a .py and import it without breaking. Importing a module if it's part of the package will likely break. Thus I want to automatically append the directory wherein the root of the package resides to sys.path (if not already in there) and then import the module with its full module name.

I'm not running anywhere from the same directory or from that script, so I can't use __file__ and that kind of thing. Also I don't have the module imported yet, so I can't (as far as I know) inspect the module object, because there is none.

This is a working version, yet I'm 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's a __init__.py in the folder:
    #   Find the root module folder by recursively going up until there's no more __init__.py
    # Else:
    #   It's 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's not in a Python package but a seperate ".py" script
        # Thus append it's 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's filePath.
        It adds the root folder to sys.path if it's 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

This is impossible due to namespace packages - there is simply no way to decide whether the correct package for baz.py is foo.bar or just bar with the following file structure:

foo/
    bar/
        __init__.py
        baz.py

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM