как использовать distutils для создания исполняемого файла .zip?

Python 2.6 и более поздние версии имеют возможность напрямую выполнять ZIP-файл, если ZIP-файл содержит файл __main__.py в начале ZIP-архива. Я хочу использовать эту функцию, чтобы предоставлять предварительные версии инструмента, который я разрабатываю, который не потребует от пользователей установки чего-либо, кроме копирования ZIP-файла на свой диск. Есть ли стандартный способ создать такой zip-файл? Я ищу решение, которое работает с python 2.6 и python 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, а только файлы под src. Это или именно то, что мне дает sdist, но с дополнительным __main__.py вверху архива.


person Bryan Oakley    schedule 31.05.2011    source источник


Ответы (2)


Обновлено: поскольку setup.cfg является глобальным, он влияет на параметр install-lib для всех команд, что нежелательно. К сожалению, нет способа (насколько мне известно) передать параметры подкоманде через командную строку, например. если вы укажете bdist --install-lib=/, это вызовет ошибку вместо того, чтобы передать ее подкомандам.

Чтобы настроить install-lib для подкоманды install только при запуске bdist, вы можете подклассировать команду 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.
person samplebias    schedule 01.06.2011
comment
Однако я обнаружил, что это означает, что установка setup.py завершается ошибкой, потому что она пытается записать в /. Мне нужна возможность как создать файл .zip, так и установить обычным способом. - person Bryan Oakley; 09.06.2011
comment
@Bryan Я обновил ответ, чтобы исправить это, и вы можете удалить изменения setup.cfg. Надеюсь, решение работает для вас. - person samplebias; 09.06.2011
comment
FWIW, когда я запустил это в Windows в оболочке cygwin (не уверен, что это имеет значение ...), я получил «ValueError: путь»/ «не может быть абсолютным». Замена cmd.install_lib = '/' на cmd.install_lib = '\\' устранила эту проблему. - person Bryan Oakley; 06.02.2012

Также можно сделать исполняемым файл sdist, поместив следующий файл __main__.py в корень вашего .zip:

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, но это определенно реально.

person anatoly techtonik    schedule 07.01.2015