将zip文件视为站点目录的标准方法?

时间:2013-05-05 20:41:41

标签: python zip packaging

我正在尝试将包含其依赖项的脚本打包成可执行的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)会忽略它们(可能是因为it doesn't seem to have been written to understand zip files)。

这对 me 来说尤其是一个问题,因为我的某些依赖项使用的是setuptools namespace-packages,其魔力大多发生在.pth个文件中。

在我离开之前写下像addzipsitedir之类的东西,我想知道是否有人遇到过这类问题,我有什么明显之处吗?一般来说,zipimport docs并没有说“它只是有效!”。 (除了命名空间包和安装到鸡蛋中的东西等之外,它主要是这样做的)。

1 个答案:

答案 0 :(得分: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列表替换为zipfile中的.pth个文件列表。您还必须复制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()site.makepath()known_paths确保找到的所有路径只添加一次。

因此,实质上,.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

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