[Python-checkins] r53194 - in sandbox/branches/setuptools-0.6: README.txt launcher.c setuptools.txt setuptools/cli.exe setuptools/command/easy_install.py setuptools/gui.exe

phillip.eby python-checkins at python.org
Fri Dec 29 18:43:40 CET 2006


Author: phillip.eby
Date: Fri Dec 29 18:43:39 2006
New Revision: 53194

Modified:
   sandbox/branches/setuptools-0.6/README.txt
   sandbox/branches/setuptools-0.6/launcher.c
   sandbox/branches/setuptools-0.6/setuptools.txt
   sandbox/branches/setuptools-0.6/setuptools/cli.exe
   sandbox/branches/setuptools-0.6/setuptools/command/easy_install.py
   sandbox/branches/setuptools-0.6/setuptools/gui.exe
Log:
Overhauled Windows script wrapping to support ``bdist_wininst`` better.
Scripts installed with ``bdist_wininst`` will always use ``#!python.exe`` or
``#!pythonw.exe`` as the executable name (even when built on non-Windows
platforms!), and the wrappers will look for the executable in the script's
parent directory (which should find the right version of Python).
(backport from trunk)


Modified: sandbox/branches/setuptools-0.6/README.txt
==============================================================================
--- sandbox/branches/setuptools-0.6/README.txt	(original)
+++ sandbox/branches/setuptools-0.6/README.txt	Fri Dec 29 18:43:39 2006
@@ -14,7 +14,8 @@
 
 Install setuptools using the provided ``.exe`` installer.  If you've previously
 installed older versions of setuptools, please delete all ``setuptools*.egg``
-files from your system FIRST.
+and ``setuptools.pth`` files from your system's ``site-packages`` directory
+(and any other ``sys.path`` directories) FIRST.
 
 If you are upgrading a previous version of setuptools that was installed using
 an ``.exe`` installer, please be sure to also *uninstall that older version*
@@ -23,9 +24,7 @@
 
 Once installation is complete, you will find an ``easy_install.exe`` program in
 your Python ``Scripts`` subdirectory.  Be sure to add this directory to your
-``PATH`` environment variable, if you haven't already done so.  You must also
-have your Python installation directory (e.g. ``C:\\Python23``) on the
-``PATH``.
+``PATH`` environment variable, if you haven't already done so.
 
 
 RPM-Based Systems

Modified: sandbox/branches/setuptools-0.6/launcher.c
==============================================================================
--- sandbox/branches/setuptools-0.6/launcher.c	(original)
+++ sandbox/branches/setuptools-0.6/launcher.c	Fri Dec 29 18:43:39 2006
@@ -22,6 +22,7 @@
     starting.  So, we have to use spawnv() and wait for Python to exit before
     continuing.  :(
 */
+
 #include <stdlib.h>
 #include <stdio.h>
 #include <unistd.h>
@@ -29,20 +30,25 @@
 #include "windows.h"
 
 int fail(char *format, char *data) {
-    /* Print error message to stderr and return 1 */
+    /* Print error message to stderr and return 2 */
     fprintf(stderr, format, data);
     return 2;
 }
 
+
+
+
+
 char *quoted(char *data) {
-    int i, l = strlen(data), nb;
+    int i, ln = strlen(data), nb;
+
     /* We allocate twice as much space as needed to deal with worse-case
        of having to escape everything. */
-    char *result = calloc(l*2+3, sizeof(char));
+    char *result = calloc(ln*2+3, sizeof(char));
     char *presult = result;
 
     *presult++ = '"';
-    for (nb=0, i=0; i < l; i++)
+    for (nb=0, i=0; i < ln; i++)
       {
         if (data[i] == '\\')
           nb += 1;
@@ -56,6 +62,7 @@
           nb = 0;
         *presult++ = data[i];
       }
+
     for (; nb > 0; nb--)        /* Deal w trailing slashes */
       *presult++ = '\\';
 
@@ -64,49 +71,108 @@
     return result;
 }
 
-char *getpyopt(char *python)
+
+
+
+
+
+
+
+
+
+char *loadable_exe(char *exename) {
+    HINSTANCE hPython;  /* DLL handle for python executable */
+    char *result;
+
+    hPython = LoadLibraryEx(exename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
+    if (!hPython) return NULL;
+
+    /* Return the absolute filename for spawnv */
+    result = calloc(MAX_PATH, sizeof(char));
+    if (result) GetModuleFileName(hPython, result, MAX_PATH);
+
+    FreeLibrary(hPython);
+    return result;
+}
+
+
+char *find_exe(char *exename, char *script) {
+    char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT];
+    char path[_MAX_PATH], c, *result;
+
+    /* convert slashes to backslashes for uniform search below */
+    result = exename;
+    while (c = *result++) if (c=='/') result[-1] = '\\';
+
+    _splitpath(exename, drive, dir, fname, ext);
+    if (drive[0] || dir[0]=='\\') {
+        return loadable_exe(exename);   /* absolute path, use directly */
+    }
+    /* Use the script's parent directory, which should be the Python home
+       (This should only be used for bdist_wininst-installed scripts, because
+        easy_install-ed scripts use the absolute path to python[w].exe
+    */
+    _splitpath(script, drive, dir, fname, ext);
+    result = dir + strlen(dir) -1;
+    if (*result == '\\') result--;
+    while (*result != '\\' && result>=dir) *result-- = 0;
+    _makepath(path, drive, dir, exename, NULL);
+    return loadable_exe(path);
+}
+
+
+char **parse_argv(char *cmdline, int *argc)
 {
-  /* Search a Python command string, read from a #! line for an
-     option.  An option must be separated from an executable name by
-     one or more spaces.  An option consistes of a hyphen followed by
-     one or more letters.
-   */
-  static char *letters = 
-    "abcdefghijklmnopqrstuvwxyz"
-    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-    ;
-  char *p = python + strlen(python) - 1;
-  if (strchr(letters, *p) == NULL)
-    return NULL; /* Path doen't end with a letter. Odd. */
-  while (p > python && strchr(letters, *p) != NULL)
-    p--;
-  if (p == python || *p != '-')
-    return NULL;                /* Can't be an option */
-  p--;
-  if (p > python && isspace(*p))
-    { /* BINGO, we have an option */
-      char *pyopt = p+1;
-      /* strip trailing spaces from remainder of python command */
-      while (p > python && isspace(*p))
-        *p-- = '\0';
-      return pyopt;
-    }
-  else
-    return NULL;
+    /* Parse a command line in-place using MS C rules */
+
+    char **result = calloc(strlen(cmdline), sizeof(char *));
+    char *output = cmdline;
+    char c;
+    int nb = 0;
+    *argc = 0;
+
+    result[0] = output;
+    while (isspace(*cmdline)) cmdline++;   /* skip leading spaces */
+
+    do {
+        c = *cmdline++;
+        if (!c || isspace(c)) {
+            while (nb) {*output++ = '\\'; nb--; }
+            *output++ = 0;
+            result[++*argc] = output;
+            if (!c) return result;
+            while (isspace(*cmdline)) cmdline++;  /* skip leading spaces */
+            continue;
+        }
+        if (c == '\\')
+            ++nb;   /* count \'s */
+        else {
+            if (c == '"') {
+                if (!(nb & 1)) c = 0;   /* skip " unless odd # of \ */
+                nb = nb >> 1;   /* cut \'s in half */
+            }
+            while (nb) {*output++ = '\\'; nb--; }
+            if (c) *output++ = c;
+        }
+    } while (1);
 }
 
+
+
+
+
+
 int run(int argc, char **argv, int is_gui) {
 
     char python[256];   /* python executable's filename*/
     char *pyopt;        /* Python option */
     char script[256];   /* the script's filename */
 
-    HINSTANCE hPython;  /* DLL handle for python executable */
     int scriptf;        /* file descriptor for script file */
 
-    char **newargs, **newargsp; /* argument array for exec */
+    char **newargs, **newargsp, **parsedargs; /* argument array for exec */
     char *ptr, *end;    /* working pointers for string manipulation */
-    int i;              /* loop counter */
+    int i, parsedargc;              /* loop counter */
 
     /* compute script name from our .exe name*/
     GetModuleFileName(NULL, script, sizeof(script));
@@ -126,57 +192,51 @@
     close(scriptf);
 
     ptr = python-1;
-    while(++ptr < end && *ptr && *ptr!='\n' && *ptr!='\r') {
-        if (*ptr=='/')
-            *ptr='\\';  /* convert slashes to avoid LoadLibrary crashes... */
-    }
+    while(++ptr < end && *ptr && *ptr!='\n' && *ptr!='\r') {;}
 
     *ptr-- = '\0';
-    while (ptr>python && isspace(*ptr)) *ptr-- = '\0';  /* strip trailing sp */
+
     if (strncmp(python, "#!", 2)) {
         /* default to python.exe if no #! header */
         strcpy(python, "#!python.exe");
     }
 
-    /* Check for Python options */
-    pyopt = getpyopt(python);
-
-    /* At this point, the python buffer contains "#!pythonfilename" */
+    parsedargs = parse_argv(python+2, &parsedargc);
 
     /* Using spawnv() can fail strangely if you e.g. find the Cygwin
        Python, so we'll make sure Windows can find and load it */
-    hPython = LoadLibraryEx(python+2, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
-    if (!hPython) {
-        return fail("Cannot find Python executable %s\n", python+2);
+
+    ptr = find_exe(parsedargs[0], script);
+    if (!ptr) {
+        return fail("Cannot find Python executable %s\n", parsedargs[0]);
     }
 
-    /* And we'll use the absolute filename for spawnv */
-    GetModuleFileName(hPython, python, sizeof(python));
+    /* printf("Python executable: %s\n", ptr); */
 
-    /* printf("Python executable: %s\n", python); */
+    /* Argument array needs to be
+       parsedargc + argc, plus 1 for null sentinel */
 
-    /* Argument array needs to be 
-       argc+1 for python executable,
-       plus 1 for possible python opts,
-       plus 1 for null sentinel */
-    newargs = (char **)calloc(argc+3, sizeof(char *));
+    newargs = (char **)calloc(parsedargc + argc + 1, sizeof(char *));
     newargsp = newargs;
-    *newargsp++ = quoted(python);
-    if (pyopt)
-      *newargsp++ = pyopt;
+
+    *newargsp++ = quoted(ptr);
+    for (i = 1; i<parsedargc; i++) *newargsp++ = quoted(parsedargs[i]);
+
     *newargsp++ = quoted(script);
-    for (i = 1; i < argc; i++)
-      *newargsp++ = quoted(argv[i]);
+    for (i = 1; i < argc; i++)     *newargsp++ = quoted(argv[i]);
+
     *newargsp++ = NULL;
 
     /* printf("args 0: %s\nargs 1: %s\n", newargs[0], newargs[1]); */
+
     if (is_gui) {
         /* Use exec, we don't need to wait for the GUI to finish */
         execv(python, (const char * const *)(newargs));
         return fail("Could not exec %s", python);   /* shouldn't get here! */
     }
+
     /* We *do* need to wait for a CLI to finish, so use spawn */
-    return spawnv(P_WAIT, python, (const char * const *)(newargs));
+    return spawnv(P_WAIT, ptr, (const char * const *)(newargs));
 }
 
 
@@ -184,8 +244,3 @@
     return run(__argc, __argv, GUI);
 }
 
-
-
-
-
-

Modified: sandbox/branches/setuptools-0.6/setuptools.txt
==============================================================================
--- sandbox/branches/setuptools-0.6/setuptools.txt	(original)
+++ sandbox/branches/setuptools-0.6/setuptools.txt	Fri Dec 29 18:43:39 2006
@@ -2601,6 +2601,13 @@
 ----------------------------
 
 0.6c4
+
+ * Overhauled Windows script wrapping to support ``bdist_wininst`` better.
+   Scripts installed with ``bdist_wininst`` will always use ``#!python.exe`` or
+   ``#!pythonw.exe`` as the executable name (even when built on non-Windows
+   platforms!), and the wrappers will look for the executable in the script's
+   parent directory (which should find the right version of Python).
+
  * Fix ``upload`` command not uploading files built by ``bdist_rpm`` or
    ``bdist_wininst`` under Python 2.3 and 2.4.
 

Modified: sandbox/branches/setuptools-0.6/setuptools/cli.exe
==============================================================================
Binary files. No diff available.

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 Dec 29 18:43:39 2006
@@ -369,16 +369,13 @@
 
     def install_egg_scripts(self, dist):
         """Write all the scripts for `dist`, unless scripts are excluded"""
-
+        if not self.exclude_scripts and dist.metadata_isdir('scripts'):
+            for script_name in dist.metadata_listdir('scripts'):
+                self.install_script(
+                    dist, script_name,
+                    dist.get_metadata('scripts/'+script_name).replace('\r','\n')
+                )
         self.install_wrapper_scripts(dist)
-        if self.exclude_scripts or not dist.metadata_isdir('scripts'):
-            return
-
-        for script_name in dist.metadata_listdir('scripts'):
-            self.install_script(
-                dist, script_name,
-                dist.get_metadata('scripts/'+script_name).replace('\r','\n')
-            )
 
     def add_output(self, path):
         if os.path.isdir(path):
@@ -408,6 +405,9 @@
 
 
 
+
+
+
     def easy_install(self, spec, deps=False):
         tmpdir = tempfile.mkdtemp(prefix="easy_install-")
         download = None
@@ -1407,7 +1407,6 @@
         else:
             return path
 
-
 def get_script_header(script_text, executable=sys_executable, wininst=False):
     """Create a #! line, getting options (if any) from script_text"""
     from distutils.command.build_scripts import first_line_re
@@ -1418,8 +1417,10 @@
         options = match.group(1) or ''
         if options:
             options = ' '+options
-    if wininst and sys.platform!='win32':
+    if wininst:
         executable = "python.exe"
+    else:
+        executable = nt_quote_arg(executable)
     hdr = "#!%(executable)s%(options)s\n" % locals()
     if unicode(hdr,'ascii','ignore').encode('ascii') != hdr:
         # Non-ascii path to sys.executable, use -x to prevent warnings
@@ -1432,7 +1433,6 @@
         hdr = "#!%(executable)s%(options)s\n" % locals()
     return hdr
 
-
 def auto_chmod(func, arg, exc):
     if func is os.remove and os.name=='nt':
         os.chmod(arg, stat.S_IWRITE)
@@ -1474,6 +1474,47 @@
 
 
 
+def nt_quote_arg(arg):
+    """Quote a command line argument according to Windows parsing rules"""
+
+    result = []
+    needquote = False
+    nb = 0
+
+    needquote = (" " in arg) or ("\t" in arg)
+    if needquote:
+        result.append('"')
+
+    for c in arg:
+        if c == '\\':
+            nb += 1
+        elif c == '"':
+            # double preceding backslashes, then add a \"
+            result.append('\\' * (nb*2) + '\\"')
+            nb = 0
+        else:
+            if nb:
+                result.append('\\' * nb)
+                nb = 0
+            result.append(c)
+
+    if nb:
+        result.append('\\' * nb)
+
+    if needquote:
+        result.append('\\' * nb)    # double the trailing backslashes
+        result.append('"')
+
+    return ''.join(result)
+
+
+
+
+
+
+
+
+
 def is_python_script(script_text, filename):
     """Is this text, as a whole, a Python script? (as opposed to shell/bat/etc.
     """
@@ -1532,7 +1573,7 @@
                 ")\n"
             ) % locals()
             if sys.platform=='win32' or wininst:
-                # On Windows, add a .py extension and an .exe launcher
+                # On Windows/wininst, add a .py extension and an .exe launcher
                 if group=='gui_scripts':
                     ext, launcher = '-script.pyw', 'gui.exe'
                     old = ['.pyw']
@@ -1540,9 +1581,9 @@
                 else:
                     ext, launcher = '-script.py', 'cli.exe'
                     old = ['.py','.pyc','.pyo']
-                    new_header = re.sub('(?i)pythonw.exe','pythonw.exe',header)
+                    new_header = re.sub('(?i)pythonw.exe','python.exe',header)
 
-                if os.path.exists(new_header[2:-1]):
+                if os.path.exists(new_header[2:-1]) or sys.platform!='win32':
                     hdr = new_header
                 else:
                     hdr = header

Modified: sandbox/branches/setuptools-0.6/setuptools/gui.exe
==============================================================================
Binary files. No diff available.


More information about the Python-checkins mailing list