Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to use distutils to create executable .zip file?

Python 2.6 and beyond has the ability to directly execute a .zip file if the zip file contains a __main__.py file at the top of the zip archive. I'm wanting to leverage this feature to provide preview releases of a tool I'm developing that won't require users to install anything beyond copying the .zip file to their disk. Is there a standard way to create such a zip file? I'm looking for a solution that works with python 2.6 and python 2.7.

Ideally I would like to use distutils, since I already have it working when I want to do a normal install. Is there a canonical way to use (or extend) distutils to create such a .zip file?

distutils provides an sdist command which creates a source distribution that is almost right, but creates a structure that is a little too deep.

For example, my source tree looks like this:

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

When I do python setup.py sdist I end up with a .zip file with the following structure:

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

This isn't executable because __main__.py is not at the top of the distribution. Effectively what I want is a src distribution that doesn't include src, but only the files under src. That, or exactly what sdist gives me, but with an extra __main__.py at the top of the archive.

like image 627
Bryan Oakley Avatar asked May 31 '11 21:05

Bryan Oakley


2 Answers

Updated: Since the setup.cfg is global, it affects the 'install-lib' setting for all commands, which is not what is desired. Unfortunately there is no way (to my knowledge) to pass a options to a subcommand via the command line, e.g. if you specify bdist --install-lib=/ it will raise an error instead of passing that down to the subcommands.

To customize the install-lib for the install subcommand only when bdist is run, you can subclass the bdist_dumb command and set the path manually after the install subcommand is constructed / reinitialized:

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'}
    )

Running:

% 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.
like image 111
samplebias Avatar answered Oct 06 '22 07:10

samplebias


It is possible to make sdist executable too by placing the following __main__.py file to the root of your .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()

This adds source subdir from archive into the sys.path so that import from module1 becomes possible.

I can't tell right now how to patch distutils sdist command to inject this __main__.py into .zip automatically, but that's definitely real.

like image 30
anatoly techtonik Avatar answered Oct 06 '22 06:10

anatoly techtonik