1

我正在尝试将包含其依赖项的脚本打包到可执行的 zip 文件中,基本上遵循以下步骤:

$ mkdir build
$ pip install --target build -r requirements.txt
$ cp -r mypackage build
$ echo 'from mypackage import main; main()' > build/__main__.py
$ cd build
$ zip -r myscript-with-dependencies.zip *
$ echo '#!/usr/bin/env python' | cat - myscript-with-dependencies.zip \
    >../dist/myscript

这通常是有效的,除了.pth文件似乎没有被评估:它们被忽略site.addsitedir(zip_path)(可能是因为它似乎不是为了理解 zip 文件而编写的)。

这对我来说是一个特别的问题,因为的一些依赖项使用 setuptools namespace-packages,它的魔力主要发生在.pth文件中。

在我开始写之前,我addzipsitedir想知道是否有人有这种问题的经验,我是不是很明显?一般来说,zipimport 文档除了“它只是有效!”之外并没有说太多。(同样,它主要是这样做的,除了命名空间包和安装到鸡蛋中的东西等)

4

1 回答 1

1

确实,addsitedir()不处理 zip 文件;但它应该很容易复制行为。

site.addsitedir()源代码;该代码只是尝试调用os.listdir()路径然后发现.pth文件:

try:
    names = os.listdir(sitedir)
except os.error:
    return
dotpth = os.extsep + "pth"
names = [name for name in names if name.endswith(dotpth)]
for name in sorted(names):
    addpackage(sitedir, name, known_paths)

addpackage()只是在哪里site.addpackage()。您可以将上面names的列表替换为.pthzipfile 中的文件列表。您还必须复制该site.addpackage()行为,该函数期望能够读取.pth文件。

简化后,该函数执行以下操作:

with f:
    for n, line in enumerate(f):
        if line.startswith("#"):
            continue
        if line.startswith(("import ", "import\t")):
            exec line
            continue
        line = line.rstrip()
        dir, dircase = makepath(sitedir, line)
        if not dircase in known_paths and os.path.exists(dir):
            sys.path.append(dir)
            known_paths.add(dircase)

混合了异常处理。makepath()is site.makepath(),并known_paths确保找到的任何路径只添加一次。

因此,从本质上讲,所有以 by 命名的项目都会.pth添加到您的文件中,sys.path但以 开头的任何内容import都会在那里执行,然后为.pth文件提供一个进入site.py加载阶段的钩子。

基于 - 的包使用该import钩子setuptools来构建命名空间包;这是命名空间中的一个包中的一个zc

import sys,types,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('zc',)); ie = os.path.exists(os.path.join(p,'__init__.py')); m = not ie and sys.modules.setdefault('zc',types.ModuleType('zc')); mp = (m or []) and m.__dict__.setdefault('__path__',[]); (p not in mp) and mp.append(p)

sys.modules它使用函数中的sitedir局部变量创建一个空模块对象addpackage()。注意os.path.exists()通话;对于压缩鸡蛋,这将失败(不添加空模块对象),因此您可能需要检测命名空间包并为这些包提供您自己的版本。命名空间中的任何包都zc只是确保ModuleType()它关心的父命名空间有一个对象,其__path__属性指向{sitedir}/{packagename}/__init__.py.

另一种选择是将命名空间包合并到一个目录结构中。

于 2013-05-05T21:04:38.147 回答