[Python-checkins] r51969 - in sandbox/branches/setuptools-0.6: setup.py setuptools.egg-info/entry_points.txt setuptools.txt setuptools/command/bdist_egg.py setuptools/command/easy_install.py

phillip.eby python-checkins at python.org
Fri Sep 22 02:09:11 CEST 2006


Author: phillip.eby
Date: Fri Sep 22 02:09:06 2006
New Revision: 51969

Modified:
   sandbox/branches/setuptools-0.6/setup.py
   sandbox/branches/setuptools-0.6/setuptools.egg-info/entry_points.txt
   sandbox/branches/setuptools-0.6/setuptools.txt
   sandbox/branches/setuptools-0.6/setuptools/command/bdist_egg.py
   sandbox/branches/setuptools-0.6/setuptools/command/easy_install.py
Log:
Add support for "eggsecutable" headers: a /bin/sh script that is prepended
to an .egg file to allow it to be run as a script on Unix-ish platforms.
(This is mainly so that setuptools itself can have a single-file installer
on Unix, without doing multiple downloads, dealing with firewalls, etc.)
(Backport from trunk)


Modified: sandbox/branches/setuptools-0.6/setup.py
==============================================================================
--- sandbox/branches/setuptools-0.6/setup.py	(original)
+++ sandbox/branches/setuptools-0.6/setup.py	Fri Sep 22 02:09:06 2006
@@ -84,10 +84,13 @@
              "easy_install = setuptools.command.easy_install:main",
              "easy_install-%s = setuptools.command.easy_install:main"
                 % sys.version[:3]
-            ],
+        ],
 
         "setuptools.file_finders":
-            ["svn_cvs = setuptools.command.sdist:_default_revctrl"]
+            ["svn_cvs = setuptools.command.sdist:_default_revctrl"],
+
+        "setuptools.installation":
+            ['eggsecutable = setuptools.command.easy_install:bootstrap'],
         },
 
     classifiers = [f.strip() for f in """
@@ -118,6 +121,3 @@
 
 
 
-
-
-

Modified: sandbox/branches/setuptools-0.6/setuptools.egg-info/entry_points.txt
==============================================================================
--- sandbox/branches/setuptools-0.6/setuptools.egg-info/entry_points.txt	(original)
+++ sandbox/branches/setuptools-0.6/setuptools.egg-info/entry_points.txt	Fri Sep 22 02:09:06 2006
@@ -30,6 +30,9 @@
 easy_install = setuptools.command.easy_install:main
 easy_install-2.4 = setuptools.command.easy_install:main
 
+[setuptools.installation]
+eggsecutable = setuptools.command.easy_install:bootstrap
+
 [distutils.commands]
 bdist_rpm = setuptools.command.bdist_rpm:bdist_rpm
 rotate = setuptools.command.rotate:rotate
@@ -44,10 +47,10 @@
 alias = setuptools.command.alias:alias
 easy_install = setuptools.command.easy_install:easy_install
 install_scripts = setuptools.command.install_scripts:install_scripts
+bdist_wininst = setuptools.command.bdist_wininst:bdist_wininst
 bdist_egg = setuptools.command.bdist_egg:bdist_egg
 install = setuptools.command.install:install
 test = setuptools.command.test:test
 install_lib = setuptools.command.install_lib:install_lib
 build_ext = setuptools.command.build_ext:build_ext
 sdist = setuptools.command.sdist:sdist
-

Modified: sandbox/branches/setuptools-0.6/setuptools.txt
==============================================================================
--- sandbox/branches/setuptools-0.6/setuptools.txt	(original)
+++ sandbox/branches/setuptools-0.6/setuptools.txt	Fri Sep 22 02:09:06 2006
@@ -498,6 +498,43 @@
 Services and Plugins`_.
 
 
+"Eggsecutable" Scripts
+----------------------
+
+Occasionally, there are situations where it's desirable to make an ``.egg``
+file directly executable.  You can do this by including an entry point such
+as the following::
+
+    setup(
+        # other arguments here...
+        entry_points = {
+            'setuptools.installation': [
+                'eggsecutable = my_package.some_module:main_func',
+            ]
+        }
+    )
+
+Any eggs built from the above setup script will include a short excecutable
+prelude that imports and calls ``main_func()`` from ``my_package.some_module``.
+The prelude can be run on Unix-like platforms (including Mac and Linux) by
+invoking the egg with ``/bin/sh``, or by enabling execute permissions on the
+``.egg`` file.  For the executable prelude to run, the appropriate version of
+Python must be available via the ``PATH`` environment variable, under its
+"long" name.  That is, if the egg is built for Python 2.3, there must be a
+``python2.3`` executable present in a directory on ``PATH``.
+
+This feature is primarily intended to support bootstrapping the installation of
+setuptools itself on non-Windows platforms, but may also be useful for other
+projects as well.
+
+IMPORTANT NOTE: Eggs with an "eggsecutable" header cannot be renamed, or
+invoked via symlinks.  They *must* be invoked using their original filename, in
+order to ensure that, once running, ``pkg_resources`` will know what project
+and version is in use.  The header script will check this and exit with an
+error if the ``.egg`` file has been renamed or is invoked via a symlink that
+changes its base name.
+
+
 Declaring Dependencies
 ======================
 
@@ -2567,6 +2604,12 @@
  * Fix ``upload`` command not uploading files built by ``bdist_rpm`` or
    ``bdist_wininst`` under Python 2.3 and 2.4.
 
+ * Add support for "eggsecutable" headers: a ``#!/bin/sh`` script that is
+   prepended to an ``.egg`` file to allow it to be run as a script on Unix-ish
+   platforms.  (This is mainly so that setuptools itself can have a single-file
+   installer on Unix, without doing multiple downloads, dealing with firewalls,
+   etc.)
+
 0.6c3
  * Fixed breakages caused by Subversion 1.4's new "working copy" format
 

Modified: sandbox/branches/setuptools-0.6/setuptools/command/bdist_egg.py
==============================================================================
--- sandbox/branches/setuptools-0.6/setuptools/command/bdist_egg.py	(original)
+++ sandbox/branches/setuptools-0.6/setuptools/command/bdist_egg.py	Fri Sep 22 02:09:06 2006
@@ -8,7 +8,9 @@
 from distutils.dir_util import remove_tree, mkpath
 from distutils.sysconfig import get_python_version, get_python_lib
 from distutils import log
+from distutils.errors import DistutilsSetupError
 from pkg_resources import get_build_platform, Distribution, ensure_directory
+from pkg_resources import EntryPoint
 from types import CodeType
 from setuptools.extension import Library
 
@@ -37,8 +39,6 @@
 # stub __init__.py for packages distributed without one
 NS_PKG_STUB = '__import__("pkg_resources").declare_namespace(__name__)'
 
-
-
 class bdist_egg(Command):
 
     description = "create an \"egg\" distribution"
@@ -233,7 +233,7 @@
 
         # Make the archive
         make_zipfile(self.egg_output, archive_root, verbose=self.verbose,
-                          dry_run=self.dry_run)
+                          dry_run=self.dry_run, mode=self.gen_header())
         if not self.keep_temp:
             remove_tree(self.bdist_dir, dry_run=self.dry_run)
 
@@ -285,6 +285,47 @@
 
         return init_files
 
+    def gen_header(self):
+        epm = EntryPoint.parse_map(self.distribution.entry_points or '')
+        ep = epm.get('setuptools.installation',{}).get('eggsecutable')
+        if ep is None:
+            return 'w'  # not an eggsecutable, do it the usual way.
+
+        if not ep.attrs or ep.extras:
+            raise DistutilsSetupError(
+                "eggsecutable entry point (%r) cannot have 'extras' "
+                "or refer to a module" % (ep,)
+            )
+
+        pyver = sys.version[:3]
+        pkg = ep.module_name
+        full = '.'.join(ep.attrs)
+        base = ep.attrs[0]
+        basename = os.path.basename(self.egg_output)
+
+        header = (
+            "#!/bin/sh\n"
+            'if [[ `basename $0` = "%(basename)s" ]]\n'
+            'then exec python%(pyver)s -c "'
+            "import sys, os; sys.path.insert(0, os.path.abspath('$0')); "
+            "from %(pkg)s import %(base)s; sys.exit(%(full)s())"
+            '" "$@"\n'
+            'else\n'
+            '  echo $0 is not the correct name for this egg file.\n'
+            '  echo Please rename it back to %(basename)s and try again.\n'
+            '  exec false\n'
+            'fi\n'
+
+        ) % locals()
+
+        if not self.dry_run:
+            f = open(self.egg_output, 'w')
+            f.write(header)
+            f.close()
+        return 'a'
+
+
+
     def copy_metadata_to(self, target_dir):
         prefix = os.path.join(self.egg_info,'')
         for path in self.ei_cmd.filelist.files:
@@ -415,7 +456,9 @@
     'install_lib', 'install_dir', 'install_data', 'install_base'
 ]
 
-def make_zipfile (zip_filename, base_dir, verbose=0, dry_run=0, compress=None):
+def make_zipfile(zip_filename, base_dir, verbose=0, dry_run=0, compress=None,
+    mode='w'
+):
     """Create a zip file from all the files under 'base_dir'.  The output
     zip file will be named 'base_dir' + ".zip".  Uses either the "zipfile"
     Python module (if available) or the InfoZIP "zip" utility (if installed
@@ -426,7 +469,7 @@
     mkpath(os.path.dirname(zip_filename), dry_run=dry_run)
     log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir)
 
-    def visit (z, dirname, names):
+    def visit(z, dirname, names):
         for name in names:
             path = os.path.normpath(os.path.join(dirname, name))
             if os.path.isfile(path):
@@ -440,12 +483,10 @@
 
     compression = [zipfile.ZIP_STORED, zipfile.ZIP_DEFLATED][bool(compress)]
     if not dry_run:
-        z = zipfile.ZipFile(zip_filename, "w", compression=compression)
+        z = zipfile.ZipFile(zip_filename, mode, compression=compression)
         os.path.walk(base_dir, visit, z)
         z.close()
     else:
         os.path.walk(base_dir, visit, None)
-
     return zip_filename
-
 #

Modified: sandbox/branches/setuptools-0.6/setuptools/command/easy_install.py
==============================================================================
--- sandbox/branches/setuptools-0.6/setuptools/command/easy_install.py	(original)
+++ sandbox/branches/setuptools-0.6/setuptools/command/easy_install.py	Fri Sep 22 02:09:06 2006
@@ -1550,10 +1550,10 @@
     except os.error:
         onerror(os.rmdir, path, sys.exc_info())
 
-
-
-
-
+def bootstrap():
+    # This function is called when setuptools*.egg is run using /bin/sh
+    import setuptools; argv0 = os.path.dirname(setuptools.__path__[0])
+    sys.argv[0] = argv0; sys.argv.append(argv0); main()
 
 
 def main(argv=None, **kw):


More information about the Python-checkins mailing list