如何使用distutils创建可执行的.zip文件?

7 投票
2 回答
5852 浏览
提问于 2025-04-16 18:42

从Python 2.6开始,如果一个.zip文件里有一个叫做__main__.py的文件在最上面,Python就可以直接运行这个.zip文件。我想利用这个功能,提供我正在开发的工具的预览版本,这样用户只需要把.zip文件复制到他们的电脑上,就可以使用,不需要安装其他东西。请问有没有标准的方法来创建这样的.zip文件?我希望这个方法能在Python 2.6和2.7上都能用。

理想情况下,我想使用distutils,因为我在进行正常安装时已经成功使用过它。有没有一种标准的方法可以使用(或扩展)distutils来创建这样的.zip文件呢?

distutils提供了一个sdist命令,可以创建一个源代码分发包,这个包几乎是正确的,但它的结构有点太复杂了。

比如说,我的源代码树是这样的:

my_package/
  - setup.py
  - src/
      - __main__.py
      - module1/
      - module2/
      - module3/

当我运行python setup.py sdist时,最终得到的.zip文件结构是这样的:

my_package-0.1.zip
  - my_package-0.1/
      - README.txt
      - PKG_INFO
      - src/
          - __main__.py
          - module1/
          - module2/
          - module3/

这个文件不能直接执行,因为__main__.py不在分发包的最上面。实际上,我想要的是一个源代码分发包,但不包含src这个文件夹,只包含src文件夹下的文件。或者说,正好是sdist给我的结构,但在压缩包的最上面多一个__main__.py文件。

2 个回答

1

你可以通过把一个叫做 __main__.py 的文件放到你的 .zip 文件的根目录下,让 sdist 也变得可以执行。

import os
import sys

# add package .zip to python lookup path
__dir__ = os.path.dirname(__file__)
path = os.path.join(__dir__, 'my_package-0.1', 'src')
sys.path.insert(0, path)

import module1
module1.main()

这样做会把压缩包中的源代码子目录添加到 sys.path 中,这样就可以从 module1 中导入内容了。

我现在不能告诉你怎么修改 distutils 的 sdist 命令,让它自动把这个 __main__.py 文件放进 .zip 里,但这确实是可行的。

5

更新: 因为 setup.cfg 是全局的,所以它会影响所有命令的 'install-lib' 设置,这并不是我们想要的。不幸的是,按照我所知,无法通过命令行将选项传递给子命令。例如,如果你指定 bdist --install-lib=/,它会报错,而不是将这个选项传递给子命令。

如果你想在运行 bdist 时,仅仅为 install 子命令自定义 install-lib,你可以通过创建一个 bdist_dumb 命令的子类,并在构造/重新初始化 install 子命令后手动设置路径来实现:

setup.py

from distutils.core import setup
from distutils.command.bdist_dumb import bdist_dumb

class custom_bdist_dumb(bdist_dumb):

    def reinitialize_command(self, name, **kw):
        cmd = bdist_dumb.reinitialize_command(self, name, **kw)
        if name == 'install':
            cmd.install_lib = '/'
        return cmd

if __name__ == '__main__':
    setup(
        # our custom class override
        cmdclass = {'bdist_dumb': custom_bdist_dumb},
        name='my_package',
        py_modules = ['__main__'],
        packages = ['module1', 'module2'],
        package_dir = {'': 'src'}
    )

运行:

% python setup.py bdist --format=zip
% unzip -l dist/my_package-0.0.0.linux-x86_64.zip
Archive:  dist/my_package-0.0.0.linux-x86_64.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
      184  2011-05-31 20:34   my_package-0.0.0.egg-info
       30  2011-05-31 20:34   __main__.py
      128  2011-05-31 20:34   __main__.pyc
      107  2011-05-31 20:34   module1/__init__.pyc
        0  2011-05-31 20:27   module1/__init__.py
      107  2011-05-31 20:34   module2/__init__.pyc
        0  2011-05-31 20:27   module2/__init__.py
---------                     -------
      556                     7 files

% python dist/my_package-0.0.0.linux-x86_64.zip
my_package working.

撰写回答