简体   繁体   English

从.py文件中获取包根和完整模块名称以进行导入

[英]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. 我正在寻找一种简单快速的方法来查找包的根目录和从.py文件的路径中找到完整的模块名称。

I want the user to choose a .py and import it without breaking. 我希望用户选择.py并导入它而不会中断。 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. 因此,我想自动将包的根所在的目录附加到sys.path(如果尚未存在),然后导入模块及其完整的模块名称。

I'm not running anywhere from the same directory or from that script, so I can't use __file__ and that kind of thing. 我没有从同一目录或该脚本运行任何地方,所以我不能使用__file__这样的事情。 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: 这是不可能的,因为命名空间包 - 没有办法确定baz.py的正确包是foo.bar还是只是具有以下文件结构的bar

foo/
    bar/
        __init__.py
        baz.py

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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