diff "-ur" python.orig/dist/src/Doc/api/utilities.tex python/dist/src/Doc/api/utilities.tex --- python.orig/dist/src/Doc/api/utilities.tex Wed Nov 13 15:13:38 2002 +++ python/dist/src/Doc/api/utilities.tex Fri Nov 29 10:33:18 2002 @@ -185,6 +185,11 @@ Initialize the import mechanism. For internal use only. \end{cfuncdesc} +\begin{cfuncdesc}{void}{PyImport_InitZip}{} + Initialize the zip file support for the import mechanism. + For internal use only. +\end{cfuncdesc} + \begin{cfuncdesc}{void}{PyImport_Cleanup}{} Empty the module table. For internal use only. \end{cfuncdesc} diff "-ur" python.orig/dist/src/Doc/ref/ref6.tex python/dist/src/Doc/ref/ref6.tex --- python.orig/dist/src/Doc/ref/ref6.tex Fri Oct 18 15:18:18 2002 +++ python/dist/src/Doc/ref/ref6.tex Fri Nov 29 10:27:18 2002 @@ -648,7 +648,9 @@ of the module searching and loading process are implementation and platform specific. It generally involves searching for a ``built-in'' module with the given name and then searching a list of locations -given as \code{sys.path}. +given as \code{sys.path}. The locations in \code{sys.path} are generally +either directory names, or the names of zip files containing a directory +structure. \withsubitem{(in module sys)}{\ttindex{modules}} \ttindex{sys.modules} \indexii{module}{name} diff "-ur" python.orig/dist/src/Doc/whatsnew/whatsnew23.tex python/dist/src/Doc/whatsnew/whatsnew23.tex --- python.orig/dist/src/Doc/whatsnew/whatsnew23.tex Wed Nov 27 19:11:10 2002 +++ python/dist/src/Doc/whatsnew/whatsnew23.tex Fri Nov 29 10:20:04 2002 @@ -643,6 +643,51 @@ %====================================================================== +\section{PEP 273: Import Modules from Zip Archives\label{section-zipimport}} + +It is now possible to include the names of zip files in \code{sys.path}. Zip +files can contain a directory structure, so that packages can be stored in +them, as well as simple modules. Uncompressed zip files can be used directly, +but compressed zip files require the presence of the \module{zlib} module in +order to work. If \module{zlib} is unavailable, attempts to import files from +a compressed zip file will fail with a message "missing zlib". + +It is not possible to import extension modules from zip files. Extension +modules are coded in C, and compiled into a shared library. As loading shared +libraries is achieved by calling the OS, and in general oprating systems do +not support shared libraries stored in zip files, Python follows suit, and +only supports loading of Python code from zip files. + +When attempting to import a module from a zip file, the bytecode form of the +module (the \samp{.pyc} or \samp{.pyo} file) is searched for first. If the +appropriate bytecode format is not available, the alternative form is also +searched for (i.e., in non-optimised mode, if the \samp{.pyc} file is not +available, an attempt will be made to load the \samp{.pyo} file, and vice +versa). If no bytecode file is available in the zip file, the source file is +loaded and compiled. In this case, the bytecode is discarded after use (the +zip file is treated like a read-only directory). + +In order to support loading the whole Python library from a zip file, an +additional entry is included in \code{sys.path}. On Unix, this entry is +\code{sys.prefix + "/lib/python\%s\%s.zip" \% (sys.version[0], +sys.version[2])}. On Windows, it is the filename of the Python DLL, with the +\samp{dll} extension replaced by \samp{zip}. This new entry is always the +second in sys.path (after the directory of the main Python script). + +A cacheing scheme for remembering the contents of directories on +\code{sys.path} has been added as well. This is particularly needed for zip +files, where looking up a file name is a slow operation. However, it is also +used for normal directory imports, where it still provides a prefomance boost. + +\begin{seealso} + +\seepep{273}{Import Modules from Zip Archives}{Written and implemented by +James C. Ahlstrom.} + +\end{seealso} + + +%====================================================================== \section{Extended Slices\label{section-slices}} Ever since Python 1.4, the slicing syntax has supported an optional diff "-ur" python.orig/dist/src/Include/import.h python/dist/src/Include/import.h --- python.orig/dist/src/Include/import.h Mon Jul 29 13:41:59 2002 +++ python/dist/src/Include/import.h Thu Nov 28 20:43:05 2002 @@ -20,6 +20,7 @@ PyAPI_FUNC(PyObject *) PyImport_ReloadModule(PyObject *m); PyAPI_FUNC(void) PyImport_Cleanup(void); PyAPI_FUNC(int) PyImport_ImportFrozenModule(char *); +PyAPI_FUNC(void) PyImport_InitZip(void); PyAPI_FUNC(PyObject *)_PyImport_FindExtension(char *, char *); PyAPI_FUNC(PyObject *)_PyImport_FixupExtension(char *, char *); diff "-ur" python.orig/dist/src/Include/pythonrun.h python/dist/src/Include/pythonrun.h --- python.orig/dist/src/Include/pythonrun.h Sat Oct 26 14:39:09 2002 +++ python/dist/src/Include/pythonrun.h Thu Nov 28 20:43:05 2002 @@ -17,6 +17,9 @@ PyAPI_FUNC(void) Py_SetProgramName(char *); PyAPI_FUNC(char *) Py_GetProgramName(void); +PyAPI_FUNC(void) Py_SetScriptName(char *); +PyAPI_FUNC(char *) Py_GetScriptName(void); + PyAPI_FUNC(void) Py_SetPythonHome(char *); PyAPI_FUNC(char *) Py_GetPythonHome(void); diff "-ur" python.orig/dist/src/Lib/site.py python/dist/src/Lib/site.py --- python.orig/dist/src/Lib/site.py Sat Nov 09 05:08:06 2002 +++ python/dist/src/Lib/site.py Thu Nov 28 20:43:05 2002 @@ -75,15 +75,14 @@ _dirs_in_sys_path = {} for dir in sys.path: # Filter out paths that don't exist, but leave in the empty string - # since it's a special case. We also need to special-case the Mac, - # as file names are allowed on sys.path there. - if sys.platform != 'mac': - if dir and not os.path.isdir(dir): + # since it's a special case. For the Mac, file names are allowed + # in sys.path. For all platforms, zip archive file names are allowed. + if dir: + if not os.path.exists(dir): continue - else: - if dir and not os.path.exists(dir): - continue - dir, dircase = makepath(dir) + dir, dircase = makepath(dir) + else: # Preserve "" in sys.path + dircase = "" if not dircase in _dirs_in_sys_path: L.append(dir) _dirs_in_sys_path[dircase] = 1 Only in python/dist/src/Lib/test: foo.zip diff "-ur" python.orig/dist/src/Lib/test/test_import.py python/dist/src/Lib/test/test_import.py --- python.orig/dist/src/Lib/test/test_import.py Tue Jul 23 19:03:55 2002 +++ python/dist/src/Lib/test/test_import.py Fri Nov 29 10:48:24 2002 @@ -71,6 +71,18 @@ finally: del sys.path[0] +# Verify that imports from zipfiles work +sys.path.insert(0, os.path.join(os.curdir, "foo.zip")) +try: + try: + import foo + except ImportError, err: + raise ValueError("import from zipfile failed: %s" % err) + del sys.modules['foo'] +finally: + del sys.path[0] + + # Verify that the imp module can correctly load and find .py files import imp x = imp.find_module("os") diff "-ur" python.orig/dist/src/Misc/NEWS python/dist/src/Misc/NEWS --- python.orig/dist/src/Misc/NEWS Wed Nov 27 13:10:40 2002 +++ python/dist/src/Misc/NEWS Fri Nov 29 09:53:29 2002 @@ -317,6 +317,11 @@ - sys.exit() inadvertently allowed more than one argument. An exception will now be raised if more than one argument is used. +- Importing from zip files (PEP 273) is now implemented. Elements of + sys.path can be zip files, which will be treated just like (read- + only) directories. An additional default zipfile entry on sys.path + has been included. + Extension modules ----------------- diff "-ur" python.orig/dist/src/Modules/getpath.c python/dist/src/Modules/getpath.c --- python.orig/dist/src/Modules/getpath.c Fri Sep 13 14:35:56 2002 +++ python/dist/src/Modules/getpath.c Thu Nov 28 20:43:05 2002 @@ -76,12 +76,14 @@ * * That's it! * - * Well, almost. Once we have determined prefix and exec_prefix, the - * preprocessor variable PYTHONPATH is used to construct a path. Each - * relative path on PYTHONPATH is prefixed with prefix. Then the directory - * containing the shared library modules is appended. The environment - * variable $PYTHONPATH is inserted in front of it all. Finally, the - * prefix and exec_prefix globals are tweaked so they reflect the values + * Well, almost. Once we have determined prefix and exec_prefix, we + * can construct the module_search_path that is returned by Py_GetPath(). + * The first item is the environment variable $PYTHONPATH. Next is the + * directory of the Python script being run, or "", or perhaps nothing. + * Next is the zip file name. Next we add each item of the preprocessor + * variable PYTHONPATH, prepending each relative path with prefix. Then + * the directory containing the shared library modules is added. Finally, + * the prefix and exec_prefix globals are tweaked so they reflect the values * expected by other code, by stripping the "lib/python$VERSION/..." stuff * off. If either points to the build directory, the globals are reset to * the corresponding preprocessor variables (so sys.prefix will reflect the @@ -347,12 +349,79 @@ return 0; } +/* + * Calculate item 0 of sys.path from the Python script name. + * If the script name is NULL, do not insert an item 0. + * The special name "" means item 0 is "". + * For a real script name, try to generate an absolute path. +*/ +static int +get_sys_path_0(char *path) +{ /* Return TRUE iff path has been set. Size of path must be MAXPATHLEN+1 */ + char *argv0 = Py_GetScriptName(); + char *p = NULL; +#ifdef HAVE_READLINK + char link[MAXPATHLEN+1]; + char argv0copy[2*MAXPATHLEN+1]; + int nr = 0; +#endif + + path[0] = 0; + if (argv0 == NULL) /* No sys.path[0] insertion */ + return 0; + + if (!argv0[0]) /* Insert "" as sys.path[0] */ + return 1; + +#ifdef HAVE_READLINK + nr = readlink(argv0, link, MAXPATHLEN); + if (nr > 0) { + /* It's a symlink */ + link[nr] = '\0'; + if (link[0] == SEP) + argv0 = link; /* Link to absolute path */ + else if (strchr(link, SEP) == NULL) + ; /* Link without path */ + else { + /* Must join(dirname(argv0), link) */ + char *q = strrchr(argv0, SEP); + if (q == NULL) + argv0 = link; /* argv0 without path */ + else { + /* Must make a copy */ + strcpy(argv0copy, argv0); + q = strrchr(argv0copy, SEP); + strcpy(q+1, link); + argv0 = argv0copy; + } + } + } +#endif /* HAVE_READLINK */ + + strncpy(path, argv0, MAXPATHLEN); + p = strrchr(path, SEP); + if (p == NULL) { /* No separator at all */ + path[0] = 0; + getcwd(path, MAXPATHLEN); + } + else if (p == path) { /* A single initial separator */ + path[0] = SEP; + path[1] = 0; + } + else { /* Last separator occurs within the name */ +#ifdef macintosh + *(p + 1) = 0; /* Keep trailing separator */ +#else + *p = 0; /* Remove trailing separator */ +#endif + absolutize(path); + } + return 1; +} static void calculate_path(void) { - extern char *Py_GetProgramName(void); - static char delimiter[2] = {DELIM, '\0'}; static char separator[2] = {SEP, '\0'}; char *pythonpath = PYTHONPATH; @@ -361,6 +430,9 @@ char *path = getenv("PATH"); char *prog = Py_GetProgramName(); char argv0_path[MAXPATHLEN+1]; + char zip_path[MAXPATHLEN+1]; + char syspath0[MAXPATHLEN+1]; + int have_syspath0; int pfound, efound; /* 1 if found; -1 if found build directory */ char *buf; size_t bufsz; @@ -479,6 +551,18 @@ else reduce(prefix); + strncpy(zip_path, prefix, MAXPATHLEN); + if (pfound > 0) { /* Use the reduced prefix returned by Py_GetPrefix() */ + reduce(zip_path); + reduce(zip_path); + } + else + strncpy(zip_path, PREFIX, MAXPATHLEN); + joinpath(zip_path, "lib/python00.zip"); + bufsz = strlen(zip_path); /* Replace "00" with version */ + zip_path[bufsz - 6] = VERSION[0]; + zip_path[bufsz - 5] = VERSION[2]; + if (!(efound = search_for_exec_prefix(argv0_path, home))) { if (!Py_FrozenFlag) fprintf(stderr, @@ -496,6 +580,10 @@ */ bufsz = 0; + have_syspath0 = get_sys_path_0(syspath0); + if (have_syspath0) + bufsz += strlen(syspath0) + 1; + if (rtpypath) bufsz += strlen(rtpypath) + 1; @@ -517,6 +605,7 @@ defpath = delim + 1; } + bufsz += strlen(zip_path) + 1; bufsz += strlen(exec_prefix) + 1; /* This is the only malloc call in this file */ @@ -537,6 +626,16 @@ else buf[0] = '\0'; + /* Next is syspath0 calculated from the script name */ + if (have_syspath0) { + strcat(buf, syspath0); + strcat(buf, delimiter); + } + + /* Next is the default zip path */ + strcat(buf, zip_path); + strcat(buf, delimiter); + /* Next goes merge of compile-time $PYTHONPATH with * dynamically located prefix. */ diff "-ur" python.orig/dist/src/Modules/main.c python/dist/src/Modules/main.c --- python.orig/dist/src/Modules/main.c Thu Oct 17 20:37:50 2002 +++ python/dist/src/Modules/main.c Thu Nov 28 20:43:05 2002 @@ -123,6 +123,7 @@ int saw_inspect_flag = 0; int saw_unbuffered_flag = 0; PyCompilerFlags cf; + static char *ZLSTRING = ""; cf.cf_flags = 0; @@ -339,7 +340,11 @@ /* Leave stderr alone - it should be unbuffered anyway. */ } - Py_SetProgramName(argv[0]); + Py_SetProgramName(argv[0]); /* For use by Py_GetPath() */ + if (filename) + Py_SetScriptName(filename); /* For use by Py_GetPath() */ + else + Py_SetScriptName(ZLSTRING); /* Add "" to sys.path */ Py_Initialize(); if (Py_VerboseFlag || diff "-ur" python.orig/dist/src/PC/getpathp.c python/dist/src/PC/getpathp.c --- python.orig/dist/src/PC/getpathp.c Mon Jul 22 13:28:21 2002 +++ python/dist/src/PC/getpathp.c Thu Nov 28 20:43:05 2002 @@ -8,10 +8,13 @@ functionality, not the implementation (ie, the order in which these are actually fetched is different) - * Python always adds an empty entry at the start, which corresponds - to the current directory. + * If the PYTHONPATH env. var. exists, it's entries are added first. - * If the PYTHONPATH env. var. exists, it's entries are added next. + * The absolute path of the Python script; or the string "" for + the "-c" option or an interactive interpreter. There is also + an option to suppress this entry. + + * The default zip archive path. * We look in the registry for "application paths" - that is, sub-keys under the main PythonPath registry key. These are added next (the @@ -60,6 +63,7 @@ #ifdef MS_WINDOWS #include #include +#include #endif #include @@ -81,6 +85,7 @@ static char prefix[MAXPATHLEN+1]; static char progpath[MAXPATHLEN+1]; +static char dllpath[MAXPATHLEN+1]; static char *module_search_path = NULL; @@ -350,6 +355,7 @@ char *prog = Py_GetProgramName(); #ifdef MS_WINDOWS + extern HANDLE PyWin_DLLhModule; #ifdef UNICODE WCHAR wprogpath[MAXPATHLEN+1]; /* Windows documents that GetModuleFileName() will "truncate", @@ -357,6 +363,14 @@ PLUS Windows itself defines MAX_PATH as the same, but anyway... */ wprogpath[MAXPATHLEN]=_T('\0'); + if (PyWin_DLLhModule && + GetModuleFileName(PyWin_DLLhModule, wprogpath, MAXPATHLEN)) { + WideCharToMultiByte(CP_ACP, 0, + wprogpath, -1, + dllpath, MAXPATHLEN+1, + NULL, NULL); + } + wprogpath[MAXPATHLEN]=_T('\0')'; if (GetModuleFileName(NULL, wprogpath, MAXPATHLEN)) { WideCharToMultiByte(CP_ACP, 0, wprogpath, -1, @@ -366,6 +380,9 @@ } #else /* static init of progpath ensures final char remains \0 */ + if (PyWin_DLLhModule) + if (!GetModuleFileName(PyWin_DLLhModule, dllpath, MAXPATHLEN)) + dllpath[0] = 0; if (GetModuleFileName(NULL, progpath, MAXPATHLEN)) return; #endif @@ -414,10 +431,118 @@ progpath[0] = '\0'; } +/* + * Calculate item 0 of sys.path from the Python script name. + * If the script name is NULL, do not insert an item 0. + * The special name "" means item 0 is "". + * For a real script name, try to generate an absolute path. +*/ +static int +get_sys_path_0(char *path) +{ /* Return TRUE iff path has been set. Size of path must be MAXPATHLEN+1 */ + char *argv0 = Py_GetScriptName(); + char *p = NULL; +#ifdef MS_WINDOWS + char dir[MAXPATHLEN+1]; + int drive; + size_t i, len; +#else + char *q; + int n = 0; +#endif + + path[0] = 0; + if (argv0 == NULL) /* No sys.path[0] insertion */ + return 0; + + if (!argv0[0]) /* Insert "" as sys.path[0] */ + return 1; + +#ifdef MS_WINDOWS + len = strlen(argv0); + if (len > 1 && argv0[1] == ':') { /* We have a drive letter */ + drive = toupper(argv0[0]) - 64; /* A=1, B=2, ... */ + strncpy(dir, argv0 + 2, MAXPATHLEN); + len -= 2; + } + else { /* Get drive letter */ + drive = _getdrive(); + strncpy(dir, argv0, MAXPATHLEN); + } + for (i=0; i < len; i++) /* Replace ALTSEP with SEP */ + if (dir[i] == ALTSEP) + dir[i] = SEP; + p = strrchr(dir, SEP); + if (p == NULL) { /* No separator at all */ + path[0] = 0; + _getdcwd(drive, path, MAXPATHLEN); + return 1; + } + if (p == dir) { /* A single initial separator */ + path[0] = 'A' + drive - 1; + path[1] = ':'; + path[2] = SEP; + path[3] = 0; + return 1; + } + /* Last separator occurs within the name */ + *p = 0; /* Remove trailing separator */ + if (dir[0] == SEP) { /* Absolute path was given */ + path[0] = 'A' + drive - 1; + path[1] = ':'; + path[2] = 0; + strncat(path, dir, MAXPATHLEN); + } + else if (_getdcwd(drive, path, MAXPATHLEN - 2)) { + len = strlen(path); + path[len] = SEP; + path[len + 1] = 0; + strncat(path, dir, MAXPATHLEN); + } + else { /* _getdcwd() failed */ + path[0] = 'A' + drive - 1; + path[1] = ':'; + path[2] = 0; + strncat(path, dir, MAXPATHLEN); + } +#else +/* getdrive(), getdcwd() are not available */ +/* When is this ever used?? */ + p = strrchr(argv0, SEP); +#ifdef ALTSEP + /* Test for alternate separator */ + q = strrchr(p ? p : argv0, ALTSEP); + if (q != NULL) + p = q; +#endif + if (p == NULL) { /* No separator at all */ + if (argv0[1] != ':') { /* No drive letter */ + path[0] = '.'; + path[1] = 0; + return 1; + } + } + else { + n = p + 1 - argv0; + if (n > 1 && p[-1] != ':') + n--; /* Drop trailing separator */ + } + if (n <= 0) + path[0] = 0; + else if (n < MAXPATHLEN) + strncpy(path, argv0, n); + else + strncpy(path, argv0, MAXPATHLEN); +#endif + return 1; +} + static void calculate_path(void) { char argv0_path[MAXPATHLEN+1]; + char syspath0[MAXPATHLEN+1]; + int have_syspath0; char *buf; size_t bufsz; char *pythonhome = Py_GetPythonHome(); @@ -427,6 +552,8 @@ int skiphome, skipdefault; char *machinepath = NULL; char *userpath = NULL; + char zip_path[MAXPATHLEN+1]; + size_t len; #endif get_progpath(); @@ -445,6 +572,22 @@ if (envpath && *envpath == '\0') envpath = NULL; +#ifdef MS_WINDOWS + /* Calculate zip archive path */ + if (dllpath[0]) /* use name of python DLL */ + strncpy(zip_path, dllpath, MAXPATHLEN); + else /* use name of executable program */ + strncpy(zip_path, progpath, MAXPATHLEN); + len = strlen(zip_path); + if (len > 4) { + zip_path[len-3] = 'z'; /* change ending to "zip" */ + zip_path[len-2] = 'i'; + zip_path[len-1] = 'p'; + } + else { + zip_path[0] = 0; + } +#endif #ifdef MS_WINDOWS skiphome = pythonhome==NULL ? 0 : 1; @@ -458,14 +601,16 @@ /* We need to construct a path from the following parts. (1) the PYTHONPATH environment variable, if set; - (2) for Win32, the machinepath and userpath, if set; - (3) the PYTHONPATH config macro, with the leading "." + (2) the absolute path of the script file, or "", or nothing; + (3) for Win32, the zip archive file path; + (4) for Win32, the machinepath and userpath, if set; + (5) the PYTHONPATH config macro, with the leading "." of each component replaced with pythonhome, if set; - (4) the directory containing the executable (argv0_path). + (6) the directory containing the executable (argv0_path). The length calculation calculates #3 first. Extra rules: - - If PYTHONHOME is set (in any way) item (2) is ignored. - - If registry values are used, (3) and (4) are ignored. + - If PYTHONHOME is set (in any way) item (4) is ignored. + - If registry values are used, (5) and (6) are ignored. */ /* Calculate size of return buffer */ @@ -480,6 +625,9 @@ } else bufsz = 0; + have_syspath0 = get_sys_path_0(syspath0); + if (have_syspath0) + bufsz += strlen(syspath0) + 1; bufsz += strlen(PYTHONPATH) + 1; bufsz += strlen(argv0_path) + 1; #ifdef MS_WINDOWS @@ -487,6 +635,7 @@ bufsz += strlen(userpath) + 1; if (machinepath) bufsz += strlen(machinepath) + 1; + bufsz += strlen(zip_path) + 1; #endif if (envpath != NULL) bufsz += strlen(envpath) + 1; @@ -517,7 +666,18 @@ buf = strchr(buf, '\0'); *buf++ = DELIM; } + + if (have_syspath0) { + strcpy(buf, syspath0); + buf = strchr(buf, '\0'); + *buf++ = DELIM; + } #ifdef MS_WINDOWS + if (zip_path[0]) { + strcpy(buf, zip_path); + buf = strchr(buf, '\0'); + *buf++ = DELIM; + } if (userpath) { strcpy(buf, userpath); buf = strchr(buf, '\0'); @@ -640,3 +800,5 @@ calculate_path(); return progpath; } + +/* Could add char * Py_GetDllFullPath(void) to return dllpath here. */ diff "-ur" python.orig/dist/src/Python/import.c python/dist/src/Python/import.c --- python.orig/dist/src/Python/import.c Sat Aug 31 15:16:14 2002 +++ python/dist/src/Python/import.c Thu Nov 28 20:40:15 2002 @@ -88,6 +88,70 @@ /* these tables define the module suffixes that Python recognizes */ struct filedescr * _PyImport_Filetab = NULL; +static struct filedescr fd_frozen = {"", "", PY_FROZEN}; +static struct filedescr fd_builtin = {"", "", C_BUILTIN}; +static struct filedescr fd_package = {"", "", PKG_DIRECTORY}; +static struct filedescr fd_zip = {"", "", PY_ZIPFILE}; + +static struct stZipSearchOrder { + char suffix[16]; + int type; +} ZipSearchOrder[] = { + {"/__init__.pyc", PKG_DIRECTORY}, + {"/__init__.pyo", PKG_DIRECTORY}, + {"/__init__.py", PKG_DIRECTORY}, + {".pyc", PY_ZIPFILE}, + {".pyo", PY_ZIPFILE}, + {".py", PY_ZIPFILE}, + {"", 0} +}; + +#define PY_IMP_NO_VALUE 0 +#define PY_IMP_BUILTIN 1 +#define PY_IMP_FROZEN 2 +#define PY_IMP_LISTDIR 3 +#define PY_IMP_NO_LISTDIR 4 +#define PY_IMP_FILE 5 +#define PY_IMP_ZIP 6 +#define PY_CACHE_USING_OS_LISTDIR 1 + +static int use_os_listdir = 0; /* 0: init, 1: use os.listdir, -1: don't use */ +static PyObject * ExternalNames = NULL; +static PyObject * InternalNames = NULL; +static void get_internal_names(void); +static struct filedescr * search_using_fopen(char *, char *, + size_t, size_t, FILE **, PyObject *); + +/* + Import dictionary entries have values of int or tuple. An int value + must be one of the PY_IMP_ constants. For a sequence, the first item + of the sequence must be one of the PY_IMP_ constants. + + InternalNames is a dictionary: keys are the names of all builtin and + frozen modules, value is PY_IMP_BUILTIN or PY_IMP_FROZEN. It is + used to speed up searching for internal modules. + + ExternalNames is a dictionary used to speed up imports. Keys are: + Names of zip archive files; value is PY_IMP_ZIP. + For example, /A/B/python22.zip. + Names of items in sys.path; values are: + PY_IMP_ZIP: The string ".zip" appears in the sys.path item. + For example, /A/B/python22.zip, /A/B/python22.zip/LibFoo. + PY_IMP_LISTDIR: A directory; use os.listdir() to record contents. + For example, /A/B/C. + PY_IMP_NO_LISTDIR: os.listdir() is not available. + For example, /A/B/C. + Names of files in the zip archive joined with the zip archive name. + For example, /usr/lib/python22.zip/Dir/mymodule.py. + Zip paths are always spelled with SEP instead of '/'. + The value is a tuple, and the first item is PY_IMP_ZIP. + Names of files in the directory joined with the directory name. + Names are returned using os.listdir(). + For example, /A/B/C/jim.pyc + The value is PY_IMP_FILE. + If os.listdir() is not available, the directory entries are absent. +*/ + #ifdef RISCOS static const struct filedescr _PyImport_StandardFiletab[] = { {"/py", "U", PY_SOURCE}, @@ -150,6 +214,28 @@ code created in normal operation mode. */ pyc_magic = MAGIC + 1; } + + if (ExternalNames) + PyDict_Clear(ExternalNames); + else + ExternalNames = PyDict_New(); + if (InternalNames) + PyDict_Clear(InternalNames); + else + InternalNames = PyDict_New(); + ZipSearchOrder[0].suffix[0] = SEP; /* Correct directory separator */ + ZipSearchOrder[1].suffix[0] = SEP; + ZipSearchOrder[2].suffix[0] = SEP; + if (Py_OptimizeFlag) { /* Reverse *.pyc and *.pyo */ + struct stZipSearchOrder zso; + zso = ZipSearchOrder[0]; + ZipSearchOrder[0] = ZipSearchOrder[1]; + ZipSearchOrder[1] = zso; + zso = ZipSearchOrder[3]; + ZipSearchOrder[3] = ZipSearchOrder[4]; + ZipSearchOrder[4] = zso; + } + get_internal_names(); } void @@ -159,6 +245,10 @@ extensions = NULL; PyMem_DEL(_PyImport_Filetab); _PyImport_Filetab = NULL; + Py_XDECREF(ExternalNames); + ExternalNames = NULL; + Py_XDECREF(InternalNames); + InternalNames = NULL; } @@ -229,6 +319,491 @@ #endif } +/* Helper functions to import from zip archives */ + +void PyImport_InitZip() +{ + PyObject *zlib; + int have_zlib; + + zlib = PyImport_ImportModule("zlib"); /* import zlib */ + if (zlib) { + have_zlib = 1; + Py_DECREF(zlib); + } + else { + have_zlib = 0; + PyErr_Clear(); + } + if (Py_VerboseFlag) + PySys_WriteStderr("# PyImport_InitZip: zlib %s\n", + have_zlib? "available": "UNAVAILABLE"); + +#if PY_CACHE_USING_OS_LISTDIR + { + PyObject *v, *vv; + v = PyImport_ImportModule("os"); /* import the os module */ + vv = NULL; + if (v && (vv = PyObject_GetAttrString(v, "listdir")) != NULL) { + if (Py_VerboseFlag) + PySys_WriteStderr("Import os.listdir() succeeds\n"); + use_os_listdir = 1; /* os.listdir() is available */ + } + else { + if (Py_VerboseFlag) + PySys_WriteStderr("Import os.listdir() fails\n"); + PyErr_Clear(); + use_os_listdir = -1; /* os.listdir() is not available */ + } + Py_XDECREF(vv); + Py_XDECREF(v); + } +#else + use_os_listdir = -1; /* os.listdir() is not available */ +#endif +} + +static void get_internal_names() +{ + int i; + PyObject *v; + struct _frozen *pFrozen; + + if (!InternalNames) + return; + PyDict_Clear(InternalNames); + /* Add frozen module names to the import dictionary */ + v = PyInt_FromLong(PY_IMP_FROZEN); + for (pFrozen = PyImport_FrozenModules; pFrozen->name; pFrozen++) + PyDict_SetItemString(InternalNames, pFrozen->name, v); + Py_DECREF(v); + /* Add builtin module names to the import dictionary */ + v = PyInt_FromLong(PY_IMP_BUILTIN); + for (i = 0; PyImport_Inittab[i].name; i++) + PyDict_SetItemString(InternalNames, PyImport_Inittab[i].name, v); + Py_DECREF(v); +} + +static int relative_path(char *path) +{ /* Return TRUE if path is a relative (non-absolute) path */ + if (path[0] == 0) /* zero-length string is current dir */ + return 1; +#ifdef macintosh + return !strchr(path, ':') || path[0] == ':'; +#else + if (SEP == '\\') /* Windows-type requires a drive letter */ + return !(path[1] == ':' && (path[2] == SEP +#ifdef ALTSEP + || path[2] == ALTSEP +#endif + )); + return path[0] != SEP; /* Unix and all other */ +#endif +} + +static int get_path_type(PyObject *dict, char *path) +{ /* return one of the PY_IMP_ constants for the import dictionary path */ + PyObject *pyobj; + int i; + + pyobj = PyDict_GetItemString(dict, path); + if (!pyobj) + return PY_IMP_NO_VALUE; + if (PyInt_Check(pyobj)) + return (int)PyInt_AsLong(pyobj); + if (PySequence_Check(pyobj)) { + pyobj = PySequence_GetItem(pyobj, 0); + if (pyobj && PyInt_Check(pyobj)) { + i = (int)PyInt_AsLong(pyobj); + Py_DECREF(pyobj); + return i; + } + Py_XDECREF(pyobj); + } + return PY_IMP_NO_VALUE; +} + +static void add_directory_names(char *path) +{ /* Read the directory names and add to ExternalNames */ + PyObject *v, *modules, *modOS, *pyList, *func, *args, *pyFile; + long count, i, length; + char name[MAXPATHLEN + 5]; + char *filename; + + if (get_path_type(ExternalNames, path) == PY_IMP_LISTDIR) + return; /* We have already seen this directory */ + v = PyInt_FromLong(PY_IMP_LISTDIR); + PyDict_SetItemString(ExternalNames, path, v); + Py_DECREF(v); + modules = PyImport_GetModuleDict(); + if (!modules) + return; + modOS = PyDict_GetItemString(modules, "os"); + if (!modOS) + return; + func = PyObject_GetAttrString(modOS, "listdir"); + if (!func) + return; + args = Py_BuildValue("(s)", path); + pyList = PyEval_CallObject(func, args); + Py_DECREF(args); + Py_DECREF(func); + if (!pyList || !PyList_Check(pyList)) { + PyErr_Clear(); + if (Py_VerboseFlag) + PySys_WriteStderr("# directory: ADD 0 names from %s\n", path); + return; + } + count = PyList_Size(pyList); + if (Py_VerboseFlag) + PySys_WriteStderr("# directory: ADD %ld names from %s\n", count, path); + strncpy(name, path, MAXPATHLEN); + length = strlen(name); + if (name[length - 1] != SEP +#ifdef ALTSEP + && name[length - 1] != ALTSEP +#endif + ) + name[length++] = SEP; + pyFile = PyInt_FromLong(PY_IMP_FILE); + for (i = 0; i < count; i++) { + filename = PyString_AsString(PyList_GetItem(pyList, i)); + /* Join member name to archive name */ + strncpy(name + length, filename, MAXPATHLEN - length); + PyDict_SetItemString(ExternalNames, name, pyFile); + } + Py_DECREF(pyFile); + Py_DECREF(pyList); + return; +} + +static int add_zip_names(char *path) +{ /* Read all zip archive names and add to ExternalNames */ + /* Return 1 if path is a zip archive, else 0 */ + PyObject *pyTuple, *pyZip; + FILE *fp; + long compress, crc, data_size, file_size, file_offset, date, time; + long header_offset, name_size, header_size; + long i, l, length, count; + char archive[MAXPATHLEN + 5]; + char zipname[MAXPATHLEN + 5]; + char ch, *pt; + + /* The "path" is an item of sys.path. We need to extract the zip + file name and read the contents of the archive. + Indicate that we have already seen this item by creating a + dictionary entry for the path. + */ + + l = (long)strlen(path) - 4; + length = 0; + for (i = 1; i <= l; i++) /* Search for ".zip/" */ + if (path[i] == '.' && path[i+1] == 'z' && + path[i+2] == 'i' && path[i+3] == 'p' && + ((ch = path[i+4]) == SEP || +#ifdef ALTSEP + ch == ALTSEP || +#endif + ch == 0)) { + length = i + 4; + strncpy(archive, path, length); + archive[length] = 0; + break; + + } + if (!length) + return 0; /* There is no ".zip" in the name */ +#ifdef ALTSEP + for (i = 0; i < length; i++) /* Use only SEP in zip names */ + if (archive[i] == ALTSEP) + archive[i] = SEP; +#endif + pyZip = PyInt_FromLong(PY_IMP_ZIP); /* Mark path as seen */ + /* "archive" is now the path up to and including ".zip" */ + if (PyDict_GetItemString(ExternalNames, archive)) { + PyDict_SetItemString(ExternalNames, path, pyZip); + Py_DECREF(pyZip); + return 1; /* We have already seen this archive */ + } + else { + PyDict_SetItemString(ExternalNames, path, pyZip); + PyDict_SetItemString(ExternalNames, archive, pyZip); + } + fp = fopen(archive, "rb"); + if (!fp) { /* Not an error; no such file */ + Py_DECREF(pyZip); + return 1; + } + fseek(fp, -22, 2); /* Seek from end of file */ + if (PyMarshal_ReadLongFromFile(fp) != 0x06054B50) { + fclose(fp); + Py_DECREF(pyZip); + return 1; /* Bad: End of Central Dir signature */ + } + fseek(fp, -6, 2); + header_offset = PyMarshal_ReadLongFromFile(fp); + /* Start of Central Directory */ + length = (long)strlen(archive); + count = 0; + while(1) { + fseek(fp, header_offset, 0); /* Start of file header */ + l = PyMarshal_ReadLongFromFile(fp); + if (l != 0x02014B50) + break; /* Bad: Central Dir File Header */ + fseek(fp, header_offset + 10, 0); + compress = PyMarshal_ReadShortFromFile(fp); + time = PyMarshal_ReadShortFromFile(fp); + date = PyMarshal_ReadShortFromFile(fp); + crc = PyMarshal_ReadLongFromFile(fp); + data_size = PyMarshal_ReadLongFromFile(fp); + file_size = PyMarshal_ReadLongFromFile(fp); + name_size = PyMarshal_ReadShortFromFile(fp); + header_size = 46 + name_size + + PyMarshal_ReadShortFromFile(fp) + + PyMarshal_ReadShortFromFile(fp); + fseek(fp, header_offset + 42, 0); + file_offset = PyMarshal_ReadLongFromFile(fp); + if (name_size > MAXPATHLEN) + name_size = MAXPATHLEN; + /* Unicode: zipname should work even if sizeof(char) > 1 */ + pt = zipname; + *pt++ = SEP; /* Add an initial '/' */ + for (i = 0; i < name_size; i++) { + ch = (char)getc(fp); + /* Must change '/' to SEP in archive */ + if (ch == '/' || ch == '\\') { + if (i > 0) /* Remove an initial '/' */ + *pt++ = SEP; + } + else + *pt++ = ch; + } + *pt = 0; /* Add ending null byte */ + header_offset += header_size; + /* Check to make sure the local file header is correct */ + fseek(fp, file_offset, 0); + l = PyMarshal_ReadLongFromFile(fp); + if (l != 0x04034B50) + continue; /* Bad: Local File Header */ + fseek(fp, file_offset + 26, 0); + l = 30 + PyMarshal_ReadShortFromFile(fp) + + PyMarshal_ReadShortFromFile(fp); /* local header size */ + file_offset += l; /* Start of file data */ + archive[length] = 0; + /* Record zip archive data for this file name */ + pyTuple = PyTuple_New(6); + Py_INCREF(pyZip); + PyTuple_SetItem(pyTuple, 0, pyZip); + PyTuple_SetItem(pyTuple, 1, PyString_FromString(archive)); + PyTuple_SetItem(pyTuple, 2, PyInt_FromLong(compress)); + PyTuple_SetItem(pyTuple, 3, PyInt_FromLong(data_size)); + PyTuple_SetItem(pyTuple, 4, PyInt_FromLong(file_size)); + PyTuple_SetItem(pyTuple, 5, PyInt_FromLong(file_offset)); + /* Join member name to archive name */ + strncpy(archive + length, zipname, MAXPATHLEN - length); + PyDict_SetItemString(ExternalNames, archive, pyTuple); + Py_DECREF(pyTuple); + count++; + } + fclose(fp); + Py_DECREF(pyZip); + archive[length] = 0; + if (Py_VerboseFlag) + PySys_WriteStderr("# zip import: add %ld names from %s\n", + count, archive); + return 1; +} + +static PyObject * +get_zip_string(char *name) +{ /* Return the string from the zip archive */ + PyObject *modules, *mZlib, *pyTuple, *deobj; + PyObject *pyData, *pyFile1, *pyFile2, *method, *decomp, *args; + int nerr; + long compress, data_size, file_size, file_offset; + char *archive, *data; + FILE *fp; + + /* This function must set its own errors if it returns NULL */ + pyTuple = PyDict_GetItemString(ExternalNames, name); + if (!pyTuple || !PyTuple_Check(pyTuple)) { + PyErr_Format(PyExc_ValueError, + "Zip import: no data for %s", name); + return NULL; + } + archive = PyString_AsString(PyTuple_GetItem(pyTuple, 1)); + compress = PyInt_AsLong(PyTuple_GetItem(pyTuple, 2)); + data_size = PyInt_AsLong(PyTuple_GetItem(pyTuple, 3)); + file_size = PyInt_AsLong(PyTuple_GetItem(pyTuple, 4)); + file_offset = PyInt_AsLong(PyTuple_GetItem(pyTuple, 5)); + fp = fopen(archive, "rb"); + if (!fp) { + PyErr_Format(PyExc_IOError, + "Zip import: can not open file %s", archive); + return NULL; + } + data = (char *)PyMem_Malloc(data_size); + fseek(fp, file_offset, 0); + fread(data, 1, data_size, fp); + fclose(fp); + /* Should use an API to create a string stealing the data ref */ + pyData = PyString_FromStringAndSize(data, data_size); + PyMem_Free(data); + if (compress == 0) /* data is not compressed */ + return pyData; + /* Uncompress with zlib */ + modules = PyImport_GetModuleDict(); + if (!modules || + (mZlib = PyDict_GetItemString(modules, "zlib")) == NULL) { + Py_DECREF(pyData); + PyErr_Format(PyExc_ImportError, + "Zip import: missing zlib, can not decompress %s", name); + return NULL; + } +/* We would prefer to use zlib.decompress(), but it sometimes returns -5 */ + decomp = method = NULL; + deobj = PyObject_GetAttrString(mZlib, "decompressobj"); + if (!deobj) { + nerr = 1; + goto error; + } + args = Py_BuildValue("(l)", -15L); + decomp = PyEval_CallObject(deobj, args); + Py_DECREF(args); + if (!decomp) { + nerr = 2; + goto error; + } + method = PyObject_GetAttrString(decomp, "decompress"); + if (!method) { + nerr = 3; + goto error; + } + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pyData); + pyData = NULL; + pyFile1 = PyEval_CallObject(method, args); + if (!pyFile1 || !PyString_Check(pyFile1)) { + Py_DECREF(args); + nerr = 4; + goto error; + } + pyFile2 = PyEval_CallObject(method, args); + while(pyFile2 && PyString_Check(pyFile2) && PyString_Size(pyFile2)) { + PyString_ConcatAndDel(&pyFile1, pyFile2); + pyFile2 = PyEval_CallObject(method, args); + } + Py_DECREF(args); + Py_DECREF(method); + Py_DECREF(decomp); + Py_DECREF(deobj); + deobj = decomp = method = NULL; + Py_XDECREF(pyFile2); + if (pyFile1 && PyString_Check(pyFile1) && + PyString_Size(pyFile1) == file_size) + return pyFile1; + Py_XDECREF(pyFile1); + nerr = 5; +error: + Py_XDECREF(method); + Py_XDECREF(decomp); + Py_XDECREF(deobj); + Py_XDECREF(pyData); + PyErr_Format(PyExc_ImportError, + "Zip import: failure %d in decompressing %s", nerr, name); + return NULL; +} + +static PyObject * +load_zip_source_module(char *name, char *pathname) +{ + PyObject *pyStr, *m; + PyCodeObject *code; + node *n; + char *pt; + + /* pathname ends in ".py" */ + pyStr = get_zip_string(pathname); + if (pyStr == NULL) + return NULL; + pt = PyString_AsString(pyStr); + n = PyParser_SimpleParseString(pt, Py_file_input); + if (n == NULL) { + Py_DECREF(pyStr); + return NULL; + } + code = PyNode_Compile(n, pathname); + PyNode_Free(n); + if (code == NULL) { + Py_DECREF(pyStr); + return NULL; + } + if (Py_VerboseFlag) + PySys_WriteStderr("import %s # source from zip %s\n", + name, pathname); + m = PyImport_ExecCodeModuleEx(name, (PyObject *)code, pathname); + Py_DECREF(code); + Py_DECREF(pyStr); + + return m; +} + +static PyObject * +load_zip_module(char *name, char *pathname) +{ + long size; + PyObject *pyStr, *m, *code; + unsigned char *pt; + char source[MAXPATHLEN]; + + /* pathname ends in ".py" or ".pyc" or ".pyo" */ + size = (long)strlen(pathname); + if (pathname[size-3] == '.' && + pathname[size-2] == 'p' && + pathname[size-1] == 'y') + return load_zip_source_module(name, pathname); + pyStr = get_zip_string(pathname); + if (pyStr == NULL) + return NULL; + pt = PyString_AsString(pyStr); + size = PyString_Size(pyStr); + if (pt[0] != ( pyc_magic & 0xFF) || + pt[1] != ((pyc_magic >> 8) & 0xFF) || + pt[2] != ((pyc_magic >> 16) & 0xFF) || + pt[3] != ((pyc_magic >> 24) & 0xFF)) { /* Bad magic number */ + strncpy(source, pathname, MAXPATHLEN); + source[strlen(source) - 1] = 0; /* try source file X.py */ + if (PyDict_GetItemString(ExternalNames, source)) { + if (Py_VerboseFlag) + PySys_WriteStderr( + "Zip import: Bad magic number in %.200s; try source X.py\n", + pathname); + Py_DECREF(pyStr); + return load_zip_source_module(name, source); + } + PyErr_Format(PyExc_ImportError, + "Bad magic number in %.200s", pathname); + Py_DECREF(pyStr); + return NULL; + } + code = PyMarshal_ReadObjectFromString(pt + 8, size - 8); + if (!code || !PyCode_Check(code)) { + Py_XDECREF(code); + PyErr_Format(PyExc_TypeError, + "zip compiled file %.200s is not a code object", + name); + Py_DECREF(pyStr); + return NULL; + } + m = PyImport_ExecCodeModuleEx(name, code, pathname); + Py_DECREF(code); + Py_DECREF(pyStr); + if (Py_VerboseFlag) + PySys_WriteStderr("import %s # precompiled from zip %s\n", + name, pathname); + return m; +} + /* Helper for sys */ PyObject * @@ -864,24 +1439,6 @@ } -/* Helper to test for built-in module */ - -static int -is_builtin(char *name) -{ - int i; - for (i = 0; PyImport_Inittab[i].name != NULL; i++) { - if (strcmp(name, PyImport_Inittab[i].name) == 0) { - if (PyImport_Inittab[i].initfunc == NULL) - return -1; - else - return 1; - } - } - return 0; -} - - /* Search the path (default sys.path) for a module. Return the corresponding filedescr struct, and (via return arguments) the pathname and an open file. Return NULL if the module is not found. */ @@ -898,23 +1455,12 @@ find_module(char *realname, PyObject *path, char *buf, size_t buflen, FILE **p_fp) { - int i, npath; + int i, npath, path_type; size_t len, namelen; struct filedescr *fdp = NULL; - char *filemode; + struct stZipSearchOrder *zso; FILE *fp = NULL; -#ifndef RISCOS - struct stat statbuf; -#endif - static struct filedescr fd_frozen = {"", "", PY_FROZEN}; - static struct filedescr fd_builtin = {"", "", C_BUILTIN}; - static struct filedescr fd_package = {"", "", PKG_DIRECTORY}; char name[MAXPATHLEN+1]; -#if defined(PYOS_OS2) - size_t saved_len; - size_t saved_namelen; - char *saved_buf = NULL; -#endif if (strlen(realname) > MAXPATHLEN) { PyErr_SetString(PyExc_OverflowError, @@ -952,11 +1498,11 @@ #endif } if (path == NULL) { - if (is_builtin(name)) { + switch(get_path_type(InternalNames, name)) { + case PY_IMP_BUILTIN: strcpy(buf, name); return &fd_builtin; - } - if ((find_frozen(name)) != NULL) { + case PY_IMP_FROZEN: strcpy(buf, name); return &fd_frozen; } @@ -977,6 +1523,7 @@ } npath = PyList_Size(path); namelen = strlen(name); + *p_fp = NULL; for (i = 0; i < npath; i++) { PyObject *copy = NULL; PyObject *v = PyList_GetItem(path, i); @@ -1002,6 +1549,26 @@ Py_XDECREF(copy); continue; /* v contains '\0' */ } + path_type = get_path_type(ExternalNames, buf); + if (path_type == PY_IMP_NO_VALUE || path_type == PY_IMP_FILE) { + /* Zip imports may fail for relative paths */ + if (add_zip_names(buf)) { + path_type = PY_IMP_ZIP; + } + else if (use_os_listdir < 0 || relative_path(buf)) { + /* Do not cache directories for relative paths */ + PyObject *vv; + vv = PyInt_FromLong(PY_IMP_NO_LISTDIR); + PyDict_SetItemString(ExternalNames, buf, vv); + Py_DECREF(vv); + path_type = PY_IMP_NO_LISTDIR; + } + else if (use_os_listdir > 0) { + add_directory_names(buf); + path_type = PY_IMP_LISTDIR; + } + /* use_os_listdir == 0: We are starting up. */ + } #ifdef macintosh /* ** Speedup: each sys.path item is interned, and @@ -1026,6 +1593,8 @@ return &resfiledescr; } #endif + + /* join the module name to the path item */ if (len > 0 && buf[len-1] != SEP #ifdef ALTSEP && buf[len-1] != ALTSEP @@ -1035,9 +1604,90 @@ strcpy(buf+len, name); len += namelen; + switch(path_type) { /* Search according to path type */ + case PY_IMP_ZIP: /* Path includes ".zip/" */ +#ifdef ALTSEP + { + size_t j; + /* Use only SEP in zip names, never ALTSEP */ + for (j = 0; j < len; j++) + if (buf[j] == ALTSEP) + buf[j] = SEP; + } +#endif + for (zso = ZipSearchOrder; zso->type; zso++) { + strcpy(buf+len, zso->suffix); + if (Py_VerboseFlag > 1) + PySys_WriteStderr("# trying %s\n", buf); + if (PyDict_GetItemString(ExternalNames, buf)) { + if (zso->type == PKG_DIRECTORY) { + buf[len] = 0; + return &fd_package; + } + return &fd_zip; + } + } + break; + case PY_IMP_LISTDIR: /* We have read the directory names */ + if (PyDict_GetItemString(ExternalNames, buf)) { + /* Check for package directory */ + add_directory_names(buf); + buf[len] = SEP; + strcpy(buf+len+1, "__init__.py"); + if (get_path_type(ExternalNames, buf)) { + buf[len] = 0; + return &fd_package; + } + if (Py_OptimizeFlag) /* Add 'o' or 'c' */ + buf[len+12] = 'o'; + else + buf[len+12] = 'c'; + buf[len+13] = 0; + if (get_path_type(ExternalNames, buf)) { + buf[len] = 0; + return &fd_package; + } + } + /* Search the list of suffixes: .pyc, .pyd, ... */ + for (fdp = _PyImport_Filetab; fdp->suffix; fdp++) { + strcpy(buf+len, fdp->suffix); + if (Py_VerboseFlag > 1) + PySys_WriteStderr("# trying %s\n", buf); + if (get_path_type(ExternalNames, buf) && + (fp = fopen(buf, fdp->mode)) != NULL) { + *p_fp = fp; + return fdp; + } + } + break; + default: /* Not zip, no names: search using fopen() */ + fdp = search_using_fopen(name, buf, len, namelen, p_fp, copy); + if (fdp) + return fdp; + break; + } /* End of switch on path type */ + } /* End of loop on path items */ + PyErr_Format(PyExc_ImportError, + "No module named %.200s", name); + return NULL; +} + +static struct filedescr * +search_using_fopen(char *name, char *buf, size_t len, size_t namelen, FILE **p_fp, PyObject *copy) +{ + struct filedescr *fdp; + FILE *fp; + char *filemode; +#if defined(PYOS_OS2) + size_t saved_len; + size_t saved_namelen; + char *saved_buf = NULL; +#endif + /* Check for package import (buf holds a directory name, and there's an __init__ module in that directory */ #ifdef HAVE_STAT + struct stat statbuf; if (stat(buf, &statbuf) == 0 && /* it exists */ S_ISDIR(statbuf.st_mode) && /* it's a directory */ find_init_module(buf) && /* it has __init__.py */ @@ -1056,6 +1706,7 @@ } #endif #endif + fp = NULL; #ifdef macintosh fdp = PyMac_FindModuleExtension(buf, &len, name); if (fdp) { @@ -1126,16 +1777,11 @@ } #endif Py_XDECREF(copy); - if (fp != NULL) - break; - } - if (fp == NULL) { - PyErr_Format(PyExc_ImportError, - "No module named %.200s", name); - return NULL; - } *p_fp = fp; - return fdp; + if (fp == NULL) + return NULL; + else + return fdp; } /* case_ok(char* buf, int len, int namelen, char* name) @@ -1520,6 +2166,10 @@ Py_INCREF(m); break; + case PY_ZIPFILE: + m = load_zip_module(name, buf); + break; + default: PyErr_Format(PyExc_ImportError, "Don't know how to import %.200s (type code %d)", @@ -2309,9 +2959,19 @@ imp_is_builtin(PyObject *self, PyObject *args) { char *name; + int i; + if (!PyArg_ParseTuple(args, "s:is_builtin", &name)) return NULL; - return PyInt_FromLong(is_builtin(name)); + for (i = 0; PyImport_Inittab[i].name != NULL; i++) { + if (strcmp(name, PyImport_Inittab[i].name) == 0) { + if (PyImport_Inittab[i].initfunc == NULL) + return PyInt_FromLong(-1); + else + return PyInt_FromLong(1); + } + } + return PyInt_FromLong(0); } static PyObject * @@ -2576,6 +3236,7 @@ if (setint(d, "C_BUILTIN", C_BUILTIN) < 0) goto failure; if (setint(d, "PY_FROZEN", PY_FROZEN) < 0) goto failure; if (setint(d, "PY_CODERESOURCE", PY_CODERESOURCE) < 0) goto failure; + if (setint(d, "PY_ZIPFILE", PY_ZIPFILE) < 0) goto failure; failure: ; @@ -2615,6 +3276,7 @@ memcpy(p, PyImport_Inittab, (i+1) * sizeof(struct _inittab)); PyImport_Inittab = our_copy = p; memcpy(p+i, newtab, (n+1) * sizeof(struct _inittab)); + get_internal_names(); return 0; } diff "-ur" python.orig/dist/src/Python/importdl.h python/dist/src/Python/importdl.h --- python.orig/dist/src/Python/importdl.h Tue Feb 26 11:41:34 2002 +++ python/dist/src/Python/importdl.h Thu Nov 28 20:43:05 2002 @@ -16,7 +16,8 @@ PKG_DIRECTORY, C_BUILTIN, PY_FROZEN, - PY_CODERESOURCE /* Mac only */ + PY_CODERESOURCE, /* Mac only */ + PY_ZIPFILE }; struct filedescr { diff "-ur" python.orig/dist/src/Python/pythonrun.c python/dist/src/Python/pythonrun.c --- python.orig/dist/src/Python/pythonrun.c Sun Nov 17 17:52:44 2002 +++ python/dist/src/Python/pythonrun.c Thu Nov 28 20:43:05 2002 @@ -164,6 +164,7 @@ initsigs(); /* Signal handling stuff, including initintr() */ initmain(); /* Module __main__ */ + PyImport_InitZip(); if (!Py_NoSiteFlag) initsite(); /* Module site */ } @@ -331,6 +332,7 @@ PyDict_SetItemString(interp->sysdict, "modules", interp->modules); initmain(); + PyImport_InitZip(); if (!Py_NoSiteFlag) initsite(); } @@ -394,6 +396,20 @@ return progname; } +static char *scriptname = NULL; + +void +Py_SetScriptName(char *pn) +{ + scriptname = pn; +} + +char * +Py_GetScriptName(void) +{ + return scriptname; +} + static char *default_home = NULL; void diff "-ur" python.orig/dist/src/Python/sysmodule.c python/dist/src/Python/sysmodule.c --- python.orig/dist/src/Python/sysmodule.c Tue Oct 08 02:44:28 2002 +++ python/dist/src/Python/sysmodule.c Thu Nov 28 20:43:05 2002 @@ -970,80 +970,10 @@ PySys_SetArgv(int argc, char **argv) { PyObject *av = makeargvobject(argc, argv); - PyObject *path = PySys_GetObject("path"); if (av == NULL) Py_FatalError("no mem for sys.argv"); if (PySys_SetObject("argv", av) != 0) Py_FatalError("can't assign sys.argv"); - if (path != NULL) { - char *argv0 = argv[0]; - char *p = NULL; - int n = 0; - PyObject *a; -#ifdef HAVE_READLINK - char link[MAXPATHLEN+1]; - char argv0copy[2*MAXPATHLEN+1]; - int nr = 0; - if (argc > 0 && argv0 != NULL) - nr = readlink(argv0, link, MAXPATHLEN); - if (nr > 0) { - /* It's a symlink */ - link[nr] = '\0'; - if (link[0] == SEP) - argv0 = link; /* Link to absolute path */ - else if (strchr(link, SEP) == NULL) - ; /* Link without path */ - else { - /* Must join(dirname(argv0), link) */ - char *q = strrchr(argv0, SEP); - if (q == NULL) - argv0 = link; /* argv0 without path */ - else { - /* Must make a copy */ - strcpy(argv0copy, argv0); - q = strrchr(argv0copy, SEP); - strcpy(q+1, link); - argv0 = argv0copy; - } - } - } -#endif /* HAVE_READLINK */ -#if SEP == '\\' /* Special case for MS filename syntax */ - if (argc > 0 && argv0 != NULL) { - char *q; - p = strrchr(argv0, SEP); - /* Test for alternate separator */ - q = strrchr(p ? p : argv0, '/'); - if (q != NULL) - p = q; - if (p != NULL) { - n = p + 1 - argv0; - if (n > 1 && p[-1] != ':') - n--; /* Drop trailing separator */ - } - } -#else /* All other filename syntaxes */ - if (argc > 0 && argv0 != NULL) - p = strrchr(argv0, SEP); - if (p != NULL) { -#ifndef RISCOS - n = p + 1 - argv0; -#else /* don't include trailing separator */ - n = p - argv0; -#endif /* RISCOS */ -#if SEP == '/' /* Special case for Unix filename syntax */ - if (n > 1) - n--; /* Drop trailing separator */ -#endif /* Unix */ - } -#endif /* All others */ - a = PyString_FromStringAndSize(argv0, n); - if (a == NULL) - Py_FatalError("no mem for sys.path insertion"); - if (PyList_Insert(path, 0, a) < 0) - Py_FatalError("sys.path.insert(0) failed"); - Py_DECREF(a); - } Py_DECREF(av); }