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

phillip.eby python-checkins at python.org
Fri Dec 29 18:33:16 CET 2006


Author: phillip.eby
Date: Fri Dec 29 18:33:14 2006
New Revision: 53191

Modified:
   sandbox/trunk/setuptools/README.txt
   sandbox/trunk/setuptools/launcher.c
   sandbox/trunk/setuptools/setuptools/cli.exe
   sandbox/trunk/setuptools/setuptools/command/easy_install.py
   sandbox/trunk/setuptools/setuptools/gui.exe
Log:
Overhaul Windows script wrappers to support bdist_wininst better.  
Scripts installed with bdist_wininst will always use #!python.exe or 
#!pythonw.exe as the executable name, and the wrappers will look for the 
executable in the script's parent directory.  Since bdist_wininst 
scripts are installed to Python2X/Scripts, this will look for 
Python2X/python.exe.  Scripts installed by easy_install, however, will 
still use an absolute executable path.  Also fixed: some egg<->exe
roundtripping issues, and script #! lines on Windows can now have an 
arbitrary number of arguments following the Python exe name, and the  
exe name is quoted if necessary, following the MS command line parsing
rules.


Modified: sandbox/trunk/setuptools/README.txt
==============================================================================
--- sandbox/trunk/setuptools/README.txt	(original)
+++ sandbox/trunk/setuptools/README.txt	Fri Dec 29 18:33:14 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/trunk/setuptools/launcher.c
==============================================================================
--- sandbox/trunk/setuptools/launcher.c	(original)
+++ sandbox/trunk/setuptools/launcher.c	Fri Dec 29 18:33:14 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/trunk/setuptools/setuptools/cli.exe
==============================================================================
Binary files. No diff available.

Modified: sandbox/trunk/setuptools/setuptools/command/easy_install.py
==============================================================================
--- sandbox/trunk/setuptools/setuptools/command/easy_install.py	(original)
+++ sandbox/trunk/setuptools/setuptools/command/easy_install.py	Fri Dec 29 18:33:14 2006
@@ -369,16 +369,15 @@
 
     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')
+                )
+        # we install wrapper scripts second, so they'll overwrite any dupes
+        # created as a result of exe<->egg roundtripping
         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 +407,7 @@
 
 
 
+
     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/trunk/setuptools/setuptools/gui.exe
==============================================================================
Binary files. No diff available.


More information about the Python-checkins mailing list