Index: Include/pythonrun.h =================================================================== RCS file: /cvsroot/python/python/dist/src/Include/pythonrun.h,v retrieving revision 2.56 diff -c -r2.56 pythonrun.h *** Include/pythonrun.h 11 Dec 2002 14:04:58 -0000 2.56 --- Include/pythonrun.h 23 Dec 2002 22:14:09 -0000 *************** *** 99,104 **** --- 99,105 ---- PyAPI_FUNC(PyObject *) _PySys_Init(void); PyAPI_FUNC(void) _PyImport_Init(void); PyAPI_FUNC(void) _PyExc_Init(void); + PyAPI_FUNC(void) _PyImportHooks_Init(void); /* Various internal finalizers */ PyAPI_FUNC(void) _PyExc_Fini(void); Index: Lib/site.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/site.py,v retrieving revision 1.46 diff -c -r1.46 site.py *** Lib/site.py 9 Nov 2002 05:08:06 -0000 1.46 --- Lib/site.py 23 Dec 2002 22:14:09 -0000 *************** *** 73,88 **** # only absolute pathnames, even if we're running from the build directory. L = [] _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): ! continue ! else: ! if dir and not os.path.exists(dir): ! continue dir, dircase = makepath(dir) if not dircase in _dirs_in_sys_path: L.append(dir) --- 73,83 ---- # only absolute pathnames, even if we're running from the build directory. L = [] _dirs_in_sys_path = {} + dir = dircase = None # sys.path may be empty at this point for dir in sys.path: ! # Filter out duplicate paths (on case-insensitive file systems also ! # if they only differ in case); turn relative paths into absolute ! # paths. dir, dircase = makepath(dir) if not dircase in _dirs_in_sys_path: L.append(dir) Index: Lib/test/test_importhooks.py =================================================================== RCS file: Lib/test/test_importhooks.py diff -N Lib/test/test_importhooks.py *** /dev/null 1 Jan 1970 00:00:00 -0000 --- Lib/test/test_importhooks.py 23 Dec 2002 22:14:09 -0000 *************** *** 0 **** --- 1,213 ---- + import sys + import imp + import os + import unittest + from test import test_support + + + test_src = """\ + def get_name(): + return __name__ + def get_file(): + return __file__ + """ + + test_co = compile(test_src, "", "exec") + test_path = "!!!_test_!!!" + + + class ImportTracker: + """Importer that only tracks attempted imports.""" + def __init__(self): + self.imports = [] + def find_module(self, fullname, path=None): + self.imports.append(fullname) + return None + + + class TestImporter: + + modules = { + "hooktestmodule": (False, test_co), + "hooktestpackage": (True, test_co), + "hooktestpackage.sub": (True, test_co), + "hooktestpackage.sub.subber": (False, test_co), + } + + def __init__(self, path=test_path): + if path != test_path: + # if out class is on sys.path_hooks, we must raise + # ImportError for any path item that we can't handle. + raise ImportError + self.path = path + + def _get__path__(self): + raise NotImplementedError + + def find_module(self, fullname, path=None): + if fullname in self.modules: + return self + else: + return None + + def load_module(self, fullname): + ispkg, code = self.modules[fullname] + mod = imp.new_module(fullname) + sys.modules[fullname] = mod + mod.__file__ = "<%s>" % self.__class__.__name__ + mod.__importer__ = self + if ispkg: + mod.__path__ = self._get__path__() + exec code in mod.__dict__ + return mod + + + class MetaImporter(TestImporter): + def _get__path__(self): + return [] + + class PathImporter(TestImporter): + def _get__path__(self): + return [self.path] + + class ImporterOnSysPath(TestImporter): + def _get__path__(self): + return [self] + + + class ImportBlocker: + """Place an ImportBlocker instance on sys.meta_path and you + can be sure the modules you specified can't be imported, even + if it's a builtin.""" + def __init__(self, *namestoblock): + self.namestoblock = dict.fromkeys(namestoblock) + def find_module(self, fullname, path=None): + if fullname in self.namestoblock: + return self + return None + def load_module(self, fullname): + raise ImportError, "I dare you" + + + class ImpWrapper: + + def __init__(self, path=None): + if path is not None and not os.path.isdir(path): + raise ImportError + self.path = path + + def find_module(self, fullname, path=None): + subname = fullname.split(".")[-1] + if subname != fullname and self.path is None: + return None + if self.path is None: + path = None + else: + path = [self.path] + try: + file, filename, stuff = imp.find_module(subname, path) + except ImportError: + return None + return ImpLoader(file, filename, stuff) + + + class ImpLoader: + + def __init__(self, file, filename, stuff): + self.file = file + self.filename = filename + self.stuff = stuff + + def load_module(self, fullname): + mod = imp.load_module(fullname, self.file, self.filename, self.stuff) + if self.file: + self.file.close() + mod.__importer__ = self # for introspection + return mod + + + class ImportHooksBaseTestCase(unittest.TestCase): + + def setUp(self): + self.path = sys.path[:] + self.meta_path = sys.meta_path[:] + self.path_hooks = sys.path_hooks[:] + sys.path_importer_cache.clear() + self.tracker = ImportTracker() + sys.meta_path.insert(0, self.tracker) + + def tearDown(self): + sys.path[:] = self.path + sys.meta_path[:] = self.meta_path + sys.path_hooks[:] = self.path_hooks + sys.path_importer_cache.clear() + for fullname in self.tracker.imports: + if fullname in sys.modules: + del sys.modules[fullname] + + + class ImportHooksTestCase(ImportHooksBaseTestCase): + + def doTestImports(self, importer=None): + import hooktestmodule + import hooktestpackage + import hooktestpackage.sub + import hooktestpackage.sub.subber + self.assertEqual(hooktestmodule.get_name(), + "hooktestmodule") + self.assertEqual(hooktestpackage.get_name(), + "hooktestpackage") + self.assertEqual(hooktestpackage.sub.get_name(), + "hooktestpackage.sub") + self.assertEqual(hooktestpackage.sub.subber.get_name(), + "hooktestpackage.sub.subber") + if importer: + self.assertEqual(hooktestmodule.__importer__, importer) + self.assertEqual(hooktestpackage.__importer__, importer) + self.assertEqual(hooktestpackage.sub.__importer__, importer) + self.assertEqual(hooktestpackage.sub.subber.__importer__, importer) + + def testMetaPath(self): + i = MetaImporter() + sys.meta_path.append(i) + self.doTestImports(i) + + def testImporterOnSysPath(self): + i = ImporterOnSysPath() + sys.path.append(i) + self.doTestImports(i) + + def testPathHook(self): + sys.path_hooks.append(PathImporter) + sys.path.append(test_path) + self.doTestImports() + + def testBlocker(self): + mname = "exceptions" # an arbitrary harmless builtin module + if mname in sys.modules: + del sys.modules[mname] + sys.meta_path.append(ImportBlocker(mname)) + try: + __import__(mname) + except ImportError: + pass + else: + self.fail("'%s' was not supposed to be importable" % mname) + + def testImpWrapper(self): + i = ImpWrapper() + sys.meta_path.append(i) + sys.path_hooks.append(ImpWrapper) + mnames = ("colorsys", "urlparse", "distutils.core", "compiler.misc") + for mname in mnames: + parent = mname.split(".")[0] + for n in sys.modules.keys(): + if n.startswith(parent): + del sys.modules[n] + for mname in mnames: + m = __import__(mname, globals(), locals(), ["__dummy__"]) + m.__importer__ # to make sure we actually handled the import + + + if __name__ == "__main__": + test_support.run_unittest(ImportHooksTestCase) Index: Lib/test/test_zipimport.py =================================================================== RCS file: Lib/test/test_zipimport.py diff -N Lib/test/test_zipimport.py *** /dev/null 1 Jan 1970 00:00:00 -0000 --- Lib/test/test_zipimport.py 23 Dec 2002 22:14:10 -0000 *************** *** 0 **** --- 1,174 ---- + import sys + import os + import marshal + import imp + import struct + import time + + import zlib # implied prerequisite + from zipfile import ZipFile, ZipInfo, ZIP_STORED, ZIP_DEFLATED + from test import test_support + from test.test_importhooks import ImportHooksBaseTestCase, test_src, test_co + + import zipimport + + + def make_pyc(co, mtime): + data = marshal.dumps(co) + pyc = imp.get_magic() + struct.pack("", "exec"), NOW) + files = {TESTMOD + pyc_ext: (NOW, pyc), + "some.data": (NOW, "some data")} + self.doTest(pyc_ext, files, TESTMOD) + + + class CompressedZipImportTestCase(UncompressedZipImportTestCase): + compression = ZIP_DEFLATED + + + if __name__ == "__main__": + test_support.run_unittest(UncompressedZipImportTestCase) + test_support.run_unittest(CompressedZipImportTestCase) Index: Modules/Setup.dist =================================================================== RCS file: /cvsroot/python/python/dist/src/Modules/Setup.dist,v retrieving revision 1.34 diff -c -r1.34 Setup.dist *** Modules/Setup.dist 12 Dec 2002 17:37:50 -0000 1.34 --- Modules/Setup.dist 23 Dec 2002 22:14:10 -0000 *************** *** 113,118 **** --- 113,122 ---- _sre _sre.c # Fredrik Lundh's new regular expressions _codecs _codecsmodule.c # access to the builtin codecs and codec registry + # The zipimport module is always imported at startup. Having it as a + # builtin module avoids some bootstrapping problems and reduces overhead. + zipimport zipimport.c + # The rest of the modules listed in this file are all commented out by # default. Usually they can be detected and built as dynamically # loaded modules by the new setup.py script added in Python 2.1. If Index: Modules/getpath.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Modules/getpath.c,v retrieving revision 1.44 diff -c -r1.44 getpath.c *** Modules/getpath.c 23 Dec 2002 21:03:36 -0000 1.44 --- Modules/getpath.c 23 Dec 2002 22:14:10 -0000 *************** *** 365,370 **** --- 365,371 ---- char *path = getenv("PATH"); char *prog = Py_GetProgramName(); char argv0_path[MAXPATHLEN+1]; + char zip_path[MAXPATHLEN+1]; int pfound, efound; /* 1 if found; -1 if found build directory */ char *buf; size_t bufsz; *************** *** 483,488 **** --- 484,501 ---- 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, *************** *** 521,526 **** --- 534,540 ---- defpath = delim + 1; } + bufsz += strlen(zip_path) + 1; bufsz += strlen(exec_prefix) + 1; /* This is the only malloc call in this file */ *************** *** 540,545 **** --- 554,563 ---- } else buf[0] = '\0'; + + /* Next is the default zip path */ + strcat(buf, zip_path); + strcat(buf, delimiter); /* Next goes merge of compile-time $PYTHONPATH with * dynamically located prefix. Index: Modules/zipimport.c =================================================================== RCS file: Modules/zipimport.c diff -N Modules/zipimport.c *** /dev/null 1 Jan 1970 00:00:00 -0000 --- Modules/zipimport.c 23 Dec 2002 22:14:10 -0000 *************** *** 0 **** --- 1,1219 ---- + #include "Python.h" + #include "structmember.h" + #include "osdefs.h" + #include "marshal.h" + #include "compile.h" + #include + + + #define IS_SOURCE 0x0 + #define IS_BYTECODE 0x1 + #define IS_PACKAGE 0x2 + + struct st_zip_searchorder { + char suffix[14]; + int type; + }; + + /* zip_searchorder defines how we search for a module in the Zip + archive: we first search for a package __init__, then for + non-package .pyc, .pyo and .py entries. The .pyc and .pyo entries + are swapped by initzipimport() if we run in optimized mode. Also, + '/' is replaced by SEP there. */ + struct st_zip_searchorder zip_searchorder[] = { + {"/__init__.pyc", IS_PACKAGE | IS_BYTECODE}, + {"/__init__.pyo", IS_PACKAGE | IS_BYTECODE}, + {"/__init__.py", IS_PACKAGE | IS_SOURCE}, + {".pyc", IS_BYTECODE}, + {".pyo", IS_BYTECODE}, + {".py", IS_SOURCE}, + {"", 0} + }; + + /* zipimporter object definition and support */ + + typedef struct _zipimporter ZipImporter; + + struct _zipimporter { + PyObject_HEAD + PyObject *archive; /* pathname of the Zip archive */ + PyObject *files; /* dict with file info {path: toc_entry} */ + }; + + static PyTypeObject ZipImporter_Type; + static PyObject *ZipImportError; + + /* forward decls */ + static PyObject *read_directory(char *archive); + static PyObject *get_data(char *archive, PyObject *toc_entry); + static PyObject *get_module_code(ZipImporter *self, char *fullname, + int *p_ispackage, char **p_modpath); + + + #define ZipImporter_Check(op) PyObject_TypeCheck(op, &ZipImporter_Type) + + + /* zipimporter.__init__ + Split the "subdirectory" from the Zip archive path, lookup a matching + entry in sys.path_importer_cache, fetch the file directory from there + if found, or else read it from the archive. */ + static int + zipimporter_init(ZipImporter *self, PyObject *args, PyObject *kwds) + { + char *path; + int len; + + if (!PyArg_ParseTuple(args, "s:zipimporter", + &path)) + return -1; + + len = strlen(path); + if (len == 0) { + PyErr_SetString(ZipImportError, "archive path is empty"); + return -1; + } + if (len >= MAXPATHLEN) { + PyErr_SetString(ZipImportError, + "archive path too long"); + return -1; + } + self->archive = PyString_FromString(path); + if (self->archive == NULL) + return -1; + + self->files = read_directory(path); + if (self->files == NULL) + return -1; + return 0; + } + + /* GC support. */ + static int + zipimporter_traverse(PyObject *obj, visitproc visit, void *arg) + { + ZipImporter *self = (ZipImporter *)obj; + int err; + + if (self->files != NULL) { + err = visit(self->files, arg); + if (err) + return err; + } + return 0; + } + + static void + zipimporter_dealloc(ZipImporter *self) + { + PyObject_GC_UnTrack(self); + Py_XDECREF(self->archive); + Py_XDECREF(self->files); + self->ob_type->tp_free((PyObject *)self); + } + + static PyObject * + zipimporter_repr(ZipImporter *self) + { + char buf[500]; + char *archive = "???"; + if (self->archive != NULL && PyString_Check(self->archive)) + archive = PyString_AsString(self->archive); + + PyOS_snprintf(buf, sizeof(buf), + "", + archive); + return PyString_FromString(buf); + } + + /* Given a (sub)modulename, write the potential file path in the + archive (without extension) to the path buffer. Return the + length of the resulting string. */ + static int + make_filename(char *name, char *path) + { + char *p; + + /* name [+ SEP + "__init__"] + ".py[co]" */ + if (strlen(name) + 13 >= MAXPATHLEN) { + PyErr_SetString(ZipImportError, "path too long"); + return -1; + } + + strcpy(path, name); + for (p = path; *p; p++) { + if (*p == '.') + *p = SEP; + } + return strlen(name); + } + + enum module_info { + MI_ERROR, + MI_NOT_FOUND, + MI_MODULE, + MI_PACKAGE + }; + + /* Return some information about a module. */ + static enum module_info + get_module_info(ZipImporter *self, char *fullname) + { + char path[MAXPATHLEN + 1]; + int len; + struct st_zip_searchorder *zso; + + len = make_filename(fullname, path); + if (len < 0) + return MI_ERROR; + + for (zso = zip_searchorder; *zso->suffix; zso++) { + strcpy(path + len, zso->suffix); + if (PyDict_GetItemString(self->files, path) != NULL) { + if (zso->type & IS_PACKAGE) + return MI_PACKAGE; + else + return MI_MODULE; + } + } + return MI_NOT_FOUND; + } + + /* Check whether we can satisfy the import of the module named by + 'fullname'. Return self if we can, None if we can't. */ + static PyObject * + zipimporter_find_module(PyObject *obj, PyObject *args) + { + ZipImporter *self = (ZipImporter *)obj; + PyObject *path = NULL; + char *fullname; + enum module_info mi; + + if (!PyArg_ParseTuple(args, "s|O:zipimporter.find_module", + &fullname, &path)) + return NULL; + + mi = get_module_info(self, fullname); + if (mi == MI_ERROR) + return NULL; + if (mi == MI_NOT_FOUND) { + Py_INCREF(Py_None); + return Py_None; + } + Py_INCREF(self); + return (PyObject *)self; + } + + /* Load and return the module named by 'fullname'. */ + static PyObject * + zipimporter_load_module(PyObject *obj, PyObject *args) + { + ZipImporter *self = (ZipImporter *)obj; + PyObject *code, *mod, *dict; + char *fullname, *modpath; + int ispackage; + + if (!PyArg_ParseTuple(args, "s:zipimporter.load_module", + &fullname)) + return NULL; + + code = get_module_code(self, fullname, &ispackage, &modpath); + if (code == NULL) + return NULL; + + mod = PyImport_AddModule(fullname); + if (mod == NULL) { + Py_DECREF(code); + return NULL; + } + dict = PyModule_GetDict(mod); + + /* mod.__importer__ = self */ + if (PyDict_SetItemString(dict, "__importer__", (PyObject *)self) != 0) + goto error; + + if (ispackage) { + /* add __path__ to the module *before* the code gets + executed */ + PyObject *pkgpath; + int err; + + pkgpath = Py_BuildValue("[O]", self->archive); + if (pkgpath == NULL) + goto error; + err = PyDict_SetItemString(dict, "__path__", pkgpath); + Py_DECREF(pkgpath); + if (err != 0) + goto error; + } + mod = PyImport_ExecCodeModuleEx(fullname, code, modpath); + Py_DECREF(code); + if (Py_VerboseFlag) + PySys_WriteStderr("import %s # loaded from Zip %s\n", + fullname, modpath); + return mod; + error: + Py_DECREF(code); + Py_DECREF(mod); + return NULL; + } + + /* Return None if the module is not a package, a list to use as + pkg.__path__ if it is a package. Raise ZipImportError if the + module couldn't be found. */ + static PyObject * + zipimporter_get_package_path(PyObject *obj, PyObject *args) + { + ZipImporter *self = (ZipImporter *)obj; + char *fullname; + enum module_info mi; + + if (!PyArg_ParseTuple(args, "s:zipimporter.find_module", + &fullname)) + return NULL; + + mi = get_module_info(self, fullname); + if (mi == MI_ERROR) + return NULL; + if (mi == MI_NOT_FOUND) { + PyErr_Format(ZipImportError, "can't find module '%.200s'", + fullname); + return NULL; + } + if (mi == MI_PACKAGE) + return Py_BuildValue("[O]", self->archive); + Py_INCREF(Py_None); + return Py_None; + } + + static PyObject * + zipimporter_get_data(PyObject *obj, PyObject *args) + { + ZipImporter *self = (ZipImporter *)obj; + char *path; + #ifdef ALTSEP + char *p, buf[MAXPATHLEN + 1]; + #endif + PyObject *toc_entry; + + if (!PyArg_ParseTuple(args, "s:zipimporter.get_data", &path)) + return NULL; + + #ifdef ALTSEP + if (strlen(path) >= MAXPATHLEN) { + PyErr_SetString(ZipImportError, "path too long"); + return NULL; + } + strcpy(buf, path); + for (p = buf; *p; p++) { + if (*p == ALTSEP) + *p = SEP; + } + path = buf; + #endif + toc_entry = PyDict_GetItemString(self->files, path); + if (toc_entry == NULL) { + PyErr_Format(PyExc_IOError, "file not found [%.200s]", + path); + return NULL; + } + return get_data(PyString_AsString(self->archive), toc_entry); + } + + static PyObject * + zipimporter_get_code(PyObject *obj, PyObject *args) + { + ZipImporter *self = (ZipImporter *)obj; + char *fullname; + + if (!PyArg_ParseTuple(args, "s:zipimporter.get_code", &fullname)) + return NULL; + + return get_module_code(self, fullname, NULL, NULL); + } + + static PyObject * + zipimporter_get_source(PyObject *obj, PyObject *args) + { + ZipImporter *self = (ZipImporter *)obj; + PyObject *toc_entry; + char *fullname, path[MAXPATHLEN+1]; + int len; + enum module_info mi; + + if (!PyArg_ParseTuple(args, "s:zipimporter.get_source", &fullname)) + return NULL; + + mi = get_module_info(self, fullname); + if (mi == MI_ERROR) + return NULL; + if (mi == MI_NOT_FOUND) { + PyErr_Format(ZipImportError, "can't find module '%.200s'", + fullname); + return NULL; + } + + len = make_filename(fullname, path); + if (len < 0) + return NULL; + + if (mi == MI_PACKAGE) { + path[len] = SEP; + strcpy(path + len + 1, "__init__.py"); + } + else + strcpy(path + len, ".py"); + + toc_entry = PyDict_GetItemString(self->files, path); + if (toc_entry != NULL) + return get_data(PyString_AsString(self->archive), toc_entry); + + /* we have the module, but no source */ + Py_INCREF(Py_None); + return Py_None; + } + + PyDoc_STRVAR(doc_find_module, + "find_module(fullname, path=None) -> self or None.\n\ + \n\ + Search for a module specified by 'fullname'. 'fullname' must be the\n\ + fully qualified (dotted) module name. It returns the zipimporter\n\ + instance itself if the module was found, or None if it wasn't.\n\ + The optional 'path' argument is ignored -- it's there for compatibility\n\ + with the importer protocol."); + + PyDoc_STRVAR(doc_load_module, + "load_module(fullname) -> module.\n\ + \n\ + Load the module specified by 'fullname'. 'fullname' must be the\n\ + fully qualified (dotted) module name. It returns the imported\n\ + module, or raises ZipImportError if it wasn't found."); + + PyDoc_STRVAR(doc_get_data, + "get_data(pathname) -> string with file data.\n\ + \n\ + Return the data associated with 'pathname'. Raise IOError if\n\ + the file wasn't found."); + + PyDoc_STRVAR(doc_get_package_path, + "get_package_path(fullname) -> None or pkg.__path__ list.\n\ + \n\ + Return None is the module specified by 'fullname' is a plain module,\n\ + return the value for pkg.__path__ if it's a package (which is always\n\ + [path_to_zip_archive] for packages importer by zipimporter). Raise\n\ + ZipImportError is the module couldn't be found."); + + PyDoc_STRVAR(doc_get_code, + "get_code(fullname) -> code object.\n\ + \n\ + Return the code object for the specified module. Raise ZipImportError\n\ + is the module couldn't be found."); + + PyDoc_STRVAR(doc_get_source, + "get_source(fullname) -> source string.\n\ + \n\ + Return the source code for the specified module. Raise ZipImportError\n\ + is the module couldn't be found, return None if the archive does\n\ + contain the module, but has no source for it."); + + static PyMethodDef zipimporter_methods[] = { + {"find_module", zipimporter_find_module, METH_VARARGS, + doc_find_module}, + {"load_module", zipimporter_load_module, METH_VARARGS, + doc_load_module}, + {"get_data", zipimporter_get_data, METH_VARARGS, + doc_get_data}, + {"get_code", zipimporter_get_code, METH_VARARGS, + doc_get_code}, + {"get_source", zipimporter_get_source, METH_VARARGS, + doc_get_source}, + {"get_package_path", zipimporter_get_package_path, METH_VARARGS, + doc_get_package_path}, + {NULL, NULL} /* sentinel */ + }; + + static PyMemberDef zipimporter_members[] = { + {"archive", T_OBJECT, offsetof(ZipImporter, archive), READONLY}, + {"_files", T_OBJECT, offsetof(ZipImporter, files), READONLY}, + {NULL} + }; + + PyDoc_STRVAR(zipimporter_doc, + "zipimporter(archivepath) -> zipimporter object\n\ + \n\ + Create a new zipimporter instance. 'archivepath' must be a path to\n\ + a zipfile. ZipImportError is raised if 'archivepath' doesn't point to\n\ + a valid Zip archive."); + + #define DEFERRED_ADDRESS(ADDR) 0 + + static PyTypeObject ZipImporter_Type = { + PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type)) + 0, + "zipimport.zipimporter", + sizeof(ZipImporter), + 0, /* tp_itemsize */ + (destructor)zipimporter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)zipimporter_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HAVE_GC, /* tp_flags */ + zipimporter_doc, /* tp_doc */ + zipimporter_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + zipimporter_methods, /* tp_methods */ + zipimporter_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)zipimporter_init, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + PyObject_GC_Del, /* tp_free */ + }; + + + /* implementation */ + + /* Given a buffer, return the short that is represented by the first + 2 bytes, encoded as little endian. This partially reimplements + marshal.c:r_short() + XXX It's currently only used by the unused read_directory_FAST + function, which will be deleted unless someone proves it can make + a significant improvement. See below.*/ + static int + get_short(unsigned char *buf) + { + short x; + x = buf[0]; + x |= buf[1] << 8; + /* Sign-extension, in case short greater than 16 bits */ + x |= -(x & 0x8000); + return x; + } + + /* Given a buffer, return the long that is represented by the first + 4 bytes, encoded as little endian. This partially reimplements + marshal.c:r_long() */ + static long + get_long(unsigned char *buf) { + long x; + x = buf[0]; + x |= (long)buf[1] << 8; + x |= (long)buf[2] << 16; + x |= (long)buf[3] << 24; + #if SIZEOF_LONG > 4 + /* Sign extension for 64-bit machines */ + x |= -(x & 0x80000000L); + #endif + return x; + } + + /* + read_directory(archive) -> files dict (new reference) + + Given a path to a Zip archive, build a dict, mapping file names + (local to the archive, using SEP as a separator) to toc entries. + + A toc_entry is a tuple: + + (compress, # compression kind; 0 for uncompressed + data_size, # size of compressed data on disk + file_size, # size of decompressed data + file_offset, # offset of file header from start of archive + time, # mod time of file (in dos format) + date, # mod data of file (in dos format) + crc, # crc checksum of the data + ) + + Directories can be recognized by the trailing SEP in the name, + data_size and file_offset are 0. + */ + static PyObject * + read_directory(char *archive) + { + PyObject *files = NULL; + FILE *fp; + long compress, crc, data_size, file_size, file_offset, date, time; + long header_offset, name_size, header_size, header_end; + long i, l, length, count; + char path[MAXPATHLEN + 5]; + char name[MAXPATHLEN + 5]; + char *p, endof_central_dir[22]; + + if (strlen(archive) > MAXPATHLEN) { + PyErr_SetString(PyExc_OverflowError, + "Zip path name is too long"); + return NULL; + } + strcpy(path, archive); + + fp = fopen(archive, "rb"); + if (fp == NULL) { + PyErr_Format(ZipImportError, "can't open Zip file: " + "'%.200s'", archive); + return NULL; + } + fseek(fp, -22, 2); /* Seek from end of file */ + header_end = ftell(fp); + if (fread(endof_central_dir, 1, 22, fp) != 22) { + fclose(fp); + PyErr_Format(ZipImportError, "can't read Zip file: " + "'%.200s'", archive); + return NULL; + } + if (get_long(endof_central_dir) != 0x06054B50) { + /* Bad: End of Central Dir signature */ + fclose(fp); + PyErr_Format(ZipImportError, "not a Zip file: " + "'%.200s'", archive); + return NULL; + } + + header_offset = get_long(endof_central_dir + 16); + + files = PyDict_New(); + if (files == NULL) + goto error; + + length = (long)strlen(path); + path[length] = SEP; + + /* Start of Central Directory */ + count = 0; + for (;;) { + PyObject *t; + int err; + + 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; + + p = name; + for (i = 0; i < name_size; i++) { + *p = (char)getc(fp); + if (*p == '/') + *p = SEP; + p++; + } + *p = 0; /* Add terminating null byte */ + header_offset += header_size; + + strncpy(path + length + 1, name, MAXPATHLEN - length - 1); + + t = Py_BuildValue("siiiiiii", path, compress, data_size, + file_size, file_offset, time, date, crc); + if (t == NULL) + goto error; + err = PyDict_SetItemString(files, name, t); + Py_DECREF(t); + if (err != 0) + goto error; + count++; + } + fclose(fp); + if (Py_VerboseFlag) + PySys_WriteStderr("# zipimport: found %ld names in %s\n", + count, archive); + return files; + error: + fclose(fp); + Py_XDECREF(files); + return NULL; + } + + /* XXX The function below is an unfinished replacement for + read_directory() which reads the entire central directory into + memory instead of doing fseek()/PyMarshal_Read* all the time. + I only managed to shave off about 10% on my platform (MacOSX), + so I don't think is worth the hassle. If you think it may + make a greater difference on your platform: please try it out + and send me the results. Try it with a Zip containing + a large amount of files: + + import time + from zipimport import zipimporter + + archive = "Archive.zip" + + def test(N): + r = range(N) + t = time.clock() + for i in r: + z = zipimporter(archive) + print "time elapsed:", time.clock() - t + + test(250) + + It's unfinished as it may read beyond the buffer. -- jvr + */ + static PyObject * + read_directory_FAST(char *archive) + { + PyObject *files = NULL; + FILE *fp; + long compress, crc, data_size, file_size, file_offset, date, time; + long header_offset, name_size, header_size, header_end; + long i, l, length, count; + char path[MAXPATHLEN + 5]; + char name[MAXPATHLEN + 5]; + char *p, endof_central_dir[22], *central_dir = NULL; + char *error_msg = "unknown error"; + + if (strlen(archive) > MAXPATHLEN) { + PyErr_SetString(PyExc_OverflowError, + "Zip path name is too long"); + return NULL; + } + strcpy(path, archive); + + fp = fopen(archive, "rb"); + if (fp == NULL) { + error_msg = "can't open Zip file: '%.200s'"; + goto error; + } + fseek(fp, -22, 2); /* Seek from end of file */ + header_end = ftell(fp); + if (fread(endof_central_dir, 1, 22, fp) != 22) { + error_msg = "can't read Zip file: '%.200s'"; + goto error; + } + if (get_long(endof_central_dir) != 0x06054B50) { + /* Bad: End of Central Dir signature */ + error_msg = "not a Zip file: '%.200s'"; + goto error; + } + + header_offset = get_long(endof_central_dir + 16); + header_size = header_end - header_offset; + + central_dir = PyMem_Malloc(header_size); /* XXX check */ + if (central_dir == NULL) { + PyErr_SetString(PyExc_MemoryError, "can't allocate memory " + "for Zip directory"); + goto error; + } + + fseek(fp, header_offset, 0); /* Start of file header */ + if (fread(central_dir, 1, header_size, fp) != header_size) { + error_msg = "can't read Zip header for '%.200s'"; + goto error; + } + fclose(fp); + header_offset = 0; + + files = PyDict_New(); + if (files == NULL) + goto error; + + length = (long)strlen(path); + path[length] = SEP; + + /* Start of Central Directory */ + count = 0; + for (;;) { + PyObject *t; + int err; + + l = get_long(central_dir + header_offset); + if (l != 0x02014B50) + break; /* Bad: Central Dir File Header */ + compress = get_short(central_dir + header_offset + 10); + time = get_short(central_dir + header_offset + 12); + date = get_short(central_dir + header_offset + 14); + crc = get_long(central_dir + header_offset + 16); + data_size = get_long(central_dir + header_offset + 20); + file_size = get_long(central_dir + header_offset + 24); + name_size = get_short(central_dir + header_offset + 28); + header_size = 46 + name_size + + get_short(central_dir + header_offset + 30); + + get_short(central_dir + header_offset + 32);; + file_offset = get_long(central_dir + header_offset + 42); + if (name_size > MAXPATHLEN) + name_size = MAXPATHLEN; + + p = name; + for (i = 0; i < name_size; i++) { + *p = (char)central_dir[header_offset + 46 + i]; + if (*p == '/') + *p = SEP; + p++; + } + *p = 0; /* Add terminating null byte */ + header_offset += header_size; + + strncpy(path + length + 1, name, MAXPATHLEN - length - 1); + + t = Py_BuildValue("siiiiiii", path, compress, data_size, + file_size, file_offset, time, date, crc); + if (t == NULL) + goto error; + err = PyDict_SetItemString(files, name, t); + Py_DECREF(t); + if (err != 0) + goto error; + count++; + } + PyMem_Free(central_dir); + if (Py_VerboseFlag) + PySys_WriteStderr("# zipimport: found %ld names in %s\n", + count, archive); + return files; + error: + if (fp != NULL) + fclose(fp); + if (central_dir != NULL) + PyMem_Free(central_dir); + Py_XDECREF(files); + if (!PyErr_Occurred()) + PyErr_Format(ZipImportError, error_msg, archive); + return NULL; + } + + /* Return the zlib.decompress function object, or NULL if zlib couldn't + be imported. The function is cached when found, so subsequent calls + don't import zlib again. Returns a *borrowed* reference. + XXX This makes zlib.decompress immortal. */ + static PyObject * + get_decompress_func(void) + { + static PyObject *decompress = NULL; + + if (decompress == NULL) { + PyObject *zlib; + static int importing_zlib = 0; + + if (importing_zlib != 0) + /* Someone has a zlib.py[co] in their Zip file; + let's avoid a stack overflow. */ + return NULL; + importing_zlib = 1; + zlib = PyImport_ImportModule("zlib"); /* import zlib */ + importing_zlib = 0; + if (zlib != NULL) { + decompress = PyObject_GetAttrString(zlib, + "decompress"); + Py_DECREF(zlib); + } + else + PyErr_Clear(); + if (Py_VerboseFlag) + PySys_WriteStderr("# zipimport: zlib %s\n", + zlib != NULL ? "available": "UNAVAILABLE"); + } + return decompress; + } + + /* Given a path to a Zip file and a toc_entry, return the (uncompressed) + data as a new reference. */ + static PyObject * + get_data(char *archive, PyObject *toc_entry) + { + PyObject *raw_data, *data = NULL, *decompress; + char *buf; + FILE *fp; + int err, bytes_read = 0; + long l; + char *datapath; + long compress, data_size, file_size, file_offset; + long time, date, crc; + + if (!PyArg_ParseTuple(toc_entry, "siiiiiii", &datapath, &compress, + &data_size, &file_size, &file_offset, &time, + &date, &crc)) { + return NULL; + } + + fp = fopen(archive, "rb"); + if (!fp) { + PyErr_Format(PyExc_IOError, + "zipimport: can not open file %s", archive); + return NULL; + } + + /* Check to make sure the local file header is correct */ + fseek(fp, file_offset, 0); + l = PyMarshal_ReadLongFromFile(fp); + if (l != 0x04034B50) { + /* Bad: Local File Header */ + PyErr_Format(ZipImportError, + "bad local file header in %s", + archive); + fclose(fp); + return NULL; + } + fseek(fp, file_offset + 26, 0); + l = 30 + PyMarshal_ReadShortFromFile(fp) + + PyMarshal_ReadShortFromFile(fp); /* local header size */ + file_offset += l; /* Start of file data */ + + raw_data = PyString_FromStringAndSize((char *)NULL, compress == 0 ? + data_size : data_size + 1); + if (raw_data == NULL) { + fclose(fp); + return NULL; + } + buf = PyString_AsString(raw_data); + + err = fseek(fp, file_offset, 0); + if (err == 0) + bytes_read = fread(buf, 1, data_size, fp); + fclose(fp); + if (err || bytes_read != data_size) { + PyErr_SetString(PyExc_IOError, + "zipimport: can't read data"); + Py_DECREF(raw_data); + return NULL; + } + + if (compress != 0) { + buf[data_size] = 'Z'; /* saw this in zipfile.py */ + data_size++; + } + buf[data_size] = '\0'; + + if (compress == 0) /* data is not compressed */ + return raw_data; + + /* Decompress with zlib */ + decompress = get_decompress_func(); + if (decompress == NULL) { + PyErr_SetString(ZipImportError, + "can't decompress data; " + "zlib not available"); + goto error; + } + data = PyObject_CallFunction(decompress, "Ol", raw_data, -15); + error: + Py_DECREF(raw_data); + return data; + } + + /* Lenient date/time comparison function. The precision of the mtime + in the archive is lower than the mtime stored in a .pyc: we + must allow a difference of at most one second. */ + static int + eq_mtime(time_t t1, time_t t2) + { + time_t d = t1 - t2; + if (d < 0) + d = -d; + /* dostime only stores even seconds, so be lenient */ + return d <= 1; + } + + /* Given the contents of a .py[co] file in a buffer, unmarshal the data + and return the code object. Return None if it the magic word doesn't + match (we do this instead of raising an exception as we fall back + to .py if available and we don't want to mask other errors). + Returns a new reference. */ + static PyObject * + unmarshal_code(char *fullname, char *pathname, PyObject *data, time_t mtime) + { + PyObject *code; + char *buf = PyString_AsString(data); + int size = PyString_Size(data); + + if (size <= 9) { + PyErr_SetString(ZipImportError, + "bad pyc data"); + return NULL; + } + + if (get_long(buf) != PyImport_GetMagicNumber()) { + if (Py_VerboseFlag) + PySys_WriteStderr("# %s has bad magic\n", + pathname); + Py_INCREF(Py_None); + return Py_None; /* signal caller to try alternative */ + } + + if (mtime != 0 && !eq_mtime(get_long(buf + 4), mtime)) { + if (Py_VerboseFlag) + PySys_WriteStderr("# %s has bad mtime\n", + pathname); + Py_INCREF(Py_None); + return Py_None; /* signal caller to try alternative */ + } + + code = PyMarshal_ReadObjectFromString(buf + 8, size - 8); + if (code == NULL) + return NULL; + if (!PyCode_Check(code)) { + Py_DECREF(code); + PyErr_Format(PyExc_TypeError, + "compiled module %.200s is not a code object", + pathname); + return NULL; + } + return code; + } + + /* Replace any occurances of "\r\n?" in the input string with "\n". + This converts DOS and Mac line endings to Unix line endings. + Also append a trailing "\n" to be compatible with + PyParser_SimpleParseFile(). Returns a new reference. */ + static PyObject * + normalize_line_endings(PyObject *source) + { + char *q, *p = PyString_AsString(source); + int length = PyString_Size(source) + 1; + PyObject *fixed_source; + + fixed_source = PyString_FromStringAndSize(p, length); + if (fixed_source == NULL) + return NULL; + + q = PyString_AsString(fixed_source); + /* replace "\r\n?" by "\n" */ + for (;;) { + if (*p == '\r') { + *q++ = '\n'; + if (*(p + 1) == '\n') { + p++; + length--; + } + } + else + *q++ = *p; + if (*p == '\0') + break; + p++; + } + *q++ = '\n'; /* add trailing \n */ + *q = '\0'; + _PyString_Resize(&fixed_source, length); + return fixed_source; + } + + /* Given a string buffer containing Python source code, compile it + return and return a code object as a new reference. */ + static PyObject * + compile_source(char *fullname, char *pathname, PyObject *source) + { + PyObject *code, *fixed_source; + + fixed_source = normalize_line_endings(source); + if (fixed_source == NULL) + return NULL; + + code = Py_CompileString(PyString_AsString(fixed_source), pathname, + Py_file_input); + Py_DECREF(fixed_source); + return code; + } + + /* Convert the date/time values found in the Zip archive to a value + that's compatible with the time stamp stored in .pyc files. */ + time_t parse_dostime(int dostime, int dosdate) + { + struct tm stm; + + stm.tm_sec = (dostime & 0x1f) * 2; + stm.tm_min = (dostime >> 5) & 0x3f; + stm.tm_hour = (dostime >> 11) & 0x1f; + stm.tm_mday = dosdate & 0x1f; + stm.tm_mon = ((dosdate >> 5) & 0x0f) - 1; + stm.tm_year = ((dosdate >> 9) & 0x7f) + 80; + stm.tm_isdst = 0; /* wday/yday is ignored */ + + return mktime(&stm); + } + + /* Given a path to a .pyc or .pyo file in the archive, return the + modifictaion time of the matching .py file, or 0 if no source + is available. */ + static time_t + get_mtime_of_source(ZipImporter *self, char *path) + { + PyObject *toc_entry; + time_t mtime = 0; + int lastchar = strlen(path) - 1; + char savechar = path[lastchar]; + path[lastchar] = '\0'; /* strip 'c' or 'o' from *.py[co] */ + toc_entry = PyDict_GetItemString(self->files, path); + if (toc_entry != NULL && PyTuple_Check(toc_entry) && + PyTuple_Size(toc_entry) == 8) { + /* fetch the time stamp of the .py file for comparison + with an embedded pyc time stamp */ + int time, date; + time = PyInt_AsLong(PyTuple_GetItem(toc_entry, 5)); + date = PyInt_AsLong(PyTuple_GetItem(toc_entry, 6)); + mtime = parse_dostime(time, date); + } + path[lastchar] = savechar; + return mtime; + } + + /* Return the code object for the module named by 'fullname' from the + Zip archive as a new reference. */ + static PyObject * + get_code_from_data(ZipImporter *self, char *fullname, + int ispackage, int isbytecode, time_t mtime, + PyObject *toc_entry) + { + PyObject *data, *code; + char *modpath; + char *archive = PyString_AsString(self->archive); + + if (archive == NULL) + return NULL; + + data = get_data(archive, toc_entry); + if (data == NULL) + return NULL; + + modpath = PyString_AsString(PyTuple_GetItem(toc_entry, 0)); + + if (isbytecode) { + code = unmarshal_code(fullname, modpath, data, mtime); + } + else { + code = compile_source(fullname, modpath, data); + } + Py_DECREF(data); + return code; + } + + /* Get the code object assoiciated with the module specified by + 'fullname'. */ + static PyObject * + get_module_code(ZipImporter *self, char *fullname, + int *p_ispackage, char **p_modpath) + { + PyObject *toc_entry; + char path[MAXPATHLEN + 1]; + int len; + struct st_zip_searchorder *zso; + + len = make_filename(fullname, path); + if (len < 0) + return NULL; + + for (zso = zip_searchorder; *zso->suffix; zso++) { + PyObject *code = NULL; + + strcpy(path + len, zso->suffix); + if (Py_VerboseFlag > 1) + PySys_WriteStderr("# trying %s%c%s\n", + PyString_AsString(self->archive), + SEP, path); + toc_entry = PyDict_GetItemString(self->files, path); + if (toc_entry != NULL) { + time_t mtime = 0; + int ispackage = zso->type & IS_PACKAGE; + int isbytecode = zso->type & IS_BYTECODE; + + if (isbytecode) + mtime = get_mtime_of_source(self, path); + if (p_ispackage != NULL) + *p_ispackage = ispackage; + code = get_code_from_data(self, fullname, ispackage, + isbytecode, mtime, + toc_entry); + if (code == Py_None) { + /* bad magic number or non-matching mtime + in byte code, try next */ + Py_DECREF(code); + continue; + } + if (code != NULL && p_modpath != NULL) + *p_modpath = PyString_AsString( + PyTuple_GetItem(toc_entry, 0)); + return code; + } + } + PyErr_Format(ZipImportError, "can't find module '%.200s'", fullname); + return NULL; + } + + + /* Module init */ + + PyDoc_STRVAR(zipimport_doc, + "zipimport provides support for importing Python modules from Zip archives.\n\ + \n\ + This module exports two objects:\n\ + - zipimporter: a class; its constructor takes a path to a Zip archive.\n\ + - ZipImporterError: exception raised by zipimporter objects. It's a\n\ + subclass of ImportError, so it can be caught as ImportError, too.\n\ + \n\ + It is usually not needed to use the zipimport module explicitly; it is\n\ + used by the builtin import mechanism for sys.path items that are paths\n\ + to Zip archives."); + + PyMODINIT_FUNC + initzipimport(void) + { + PyObject *mod; + + if (PyType_Ready(&ZipImporter_Type) < 0) + return; + + /* Correct directory separator */ + zip_searchorder[0].suffix[0] = SEP; + zip_searchorder[1].suffix[0] = SEP; + zip_searchorder[2].suffix[0] = SEP; + if (Py_OptimizeFlag) { + /* Reverse *.pyc and *.pyo */ + struct st_zip_searchorder tmp; + tmp = zip_searchorder[0]; + zip_searchorder[0] = zip_searchorder[1]; + zip_searchorder[1] = tmp; + tmp = zip_searchorder[3]; + zip_searchorder[3] = zip_searchorder[4]; + zip_searchorder[4] = tmp; + } + + mod = Py_InitModule4("zipimport", NULL, zipimport_doc, + NULL, PYTHON_API_VERSION); + + ZipImportError = PyErr_NewException("zipimport.ZipImportError", + PyExc_ImportError, NULL); + if (ZipImportError == NULL) + return; + + Py_INCREF(ZipImportError); + if (PyModule_AddObject(mod, "ZipImportError", + ZipImportError) < 0) + return; + + Py_INCREF(&ZipImporter_Type); + if (PyModule_AddObject(mod, "zipimporter", + (PyObject *)&ZipImporter_Type) < 0) + return; + } Index: PC/config.c =================================================================== RCS file: /cvsroot/python/python/dist/src/PC/config.c,v retrieving revision 1.35 diff -c -r1.35 config.c *** PC/config.c 7 Jul 2002 03:59:34 -0000 1.35 --- PC/config.c 23 Dec 2002 22:14:10 -0000 *************** *** 43,48 **** --- 43,49 ---- extern void init_weakref(void); extern void init_hotshot(void); extern void initxxsubtype(void); + extern void initzipimport(void); /* XXX tim: what's the purpose of ADDMODULE MARKER? */ /* -- ADDMODULE MARKER 1 -- */ *************** *** 96,101 **** --- 97,103 ---- {"_hotshot", init_hotshot}, {"xxsubtype", initxxsubtype}, + {"zipimport", initzipimport}, /* XXX tim: what's the purpose of ADDMODULE MARKER? */ /* -- ADDMODULE MARKER 2 -- */ Index: PC/getpathp.c =================================================================== RCS file: /cvsroot/python/python/dist/src/PC/getpathp.c,v retrieving revision 1.28 diff -c -r1.28 getpathp.c *** PC/getpathp.c 22 Jul 2002 13:28:21 -0000 1.28 --- PC/getpathp.c 23 Dec 2002 22:14:11 -0000 *************** *** 81,86 **** --- 81,87 ---- static char prefix[MAXPATHLEN+1]; static char progpath[MAXPATHLEN+1]; + static char dllpath[MAXPATHLEN+1]; static char *module_search_path = NULL; *************** *** 350,355 **** --- 351,357 ---- char *prog = Py_GetProgramName(); #ifdef MS_WINDOWS + extern HANDLE PyWin_DLLhModule; #ifdef UNICODE WCHAR wprogpath[MAXPATHLEN+1]; /* Windows documents that GetModuleFileName() will "truncate", *************** *** 357,362 **** --- 359,372 ---- 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,371 **** --- 376,384 ---- } #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 *************** *** 427,432 **** --- 440,447 ---- int skiphome, skipdefault; char *machinepath = NULL; char *userpath = NULL; + char zip_path[MAXPATHLEN+1]; + size_t len; #endif get_progpath(); *************** *** 447,452 **** --- 462,482 ---- #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; + } + skiphome = pythonhome==NULL ? 0 : 1; machinepath = getpythonregpath(HKEY_LOCAL_MACHINE, skiphome); userpath = getpythonregpath(HKEY_CURRENT_USER, skiphome); *************** *** 458,471 **** /* 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 "." of each component replaced with pythonhome, if set; ! (4) 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. */ /* Calculate size of return buffer */ --- 488,502 ---- /* We need to construct a path from the following parts. (1) the PYTHONPATH environment variable, if set; ! (2) for Win32, the zip archive file path; ! (3) for Win32, the machinepath and userpath, if set; ! (4) the PYTHONPATH config macro, with the leading "." of each component replaced with pythonhome, if set; ! (5) the directory containing the executable (argv0_path). ! The length calculation calculates #4 first. Extra rules: ! - If PYTHONHOME is set (in any way) item (3) is ignored. ! - If registry values are used, (4) and (5) are ignored. */ /* Calculate size of return buffer */ *************** *** 487,492 **** --- 518,524 ---- bufsz += strlen(userpath) + 1; if (machinepath) bufsz += strlen(machinepath) + 1; + bufsz += strlen(zip_path) + 1; #endif if (envpath != NULL) bufsz += strlen(envpath) + 1; *************** *** 518,523 **** --- 550,560 ---- *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'); Index: PCbuild/pythoncore.dsp =================================================================== RCS file: /cvsroot/python/python/dist/src/PCbuild/pythoncore.dsp,v retrieving revision 1.38 diff -c -r1.38 pythoncore.dsp *** PCbuild/pythoncore.dsp 10 Jul 2002 06:22:10 -0000 1.38 --- PCbuild/pythoncore.dsp 23 Dec 2002 22:14:11 -0000 *************** *** 545,549 **** --- 545,553 ---- SOURCE=..\Modules\yuvconvert.c # End Source File + # Begin Source File + + SOURCE=..\Modules\zipimport.c + # End Source File # End Target # End Project Index: Python/import.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/import.c,v retrieving revision 2.213 diff -c -r2.213 import.c *** Python/import.c 13 Dec 2002 15:23:10 -0000 2.213 --- Python/import.c 23 Dec 2002 22:14:11 -0000 *************** *** 153,158 **** --- 153,203 ---- } void + _PyImportHooks_Init(void) + { + PyObject *v, *zimp_mod, *zipimporter = NULL; + int err = 0; + + /* adding sys.path_hooks and sys.path_importer_cache, setting up + zipimport */ + + if (Py_VerboseFlag) + PySys_WriteStderr("# installing zipimport hook\n"); + + err = PySys_SetObject("meta_path", v = PyList_New(0)); + Py_XDECREF(v); + err = PySys_SetObject("path_importer_cache", v = PyDict_New()); + Py_XDECREF(v); + if (err == 0) + PySys_SetObject("path_hooks", v = PyList_New(0)); + if (v == NULL) { + if (Py_VerboseFlag) + PySys_WriteStderr("# adding sys.path_hooks or " + "path_importer_cache failed\n"); + PyErr_Clear(); + return; /* nothing we can do */ + } + zimp_mod = PyImport_ImportModule("zipimport"); + if (zimp_mod != NULL) { + zipimporter = PyObject_GetAttrString(zimp_mod, + "zipimporter"); + Py_DECREF(zimp_mod); + } + if (zipimporter != NULL) { + /* sys.path_hooks.append(zipimporter) */ + err = PyList_Append(v, zipimporter); + Py_DECREF(zipimporter); + } + if (err != 0 || zipimporter == NULL) { + if (Py_VerboseFlag) + PySys_WriteStderr( + "# installing zipimport hook failed\n"); + PyErr_Clear(); + } + Py_DECREF(v); + } + + void _PyImport_Fini(void) { Py_XDECREF(extensions); *************** *** 246,251 **** --- 291,297 ---- "path", "argv", "ps1", "ps2", "exitfunc", "exc_type", "exc_value", "exc_traceback", "last_type", "last_value", "last_traceback", + "path_hooks", "path_importer_cache", "meta_path", NULL }; *************** *** 808,816 **** /* Forward */ ! static PyObject *load_module(char *, FILE *, char *, int); ! static struct filedescr *find_module(char *, PyObject *, ! char *, size_t, FILE **); static struct _frozen *find_frozen(char *name); /* Load a package and return its module object WITH INCREMENTED --- 854,862 ---- /* Forward */ ! static PyObject *load_module(char *, FILE *, char *, int, PyObject *); ! static struct filedescr *find_module(char *, char *, PyObject *, ! char *, size_t, FILE **, PyObject **); static struct _frozen *find_frozen(char *name); /* Load a package and return its module object WITH INCREMENTED *************** *** 848,854 **** goto cleanup; } buf[0] = '\0'; ! fdp = find_module("__init__", path, buf, sizeof(buf), &fp); if (fdp == NULL) { if (PyErr_ExceptionMatches(PyExc_ImportError)) { PyErr_Clear(); --- 894,900 ---- goto cleanup; } buf[0] = '\0'; ! fdp = find_module(name, "__init__", path, buf, sizeof(buf), &fp, NULL); if (fdp == NULL) { if (PyErr_ExceptionMatches(PyExc_ImportError)) { PyErr_Clear(); *************** *** 857,863 **** m = NULL; goto cleanup; } ! m = load_module(name, fp, buf, fdp->type); if (fp != NULL) fclose(fp); cleanup: --- 903,909 ---- m = NULL; goto cleanup; } ! m = load_module(name, fp, buf, fdp->type, NULL); if (fp != NULL) fclose(fp); cleanup: *************** *** 884,889 **** --- 930,984 ---- return 0; } + /* Return an importer object for a sys.path/pkg.__path__ item 'p', + possibly by fetching it from the path_importer_cache dict. If it + wasn't yet cached, traverse path_hooks until it a hook is found + that can handle the path item. Return None if no hook could; + this tells our caller it should fall back to the builtin + import mechanism. Cache the result in path_importer_cache. + Returns a borrowed reference. */ + static PyObject * + get_path_importer(PyObject *path_importer_cache, PyObject *path_hooks, + PyObject *p) + { + PyObject *importer; + int j, nhooks = PyList_Size(path_hooks); + + importer = PyDict_GetItem(path_importer_cache, p); + if (importer != NULL) + return importer; + + /* set path_importer_cache[p] to None to avoid recursion */ + if (PyDict_SetItem(path_importer_cache, p, Py_None) != 0) + return NULL; + + for (j = 0; j < nhooks; j++) { + PyObject *hook = PyList_GetItem(path_hooks, j); + if (hook == NULL) + return NULL; + importer = PyObject_CallFunction(hook, "O", p); + if (importer != NULL) + break; + + if (!PyErr_ExceptionMatches(PyExc_ImportError)) { + if (Py_VerboseFlag) { + PySys_WriteStderr("# import hook failed:\n"); + PyErr_Print(); + } + return NULL; + } + PyErr_Clear(); + } + if (importer == NULL) + importer = Py_None; + else if (importer != Py_None) { + int err = PyDict_SetItem(path_importer_cache, p, importer); + Py_DECREF(importer); + if (err != 0) + importer = NULL; + } + return importer; + } /* Search the path (default sys.path) for a module. Return the corresponding filedescr struct, and (via return arguments) the *************** *** 896,911 **** static int case_ok(char *, int, int, char *); static int find_init_module(char *); /* Forward */ static struct filedescr * ! find_module(char *realname, PyObject *path, char *buf, size_t buflen, ! FILE **p_fp) { int i, npath; size_t len, namelen; struct filedescr *fdp = NULL; char *filemode; FILE *fp = NULL; #ifndef RISCOS struct stat statbuf; #endif --- 991,1008 ---- static int case_ok(char *, int, int, char *); static int find_init_module(char *); /* Forward */ + static struct filedescr importhookdescr = {"", "", IMP_HOOK}; static struct filedescr * ! find_module(char *fullname, char *subname, PyObject *path, char *buf, ! size_t buflen, FILE **p_fp, PyObject **p_loader) { int i, npath; size_t len, namelen; struct filedescr *fdp = NULL; char *filemode; FILE *fp = NULL; + PyObject *path_hooks, *path_importer_cache; #ifndef RISCOS struct stat statbuf; #endif *************** *** 918,930 **** size_t saved_namelen; char *saved_buf = NULL; #endif ! if (strlen(realname) > MAXPATHLEN) { PyErr_SetString(PyExc_OverflowError, "module name is too long"); return NULL; } ! strcpy(name, realname); if (path != NULL && PyString_Check(path)) { /* The only type of submodule allowed inside a "frozen" --- 1015,1059 ---- size_t saved_namelen; char *saved_buf = NULL; #endif + if (p_loader != NULL) + *p_loader = NULL; ! if (strlen(subname) > MAXPATHLEN) { PyErr_SetString(PyExc_OverflowError, "module name is too long"); return NULL; } ! strcpy(name, subname); ! ! /* sys.meta_path import hook */ ! if (p_loader != NULL) { ! PyObject *meta_path; ! ! meta_path = PySys_GetObject("meta_path"); ! if (meta_path == NULL || !PyList_Check(meta_path)) { ! PyErr_SetString(PyExc_ImportError, ! "sys.meta_path must be a list of " ! "import hooks"); ! return NULL; ! } ! npath = PyList_Size(meta_path); ! for (i = 0; i < npath; i++) { ! PyObject *loader; ! PyObject *hook = PyList_GetItem(meta_path, i); ! loader = PyObject_CallMethod(hook, "find_module", ! "sO", fullname, ! path != NULL ? ! path : Py_None); ! if (loader == NULL) ! return NULL; /* error */ ! if (loader != Py_None) { ! /* a loader was found */ ! *p_loader = loader; ! return &importhookdescr; ! } ! Py_DECREF(loader); ! } ! } if (path != NULL && PyString_Check(path)) { /* The only type of submodule allowed inside a "frozen" *************** *** 978,988 **** --- 1107,1151 ---- "sys.path must be a list of directory names"); return NULL; } + + path_hooks = PySys_GetObject("path_hooks"); + if (path_hooks == NULL || !PyList_Check(path_hooks)) { + PyErr_SetString(PyExc_ImportError, + "sys.path_hooks must be a list of " + "import hooks"); + return NULL; + } + path_importer_cache = PySys_GetObject("path_importer_cache"); + if (path_importer_cache == NULL || + !PyDict_Check(path_importer_cache)) { + PyErr_SetString(PyExc_ImportError, + "sys.path_importer_cache must be a dict"); + return NULL; + } + npath = PyList_Size(path); namelen = strlen(name); for (i = 0; i < npath; i++) { PyObject *copy = NULL; PyObject *v = PyList_GetItem(path, i); + + /* object on sys.path/pkg.__path__ import hook */ + if (p_loader != NULL && !PyString_CheckExact(v) && + PyObject_HasAttrString(v, "find_module")) { + /* path item is an importer object */ + PyObject *loader; + loader = PyObject_CallMethod(v, "find_module", + "s", fullname); + if (loader == NULL) + return NULL; /* error */ + if (loader != Py_None) { + /* a loader was found */ + *p_loader = loader; + return &importhookdescr; + } + Py_DECREF(loader); + /* importer couldn't find the module */ + } #ifdef Py_USING_UNICODE if (PyUnicode_Check(v)) { copy = PyUnicode_Encode(PyUnicode_AS_UNICODE(v), *************** *** 1005,1010 **** --- 1168,1200 ---- Py_XDECREF(copy); continue; /* v contains '\0' */ } + + /* sys.path_hooks import hook */ + if (p_loader != NULL) { + PyObject *importer; + + importer = get_path_importer(path_importer_cache, + path_hooks, v); + if (importer == NULL) + return NULL; + /* Note: importer is a borrowed reference */ + if (importer != Py_None) { + PyObject *loader; + loader = PyObject_CallMethod(importer, + "find_module", + "s", fullname); + if (loader == NULL) + return NULL; /* error */ + if (loader != Py_None) { + /* a loader was found */ + *p_loader = loader; + return &importhookdescr; + } + Py_DECREF(loader); + } + /* no hook was successful, use builtin import */ + } + #ifdef macintosh /* ** Speedup: each sys.path item is interned, and *************** *** 1079,1085 **** * dynamically loaded module we're going to try, * truncate the name before trying */ ! if (strlen(realname) > 8) { /* is this an attempt to load a C extension? */ const struct filedescr *scan; scan = _PyImport_DynLoadFiletab; --- 1269,1275 ---- * dynamically loaded module we're going to try, * truncate the name before trying */ ! if (strlen(subname) > 8) { /* is this an attempt to load a C extension? */ const struct filedescr *scan; scan = _PyImport_DynLoadFiletab; *************** *** 1092,1098 **** if (scan->suffix != NULL) { /* yes, so truncate the name */ namelen = 8; ! len -= strlen(realname) - namelen; buf[len] = '\0'; } } --- 1282,1288 ---- if (scan->suffix != NULL) { /* yes, so truncate the name */ namelen = 8; ! len -= strlen(subname) - namelen; buf[len] = '\0'; } } *************** *** 1444,1450 **** its module object WITH INCREMENTED REFERENCE COUNT */ static PyObject * ! load_module(char *name, FILE *fp, char *buf, int type) { PyObject *modules; PyObject *m; --- 1634,1640 ---- its module object WITH INCREMENTED REFERENCE COUNT */ static PyObject * ! load_module(char *name, FILE *fp, char *buf, int type, PyObject *loader) { PyObject *modules; PyObject *m; *************** *** 1523,1528 **** --- 1713,1728 ---- Py_INCREF(m); break; + case IMP_HOOK: { + if (loader == NULL) { + PyErr_SetString(PyExc_ImportError, + "import hook without loader"); + return NULL; + } + m = PyObject_CallMethod(loader, "load_module", "s", name); + break; + } + default: PyErr_Format(PyExc_ImportError, "Don't know how to import %.200s (type code %d)", *************** *** 1978,1984 **** Py_INCREF(m); } else { ! PyObject *path; char buf[MAXPATHLEN+1]; struct filedescr *fdp; FILE *fp = NULL; --- 2178,2184 ---- Py_INCREF(m); } else { ! PyObject *path, *loader; char buf[MAXPATHLEN+1]; struct filedescr *fdp; FILE *fp = NULL; *************** *** 1995,2001 **** } buf[0] = '\0'; ! fdp = find_module(subname, path, buf, MAXPATHLEN+1, &fp); Py_XDECREF(path); if (fdp == NULL) { if (!PyErr_ExceptionMatches(PyExc_ImportError)) --- 2195,2202 ---- } buf[0] = '\0'; ! fdp = find_module(fullname, subname, path, buf, MAXPATHLEN+1, ! &fp, &loader); Py_XDECREF(path); if (fdp == NULL) { if (!PyErr_ExceptionMatches(PyExc_ImportError)) *************** *** 2004,2010 **** Py_INCREF(Py_None); return Py_None; } ! m = load_module(fullname, fp, buf, fdp->type); if (fp) fclose(fp); if (mod != Py_None) { --- 2205,2212 ---- Py_INCREF(Py_None); return Py_None; } ! m = load_module(fullname, fp, buf, fdp->type, loader); ! Py_XDECREF(loader); if (fp) fclose(fp); if (mod != Py_None) { *************** *** 2080,2090 **** PyErr_Clear(); } buf[0] = '\0'; ! fdp = find_module(subname, path, buf, MAXPATHLEN+1, &fp); Py_XDECREF(path); if (fdp == NULL) return NULL; ! m = load_module(name, fp, buf, fdp->type); if (fp) fclose(fp); return m; --- 2282,2292 ---- PyErr_Clear(); } buf[0] = '\0'; ! fdp = find_module(name, subname, path, buf, MAXPATHLEN+1, &fp, NULL); Py_XDECREF(path); if (fdp == NULL) return NULL; ! m = load_module(name, fp, buf, fdp->type, NULL); if (fp) fclose(fp); return m; *************** *** 2228,2234 **** pathname[0] = '\0'; if (path == Py_None) path = NULL; ! fdp = find_module(name, path, pathname, MAXPATHLEN+1, &fp); if (fdp == NULL) return NULL; if (fp != NULL) { --- 2430,2436 ---- pathname[0] = '\0'; if (path == Py_None) path = NULL; ! fdp = find_module(NULL, name, path, pathname, MAXPATHLEN+1, &fp, NULL); if (fdp == NULL) return NULL; if (fp != NULL) { *************** *** 2594,2600 **** if (fp == NULL) return NULL; } ! return load_module(name, fp, pathname, type); } static PyObject * --- 2796,2802 ---- if (fp == NULL) return NULL; } ! return load_module(name, fp, pathname, type, NULL); } static PyObject * *************** *** 2726,2731 **** --- 2928,2934 ---- 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, "IMP_HOOK", IMP_HOOK) < 0) goto failure; failure: ; Index: Python/importdl.h =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/importdl.h,v retrieving revision 2.18 diff -c -r2.18 importdl.h *** Python/importdl.h 26 Feb 2002 11:41:34 -0000 2.18 --- Python/importdl.h 23 Dec 2002 22:14:12 -0000 *************** *** 16,22 **** PKG_DIRECTORY, C_BUILTIN, PY_FROZEN, ! PY_CODERESOURCE /* Mac only */ }; struct filedescr { --- 16,23 ---- PKG_DIRECTORY, C_BUILTIN, PY_FROZEN, ! PY_CODERESOURCE, /* Mac only */ ! IMP_HOOK }; struct filedescr { Index: Python/pythonrun.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/pythonrun.c,v retrieving revision 2.171 diff -c -r2.171 pythonrun.c *** Python/pythonrun.c 13 Dec 2002 15:23:00 -0000 2.171 --- Python/pythonrun.c 23 Dec 2002 22:14:12 -0000 *************** *** 161,166 **** --- 161,168 ---- /* phase 2 of builtins */ _PyImport_FixupExtension("__builtin__", "__builtin__"); + _PyImportHooks_Init(); + initsigs(); /* Signal handling stuff, including initintr() */ initmain(); /* Module __main__ */