在 PEX 程序中访问项目代码 --not-zip-safe
问题
当使用 PEX (https://github.com/pantsbuild/pex) 来打包一个 Python 项目时,会发现有些动态的源码访问方式无法使用。例如下面这段代码,通过动态遍历 Python 源文件来载入一个 package 下的所有模块:
# -*- coding: utf-8 -*-
import glob
from os.path import dirname, basename, join, isfile
modules = glob.glob(join(dirname(__file__), "*.py"))
__all__ = [
basename(f)[:-3] for f in modules if isfile(f) and f != "__init__.py"
]
from . import * # noqa
当使用 PEX 进行打包的时候,程序默认会使用 --zip-safe
参数,即在 PEX 程序执行的过程中,源码不会存在到磁盘上,因此 glob.glob()
会返回一个空列表,所以就无法载入任何模块。如果遇到这个情况,当你需要访问某些子模块时,就会触发如下的错误:
AttributeError: 'module' object has no attribute 'submodule_name'
解决方案
如果要支持这样的代码逻辑,需要使用 PEX 的 --not-zip-safe
模式。当启用该模式时,PEX 在运行程序前,会先把程序的代码解压到 ${PEX_ROOT}/code
目录下 ( PEX_ROOT 可以通过 --pex-root
参数指定,默认值是 ~/.pex/),然后再运行程序。这样代码中就可以使用上面这样的逻辑来动态的访问源码文件了。
但是,使用这个方法有个问题,就是每运行一次,代码就会被保留一份到 ${PEX_ROOT}/code
目录下一次,不仅会占用空间,而且会让别人可以方便的访问到源代码(虽然,直接解压 PEX 文件也能获得源代码)。所以,可以使用类似如下的代码,在程序退出前删除 PEX 解压出来的源代码:
def clean_pex_code_dir(f):
# We use default PEX_ROOT setting.
pex_root = os.path.expanduser("~/.pex")
code_path = os.path.join(pex_root, "code")
def deco(*args, **kwargs):
try:
return f(*args, **kwargs)
except Exception as e:
# It's necessary to raise catched exception for dumping traceback.
raise e
finally:
if os.path.exists(code_path):
shutil.rmtree(code_path)
return deco
@clean_pex_code_dir
def main():
pass
if __name__ == "__main__":
main()
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。