Index: Python/frozenmain.c =================================================================== --- Python/frozenmain.c (revision 60883) +++ Python/frozenmain.c (working copy) @@ -47,7 +47,7 @@ PySys_SetArgv(argc, argv); - n = PyImport_ImportFrozenModule("__main__"); + n = PyImport_ImportFrozenModule("__main__", NULL); if (n == 0) Py_FatalError("__main__ not frozen"); if (n < 0) { Index: Python/getargs.c =================================================================== --- Python/getargs.c (revision 60883) +++ Python/getargs.c (working copy) @@ -823,7 +823,7 @@ } else return converterr("string", arg, msgbuf, bufsize); - if ((Py_ssize_t)strlen(*p) != PyUnicode_GetSize(arg)) + if ((Py_ssize_t)strlen(*p) != PyString_GET_SIZE(uarg)) return converterr("string without null bytes", arg, msgbuf, bufsize); } @@ -899,7 +899,7 @@ format++; } else if (*p != NULL && - (Py_ssize_t)strlen(*p) != PyUnicode_GetSize(arg)) + (Py_ssize_t)strlen(*p) != PyString_GET_SIZE(uarg)) return converterr( "string without null bytes or None", arg, msgbuf, bufsize); Index: Python/pythonrun.c =================================================================== --- Python/pythonrun.c (revision 60883) +++ Python/pythonrun.c (working copy) @@ -579,14 +579,14 @@ interp->modules = PyDict_New(); interp->modules_reloading = PyDict_New(); - bimod = _PyImport_FindExtension("builtins", "builtins"); + bimod = _PyImport_FindExtension("builtins", "builtins", NULL); if (bimod != NULL) { interp->builtins = PyModule_GetDict(bimod); if (interp->builtins == NULL) goto handle_error; Py_INCREF(interp->builtins); } - sysmod = _PyImport_FindExtension("sys", "sys"); + sysmod = _PyImport_FindExtension("sys", "sys", NULL); if (bimod != NULL && sysmod != NULL) { interp->sysdict = PyModule_GetDict(sysmod); if (interp->sysdict == NULL) @@ -733,6 +733,7 @@ PyObject *m; PyObject *std = NULL; int status = 0, fd; + PyObject * encoding_attr; /* Hack to avoid a nasty recursion issue when Python is invoked in verbose mode: pre-import the Latin-1 and UTF-8 codecs */ @@ -823,6 +824,19 @@ goto error; } } /* if (fd < 0) */ + + /* Same as hack above, pre-import stderr's codec to avoid recursion + when import.c tries to write to stderr in verbose mode. */ + encoding_attr = PyObject_GetAttrString(std, "encoding"); + if (encoding_attr != NULL) { + const char * encoding; + encoding = PyUnicode_AsString(encoding_attr); + if (encoding != NULL) { + _PyCodec_Lookup(encoding); + } + } + PyErr_Clear(); /* Not a fatal error if codec isn't available */ + PySys_SetObject("__stderr__", std); PySys_SetObject("stderr", std); Py_DECREF(std); Index: Python/import.c =================================================================== --- Python/import.c (revision 60883) +++ Python/import.c (working copy) @@ -14,6 +14,7 @@ #include "eval.h" #include "osdefs.h" #include "importdl.h" +#include "structmember.h" #ifdef HAVE_FCNTL_H #include @@ -22,7 +23,7 @@ extern "C" { #endif -extern time_t PyOS_GetLastModificationTime(char *, FILE *); +extern time_t PyOS_GetLastModificationTime(const char *, FILE *); /* In getmtime.c */ /* Magic word to reject .pyc files generated by other Python versions. @@ -160,46 +161,108 @@ } } +/* Set up importer/loader support */ void _PyImportHooks_Init(void) { - PyObject *v, *path_hooks = NULL, *zimpimport; + PyObject *builtin_meta_path = NULL; + PyObject *meta_path = NULL; + PyObject *builtin_path_importer_cache = NULL; + PyObject *path_importer_cache = NULL; + PyObject *builtin_path_hooks = NULL; + PyObject *path_hooks = NULL; + PyObject * builtin_importer = NULL; + PyObject *zimpimport = NULL; int err = 0; - /* adding sys.path_hooks and sys.path_importer_cache, setting up - zipimport */ if (PyType_Ready(&PyNullImporter_Type) < 0) goto error; + if (PyType_Ready(&PyModuleInfo_Type) < 0) + goto error; + if (PyType_Ready(&PyBuiltinImporter_Type) < 0) + goto error; + if (PyType_Ready(&PyDirectoryImporter_Type) < 0) + goto error; - if (Py_VerboseFlag) - PySys_WriteStderr("# installing zipimport hook\n"); - - v = PyList_New(0); - if (v == NULL) + meta_path = PyList_New(0); + if (meta_path == NULL) goto error; - err = PySys_SetObject("meta_path", v); - Py_DECREF(v); + err = PySys_SetObject("meta_path", meta_path); if (err) goto error; - v = PyDict_New(); - if (v == NULL) + path_importer_cache = PyDict_New(); + if (path_importer_cache == NULL) goto error; - err = PySys_SetObject("path_importer_cache", v); - Py_DECREF(v); + err = PySys_SetObject("path_importer_cache", path_importer_cache); + Py_DECREF(path_importer_cache); if (err) goto error; path_hooks = PyList_New(0); if (path_hooks == NULL) goto error; err = PySys_SetObject("path_hooks", path_hooks); + if (err) + goto error; + /* A second set of hooks with only the standard import + functionality. Used to support imp.find_module() et al */ + builtin_meta_path = PyList_New(0); + if (builtin_meta_path == NULL) + goto error; + err = PySys_SetObject("builtin_meta_path", builtin_meta_path); + if (err) + goto error; + builtin_path_importer_cache = PyDict_New(); + if (builtin_path_importer_cache == NULL) + goto error; + err = PySys_SetObject("builtin_path_importer_cache", + builtin_path_importer_cache); + Py_DECREF(builtin_path_importer_cache); + if (err) + goto error; + builtin_path_hooks = PyList_New(0); + if (builtin_path_hooks == NULL) + goto error; + err = PySys_SetObject("builtin_path_hooks", builtin_path_hooks); if (err) { error: PyErr_Print(); Py_FatalError("initializing sys.meta_path, sys.path_hooks, " - "path_importer_cache, or NullImporter failed" + "sys.path_importer_cache, or importers failed" ); } + /* Install builtin/frozen importer */ + builtin_importer = NULL; + builtin_importer = PyObject_CallFunctionObjArgs + ((PyObject *)&PyBuiltinImporter_Type, NULL); + if (builtin_importer == NULL) { + goto error; + } + err = PyList_Append(meta_path, builtin_importer); + if (err == -1) { + goto error; + } + err = PyList_Append(builtin_meta_path, builtin_importer); + if (err == -1) { + goto error; + } + Py_XDECREF(builtin_importer); + + /* Install standard file importer */ + err = PyList_Append(path_hooks, (PyObject *)&PyDirectoryImporter_Type); + if (err == -1) { + goto error; + } + err = PyList_Append(builtin_path_hooks, + (PyObject *)&PyDirectoryImporter_Type); + if (err == -1) { + goto error; + } + + /* Install zipimporter */ + if (Py_VerboseFlag) + PySys_WriteStderr("# installing zipimport hook\n"); + zimpimport = PyImport_ImportModule("zipimport"); if (zimpimport == NULL) { PyErr_Clear(); /* No zip import module -- okay */ @@ -227,7 +290,11 @@ "# installed zipimport hook\n"); } } - Py_DECREF(path_hooks); + + Py_XDECREF(meta_path); + Py_XDECREF(path_hooks); + Py_XDECREF(builtin_meta_path); + Py_XDECREF(builtin_path_hooks); } void @@ -371,6 +438,8 @@ "path", "argv", "ps1", "ps2", "last_type", "last_value", "last_traceback", "path_hooks", "path_importer_cache", "meta_path", + "builtin_path_hooks", "builtin_path_importer_cache", + "builtin_meta_path", /* misc stuff */ "flags", "float_info", NULL @@ -535,10 +604,13 @@ dictionary is stored by calling _PyImport_FixupExtension() immediately after the module initialization function succeeds. A copy can be retrieved from there by calling - _PyImport_FindExtension(). */ + _PyImport_FindExtension(). + name: fully qualified module name (default encoding) + file: module's __file__ (?? encoding) +*/ PyObject * -_PyImport_FixupExtension(char *name, char *filename) +_PyImport_FixupExtension(const char *name, const char *filename) { PyObject *modules, *mod, *dict, *copy; if (extensions == NULL) { @@ -564,8 +636,10 @@ return copy; } +/* name: fully qualified module name (default encoding) + loader: module's loader object, or NULL */ PyObject * -_PyImport_FindExtension(char *name, char *filename) +_PyImport_FindExtension(const char *name, const char *filename, PyObject *loader) { PyObject *dict, *mod, *mdict; if (extensions == NULL) @@ -581,6 +655,10 @@ return NULL; if (PyDict_Update(mdict, dict)) return NULL; + if (loader != NULL) { + if (PyDict_SetItemString(mdict, "__loader__", loader) != 0) + return NULL; + } if (Py_VerboseFlag) PySys_WriteStderr("import %s # previously loaded (%s)\n", name, filename); @@ -592,7 +670,9 @@ First check the modules dictionary if there's one there, if not, create a new one and insert it in the modules dictionary. Because the former action is most common, THIS DOES NOT RETURN A - 'NEW' REFERENCE! */ + 'NEW' REFERENCE! + name: fully qualified module name (default encoding) +*/ PyObject * PyImport_AddModule(const char *name) @@ -615,7 +695,9 @@ return m; } -/* Remove name from sys.modules, if it's there. */ +/* Remove name from sys.modules, if it's there. + name: fully qualified module name (default encoding) + */ static void _RemoveModule(const char *name) { @@ -627,6 +709,55 @@ "sys.modules failed"); } +/* Convert a filename in the default encoding to a filename in the filesystem + * encoding. The data is stored in a PyString object which is returned to the + * caller. + * filename_def_enc: filename to convert (default encoding) + * filename_fs_enc_bytes: (return value) pointer into bytes, or NULL + * Return value: new bytes object to store result, or NULL + */ +static PyObject * +def_enc_to_fs_enc(const char *filename_def_enc, const char ** p_filename_fs_enc) +{ + PyObject * filename; + PyObject * filename_bytes; + filename = PyUnicode_FromString(filename_def_enc); + if (filename == NULL) { + *p_filename_fs_enc = NULL; + return NULL; + } + filename_bytes = PyUnicode_AsEncodedString( + filename, Py_FileSystemDefaultEncoding, NULL); + Py_DECREF(filename); + if (filename_bytes == NULL) { + *p_filename_fs_enc = NULL; + return NULL; + } + *p_filename_fs_enc = PyString_AS_STRING(filename_bytes); + return filename_bytes; +} + +/* Convert a filename from default to filesystem encoding and call stat(). + filename_def_enc: (default encoding) + statbuf: pointer to allocated structure + (return value) filled in by stat() call + Returns: as stat() +*/ +static int stat_def_enc(const char * filename_def_enc, struct stat * statbuf) { + PyObject * filename_fs_enc_bytes; /* filesystem encoding */ + const char * filename_fs_enc; + int rv; + + filename_fs_enc_bytes = def_enc_to_fs_enc(filename_def_enc, + &filename_fs_enc); + if (filename_fs_enc_bytes == NULL) { + return -1; + } + rv = stat(filename_fs_enc, statbuf); + Py_DECREF(filename_fs_enc_bytes); + return rv; +} + static PyObject * get_sourcefile(const char *file); /* Execute a code object in a module and return the module object @@ -635,15 +766,20 @@ * in sys.modules. The caller may wish to restore the original * module object (if any) in this case; PyImport_ReloadModule is an * example. + * name: fully qualified module name (default encoding) + * co: code object + * pathname: module's __file__ (default encoding) + * loader: module's loader object, or NULL */ PyObject * -PyImport_ExecCodeModule(char *name, PyObject *co) +PyImport_ExecCodeModule(const char *name, PyObject *co) { - return PyImport_ExecCodeModuleEx(name, co, (char *)NULL); + return PyImport_ExecCodeModuleEx(name, co, NULL, NULL); } PyObject * -PyImport_ExecCodeModuleEx(char *name, PyObject *co, char *pathname) +PyImport_ExecCodeModuleEx(const char *name, PyObject *co, const char *pathname, + PyObject * loader) { PyObject *modules = PyImport_GetModuleDict(); PyObject *m, *d, *v; @@ -673,7 +809,10 @@ if (PyDict_SetItemString(d, "__file__", v) != 0) PyErr_Clear(); /* Not important enough to report */ Py_DECREF(v); - + if (loader != NULL) { + if (PyDict_SetItemString(d, "__loader__", loader) != 0) + goto error; + } v = PyEval_EvalCode((PyCodeObject *)co, d, d); if (v == NULL) goto error; @@ -695,26 +834,45 @@ return NULL; } +/* Extract the non-qualified module name from a fully qualified module name. + E.g. "spam.eggs.bacon" -> "bacon", "spam" -> "spam" + fullname: fully qualified module name (default encoding) + Return value: pointer into fullname +*/ +static const char * +PyImport_GetSubname(const char *fullname) +{ + const char *subname = strrchr(fullname, '.'); + if (subname == NULL) + subname = fullname; + else + subname++; + return subname; +} /* Given a pathname for a Python source file, fill a buffer with the pathname for the corresponding compiled file. Return the pathname for the compiled file, or NULL if there's no space in the buffer. - Doesn't set an exception. */ + Doesn't set an exception. + pathname: source filename (default encoding) + Return value: (default encoding) +*/ static char * -make_compiled_pathname(char *pathname, char *buf, size_t buflen) +make_compiled_pathname(const char *pathname_def_enc, char *buf, + size_t buflen) { - size_t len = strlen(pathname); + size_t len = strlen(pathname_def_enc); if (len+2 > buflen) return NULL; #ifdef MS_WINDOWS /* Treat .pyw as if it were .py. The case of ".pyw" must match that used in _PyImport_StandardFiletab. */ - if (len >= 4 && strcmp(&pathname[len-4], ".pyw") == 0) + if (len >= 4 && strcmp(&pathname_def_enc[len-4], ".pyw") == 0) --len; /* pretend 'w' isn't there */ #endif - memcpy(buf, pathname, len); + memcpy(buf, pathname_def_enc, len); buf[len] = Py_OptimizeFlag ? 'o' : 'c'; buf[len+1] = '\0'; @@ -727,42 +885,54 @@ compiled file represents the same version of the source. If so, return a FILE pointer for the compiled file, positioned just after the header; if not, return NULL. - Doesn't set an exception. */ + Doesn't set an exception. + pathname_def_enc: (default encoding) + mtime: modification time of source file + pathname_fs_enc, cpathname_fs_enc: (filesystem encoding) +*/ static FILE * -check_compiled_module(char *pathname, time_t mtime, char *cpathname) +check_compiled_module(const char *pathname_def_enc, + time_t mtime, const char *cpathname_def_enc, + const char * cpathname_fs_enc) { FILE *fp; long magic; long pyc_mtime; - fp = fopen(cpathname, "rb"); + fp = fopen(cpathname_fs_enc, "rb"); if (fp == NULL) return NULL; magic = PyMarshal_ReadLongFromFile(fp); if (magic != pyc_magic) { if (Py_VerboseFlag) - PySys_WriteStderr("# %s has bad magic\n", cpathname); + PySys_WriteStderr("# %s has bad magic\n", + cpathname_def_enc); fclose(fp); return NULL; } pyc_mtime = PyMarshal_ReadLongFromFile(fp); if (pyc_mtime != mtime) { if (Py_VerboseFlag) - PySys_WriteStderr("# %s has bad mtime\n", cpathname); + PySys_WriteStderr("# %s has bad mtime\n", + cpathname_def_enc); fclose(fp); return NULL; } if (Py_VerboseFlag) - PySys_WriteStderr("# %s matches %s\n", cpathname, pathname); + PySys_WriteStderr("# %s matches %s\n", cpathname_def_enc, + pathname_def_enc); return fp; } -/* Read a code object from a file and check it for validity */ +/* Read a code object from a file and check it for validity + cpathname: (default encoding) + fp: compiled file open for reading at offset 0 +*/ static PyCodeObject * -read_compiled_module(char *cpathname, FILE *fp) +read_compiled_module(const char *cpathname_def_enc, FILE *fp) { PyObject *co; @@ -771,7 +941,7 @@ return NULL; if (!PyCode_Check(co)) { PyErr_Format(PyExc_ImportError, - "Non-code object in %.200s", cpathname); + "Non-code object in %.200s", cpathname_def_enc); Py_DECREF(co); return NULL; } @@ -780,14 +950,17 @@ /* Load a module from a compiled file, execute it, and return its - module object WITH INCREMENTED REFERENCE COUNT */ + module object WITH INCREMENTED REFERENCE COUNT + name: fully qualified module name (default encoding) + cpathname: filename of compiled file (default encoding) + fp: source file open for reading at offset 0 +*/ static PyObject * -load_compiled_module(char *name, char *cpathname, FILE *fp) +get_code_compiled_module(const char *name, const char *cpathname, FILE *fp) { long magic; PyCodeObject *co; - PyObject *m; magic = PyMarshal_ReadLongFromFile(fp); if (magic != pyc_magic) { @@ -802,13 +975,30 @@ if (Py_VerboseFlag) PySys_WriteStderr("import %s # precompiled from %s\n", name, cpathname); - m = PyImport_ExecCodeModuleEx(name, (PyObject *)co, cpathname); + return (PyObject *) co; +} + + +static PyObject * +load_compiled_module(const char *name, const char *cpathname, FILE *fp, + PyObject * loader) +{ + PyObject *co; + PyObject *m; + + co = get_code_compiled_module(name, cpathname, fp); + if (co == NULL) + return NULL; + m = PyImport_ExecCodeModuleEx(name, co, cpathname, loader); Py_DECREF(co); return m; } -/* Parse a source file and return the corresponding code object */ +/* Parse a source file and return the corresponding code object + pathname: (default encoding) + fp: source file open for reading +*/ static PyCodeObject * parse_source_module(const char *pathname, FILE *fp) @@ -830,10 +1020,12 @@ } -/* Helper to open a bytecode file for writing in exclusive mode */ +/* Helper to open a bytecode file for writing in exclusive mode + filename_fs_enc: (filesystem encoding) +*/ static FILE * -open_exclusive(char *filename) +open_exclusive(const char *filename_fs_enc) { #if defined(O_EXCL)&&defined(O_CREAT)&&defined(O_WRONLY)&&defined(O_TRUNC) /* Use O_EXCL to avoid a race condition when another process tries to @@ -843,8 +1035,8 @@ writable, the file will never be written. Oh well. */ int fd; - (void) unlink(filename); - fd = open(filename, O_EXCL|O_CREAT|O_WRONLY|O_TRUNC + (void) unlink(filename_fs_enc); + fd = open(filename_fs_enc, O_EXCL|O_CREAT|O_WRONLY|O_TRUNC #ifdef O_BINARY |O_BINARY /* necessary for Windows */ #endif @@ -859,7 +1051,7 @@ return fdopen(fd, "wb"); #else /* Best we can do -- on Windows this can't happen anyway */ - return fopen(filename, "wb"); + return fopen(filename_fs_enc, "wb"); #endif } @@ -867,18 +1059,23 @@ /* Write a compiled module to a file, placing the time of last modification of its source into the header. Errors are ignored, if a write error occurs an attempt is made to - remove the file. */ + remove the file. + cpathname_def_enc: (default encoding) + cpathname_fs_enc: (filesystem encoding) + mtime: Last modification time of corresponding source file +*/ static void -write_compiled_module(PyCodeObject *co, char *cpathname, time_t mtime) +write_compiled_module(PyCodeObject *co, const char *cpathname_def_enc, + const char * cpathname_fs_enc, time_t mtime) { FILE *fp; - fp = open_exclusive(cpathname); + fp = open_exclusive(cpathname_fs_enc); if (fp == NULL) { if (Py_VerboseFlag) PySys_WriteStderr( - "# can't create %s\n", cpathname); + "# can't create %s\n", cpathname_fs_enc); return; } PyMarshal_WriteLongToFile(pyc_magic, fp, Py_MARSHAL_VERSION); @@ -887,10 +1084,11 @@ PyMarshal_WriteObjectToFile((PyObject *)co, fp, Py_MARSHAL_VERSION); if (fflush(fp) != 0 || ferror(fp)) { if (Py_VerboseFlag) - PySys_WriteStderr("# can't write %s\n", cpathname); + PySys_WriteStderr("# can't write %s\n", + cpathname_fs_enc); /* Don't keep partial file */ fclose(fp); - (void) unlink(cpathname); + (void) unlink(cpathname_fs_enc); return; } /* Now write the true mtime */ @@ -900,30 +1098,48 @@ fflush(fp); fclose(fp); if (Py_VerboseFlag) - PySys_WriteStderr("# wrote %s\n", cpathname); + PySys_WriteStderr("# wrote %s\n", cpathname_fs_enc); } /* Load a source module from a given file and return its module object WITH INCREMENTED REFERENCE COUNT. If there's a matching - byte-compiled file, use that instead. */ + byte-compiled file, use that instead. + fullname: fully qualified module name (default encoding) + pathname_def_enc: source filename (default encoding) + fp: source file open for reading + buf: pointer to caller-allocated storage + buflen: allocated size of buf in bytes + p_final_pathname: (return value) filename actually used (default encoding) +*/ static PyObject * -load_source_module(char *name, char *pathname, FILE *fp) +get_code_source_module(const char *fullname, const char *pathname_def_enc, + FILE *fp, char *buf, Py_ssize_t buflen, + const char ** p_final_pathname) { time_t mtime; FILE *fpc; - char buf[MAXPATHLEN+1]; - char *cpathname; - PyCodeObject *co; - PyObject *m; + PyObject * pathname_fs_enc_bytes = NULL; + const char * pathname_fs_enc; + const char *cpathname_def_enc; + PyObject * cpathname_fs_enc_bytes = NULL; + const char * cpathname_fs_enc; + PyCodeObject *co = NULL; - mtime = PyOS_GetLastModificationTime(pathname, fp); + /* Convert to filesystem encoding */ + pathname_fs_enc_bytes = def_enc_to_fs_enc(pathname_def_enc, + &pathname_fs_enc); + if (pathname_fs_enc_bytes == NULL) { + goto cleanup; + } + + mtime = PyOS_GetLastModificationTime(pathname_fs_enc, fp); if (mtime == (time_t)(-1)) { PyErr_Format(PyExc_RuntimeError, "unable to get modification time from '%s'", - pathname); - return NULL; + pathname_def_enc); + goto cleanup; } #if SIZEOF_TIME_T > 4 /* Python's .pyc timestamp handling presumes that the timestamp fits @@ -933,43 +1149,77 @@ if (mtime >> 32) { PyErr_SetString(PyExc_OverflowError, "modification time overflows a 4 byte field"); - return NULL; + goto cleanup; } #endif - cpathname = make_compiled_pathname(pathname, buf, - (size_t)MAXPATHLEN + 1); - if (cpathname != NULL && - (fpc = check_compiled_module(pathname, mtime, cpathname))) { - co = read_compiled_module(cpathname, fpc); + cpathname_def_enc = make_compiled_pathname( + pathname_def_enc, buf, buflen); + /* Convert to filesystem encoding */ + if (cpathname_def_enc != NULL) { + cpathname_fs_enc_bytes = def_enc_to_fs_enc( + cpathname_def_enc, &cpathname_fs_enc); + if (cpathname_fs_enc_bytes == NULL) { + /* Don't use byte-compiled file */ + PyErr_Clear(); + cpathname_def_enc = NULL; + } + } + if (cpathname_def_enc != NULL && + (fpc = check_compiled_module(pathname_def_enc, mtime, + cpathname_def_enc, cpathname_fs_enc))){ + co = read_compiled_module(cpathname_def_enc, fpc); fclose(fpc); if (co == NULL) - return NULL; + goto cleanup; if (Py_VerboseFlag) PySys_WriteStderr("import %s # precompiled from %s\n", - name, cpathname); - pathname = cpathname; + fullname, cpathname_def_enc); + *p_final_pathname = cpathname_def_enc; } else { - co = parse_source_module(pathname, fp); + co = parse_source_module(pathname_def_enc, fp); if (co == NULL) - return NULL; + goto cleanup; if (Py_VerboseFlag) PySys_WriteStderr("import %s # from %s\n", - name, pathname); - if (cpathname) { + fullname, pathname_def_enc); + if (cpathname_def_enc) { PyObject *ro = PySys_GetObject("dont_write_bytecode"); if (ro == NULL || !PyObject_IsTrue(ro)) - write_compiled_module(co, cpathname, mtime); + write_compiled_module(co, cpathname_def_enc, + cpathname_fs_enc, mtime); } + *p_final_pathname = pathname_def_enc; } - m = PyImport_ExecCodeModuleEx(name, (PyObject *)co, pathname); + cleanup: + Py_XDECREF(pathname_fs_enc_bytes); + Py_XDECREF(cpathname_fs_enc_bytes); + return (PyObject *) co; +} + +static PyObject * +load_source_module(const char *name, const char *pathname, FILE *fp, + PyObject *loader) +{ + char buf[MAXPATHLEN+1]; + const char * final_pathname = pathname; /* default encoding */ + PyObject *co; + PyObject *m; + + co = get_code_source_module(name, pathname, fp, buf, sizeof(buf), + &final_pathname); + if (co == NULL) + return NULL; + m = PyImport_ExecCodeModuleEx(name, co, final_pathname, loader); Py_DECREF(co); return m; } -/* Get source file -> unicode or None - * Returns the path to the py file if available, else the given path +/* Given a compiled bytecode filename, return the corresponding source + filename. If input is not a bytecode filename, return it verbatim. + file: (default encoding) + Return value: PyUnicode object or Py_None */ static PyObject * get_sourcefile(const char *file) @@ -986,41 +1236,48 @@ len = strlen(file); /* match '*.py?' */ if (len > MAXPATHLEN || PyOS_strnicmp(&file[len-4], ".py", 3) != 0) { - return PyUnicode_DecodeFSDefault(file); + return PyUnicode_FromString(file); } strncpy(py, file, len-1); - py[len] = '\0'; - if (stat(py, &statbuf) == 0 && + py[len-1] = '\0'; + if (stat_def_enc(py, &statbuf) == 0 && S_ISREG(statbuf.st_mode)) { - u = PyUnicode_DecodeFSDefault(py); + u = PyUnicode_FromString(py); } else { - u = PyUnicode_DecodeFSDefault(file); + u = PyUnicode_FromString(file); } return u; } /* 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 *); +static PyObject *load_module(const char*, FILE *, const char*, int, PyObject*); +static int +find_module_in_directory(const char * dirname, const char * subname, + FILE ** p_fp, char * buf, Py_ssize_t buflen, + struct filedescr ** p_fdp); /* Load a package and return its module object WITH INCREMENTED - REFERENCE COUNT */ + REFERENCE COUNT + name: fully qualified module name (default encoding) + pathname: filename of package directory (default encoding) + loader: loader used to load package, or NULL +*/ static PyObject * -load_package(char *name, char *pathname) +load_package(const char *name, const char *pathname, PyObject *loader) { PyObject *m, *d; PyObject *file = NULL; PyObject *path = NULL; int err; - char buf[MAXPATHLEN+1]; + int rv; + char buf[MAXPATHLEN+1]; /* filename of __init__.py (default encoding) */ FILE *fp = NULL; struct filedescr *fdp; + assert(pathname); m = PyImport_AddModule(name); if (m == NULL) return NULL; @@ -1040,17 +1297,19 @@ if (err != 0) goto error; buf[0] = '\0'; - fdp = find_module(name, "__init__", path, buf, sizeof(buf), &fp, NULL); - if (fdp == NULL) { - if (PyErr_ExceptionMatches(PyExc_ImportError)) { - PyErr_Clear(); - Py_INCREF(m); - } - else - m = NULL; + rv = find_module_in_directory(pathname, "__init__", &fp, buf, + sizeof(buf), &fdp); + if (rv == -1) { + goto error; + } else if (rv == 0) { + if (Py_VerboseFlag) + PySys_WriteStderr( + "failed to find __init__ # directory %s\n", + pathname); + Py_INCREF(m); goto cleanup; } - m = load_module(name, fp, buf, fdp->type, NULL); + m = load_module(name, fp, buf, fdp->type, loader); if (fp != NULL) fclose(fp); goto cleanup; @@ -1064,10 +1323,14 @@ } -/* Helper to test for built-in module */ +/* Helper to test for built-in module + name: fully qualified module name (default encoding) + Return value: -1: found but has no initfunc, 0: not found, + 1: found and does have an initfunc +*/ static int -is_builtin(char *name) +is_builtin(const char *name) { int i; for (i = 0; PyImport_Inittab[i].name != NULL; i++) { @@ -1160,318 +1423,342 @@ return importer; } -/* 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. */ +/* Search the currently registered importers for a module. + fullname: fully qualified module name (default encoding) + subname: rightmost component of fullname (default encoding) + pathlist: list of directories to search, or NULL to search sys.path + A directory name of "" means current directory. + only_use_builtin: 1: emulate old "imp" module semantics, + 0: use PEP 302 semantics + Return value: a loader object, or NULL +*/ #ifdef MS_COREDLL extern FILE *PyWin_FindRegisteredModule(const char *, struct filedescr **, char *, Py_ssize_t); #endif -static int case_ok(char *, Py_ssize_t, Py_ssize_t, char *); -static int find_init_module(char *); /* Forward */ -static struct filedescr importhookdescr = {"", "", IMP_HOOK}; +static int case_ok(const char *, Py_ssize_t, Py_ssize_t, const char *); +static int find_init_module(const char *); /* Forward */ -static struct filedescr * -find_module(char *fullname, char *subname, PyObject *path, char *buf, - size_t buflen, FILE **p_fp, PyObject **p_loader) +static PyObject * +find_module(const char *fullname, const char *subname, PyObject *pathlist, + int only_use_builtin) { Py_ssize_t i, npath; - size_t len, namelen; - struct filedescr *fdp = NULL; - char *filemode; - FILE *fp = NULL; PyObject *path_hooks, *path_importer_cache; - struct stat statbuf; - 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 (p_loader != NULL) - *p_loader = NULL; + PyObject *meta_path; + const char * meta_path_name = "meta_path"; + const char * path_hooks_name = "path_hooks"; + const char * path_importer_cache_name = "path_importer_cache"; + if (only_use_builtin) { + meta_path_name = "builtin_meta_path"; + path_hooks_name = "builtin_path_hooks"; + path_importer_cache_name = "builtin_path_importer_cache"; + } + 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; - } - Py_INCREF(meta_path); /* zap guard */ - 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) { - Py_DECREF(meta_path); - return NULL; /* true error */ - } - if (loader != Py_None) { - /* a loader was found */ - *p_loader = loader; - Py_DECREF(meta_path); - return &importhookdescr; - } - Py_DECREF(loader); - } - Py_DECREF(meta_path); - } - - if (path != NULL && PyUnicode_Check(path)) { - /* The only type of submodule allowed inside a "frozen" - package are other frozen modules or packages. */ - char *p = PyUnicode_AsString(path); - if (strlen(p) + 1 + strlen(name) >= (size_t)buflen) { - PyErr_SetString(PyExc_ImportError, - "full frozen module name too long"); - return NULL; - } - strcpy(buf, p); - strcat(buf, "."); - strcat(buf, name); - strcpy(name, buf); - if (find_frozen(name) != NULL) { - strcpy(buf, name); - return &fd_frozen; - } + meta_path = PySys_GetObject(meta_path_name); + if (meta_path == NULL || !PyList_Check(meta_path)) { PyErr_Format(PyExc_ImportError, - "No frozen submodule named %.200s", name); + "sys.%s must be a list of " + "import hooks", meta_path_name); return NULL; } - if (path == NULL) { - if (is_builtin(name)) { - strcpy(buf, name); - return &fd_builtin; + Py_INCREF(meta_path); /* zap guard */ + npath = PyList_Size(meta_path); + for (i = 0; i < npath; i++) { + PyObject *loader; + PyObject *hook = PyList_GetItem(meta_path, i); + if (hook == NULL) { + Py_DECREF(meta_path); + return NULL; } - if ((find_frozen(name)) != NULL) { - strcpy(buf, name); - return &fd_frozen; + loader = PyObject_CallMethod(hook, "find_module", + "sO", fullname, + pathlist != NULL ? + pathlist : Py_None); + if (loader == NULL) { + Py_DECREF(meta_path); + return NULL; /* true error */ } - -#ifdef MS_COREDLL - fp = PyWin_FindRegisteredModule(name, &fdp, buf, buflen); - if (fp != NULL) { - *p_fp = fp; - return fdp; + if (loader != Py_None) { + /* a loader was found */ + Py_DECREF(meta_path); + return loader; } -#endif - path = PySys_GetObject("path"); + Py_DECREF(loader); } - if (path == NULL || !PyList_Check(path)) { + Py_DECREF(meta_path); + + if (pathlist == NULL) { + pathlist = PySys_GetObject("path"); + } + if (pathlist == NULL || !PyList_Check(pathlist)) { PyErr_SetString(PyExc_ImportError, "sys.path must be a list of directory names"); return NULL; } - path_hooks = PySys_GetObject("path_hooks"); + path_hooks = PySys_GetObject(path_hooks_name); if (path_hooks == NULL || !PyList_Check(path_hooks)) { - PyErr_SetString(PyExc_ImportError, - "sys.path_hooks must be a list of " - "import hooks"); + PyErr_Format(PyExc_ImportError, + "sys.%s must be a list of import hooks", + path_hooks_name); return NULL; } - path_importer_cache = PySys_GetObject("path_importer_cache"); + path_importer_cache = PySys_GetObject(path_importer_cache_name); if (path_importer_cache == NULL || !PyDict_Check(path_importer_cache)) { - PyErr_SetString(PyExc_ImportError, - "sys.path_importer_cache must be a dict"); + PyErr_Format(PyExc_ImportError, "sys.%s must be a dict", + path_importer_cache_name); return NULL; } - npath = PyList_Size(path); - namelen = strlen(name); + npath = PyList_Size(pathlist); for (i = 0; i < npath; i++) { - PyObject *v = PyList_GetItem(path, i); - PyObject *origv = v; - const char *base; - Py_ssize_t size; + PyObject *v = PyList_GetItem(pathlist, i); + PyObject *importer; + if (!v) return NULL; - if (PyUnicode_Check(v)) { - v = _PyUnicode_AsDefaultEncodedString(v, NULL); - if (v == NULL) - return NULL; - } - if (!PyString_Check(v)) + if (!PyUnicode_Check(v)) continue; - base = PyString_AS_STRING(v); - size = PyString_GET_SIZE(v); - len = size; - if (len + 2 + namelen + MAXSUFFIXSIZE >= buflen) { - continue; /* Too long */ - } - strcpy(buf, base); - if (strlen(buf) != len) { - continue; /* v contains '\0' */ - } /* sys.path_hooks import hook */ - if (p_loader != NULL) { - PyObject *importer; - - importer = get_path_importer(path_importer_cache, - path_hooks, origv); - if (importer == NULL) { - return NULL; + 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; + Py_INCREF(importer); + loader = PyObject_CallMethod(importer, + "find_module", + "s", fullname); + Py_DECREF(importer); + if (loader == NULL) { + return NULL; /* error */ } - /* 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); - continue; + if (loader != Py_None) { + return loader; } + Py_DECREF(loader); + continue; } - /* no hook was found, use builtin import */ + } - if (len > 0 && buf[len-1] != SEP + PyErr_Format(PyExc_ImportError, + "No module named %.200s", fullname); + return NULL; +} + +/* Concatenate path1, directory separator, and path2 and put the result in buf + buf: pointer to caller-allocated storage + (return value) filename of module (default encoding) + buflen: size of buf in bytes + path1: relative or absolute filename (default encoding) + path2: relative filename (default encoding) + Return value: size of filename in bytes, or -1 on error +*/ +static size_t +path_join(char * buf, size_t buflen, const char * path1, const char * path2) { + size_t path1len; + size_t path2len; + size_t curlen; + + path1len = strlen(path1); + path2len = strlen(path2); + if (path1len + 1 + path2len + 1 > buflen) { + return -1; + } + + strcpy(buf, path1); + curlen = path1len; + if (curlen > 0 && buf[curlen-1] != SEP #ifdef ALTSEP - && buf[len-1] != ALTSEP + && buf[curlen-1] != ALTSEP #endif - ) - buf[len++] = SEP; - strcpy(buf+len, name); - len += namelen; + ) + buf[curlen++] = SEP; - /* Check for package import (buf holds a directory name, - and there's an __init__ module in that directory */ + strcpy(buf + curlen, path2); + curlen += path2len; + return curlen; +} + +/* Find a module in the file system. + dirname: name of directory to search (default encoding) + subname: unqualified module name (default encoding) + p_fp: (return value) opened source/bytecode file + buf: pointer to caller-allocated storage + (return value) filename of module (default encoding) + buflen: allocated size of buf in bytes + p_fdp: (return value) type of module found + Return value: 1: found, 0: not found, -1: error +*/ +static int +find_module_in_directory(const char * dirname, const char * subname, + FILE ** p_fp, char * buf, Py_ssize_t buflen, + struct filedescr ** p_fdp) { + struct filedescr * fdp; + FILE * fp = NULL; + char *filemode; +#if defined(HAVE_STAT) + struct stat statbuf; +#endif + static struct filedescr fd_package = {"", "", PKG_DIRECTORY}; + int subname_len; +#if defined(PYOS_OS2) + size_t saved_rootname_len; + char *saved_buf = NULL; +#endif + int rv = -1; + int rootname_len; + PyObject * filename_fs_enc_bytes = NULL; + const char * filename_fs_enc; + + assert(dirname); + assert(subname); + assert(buf); + assert(buflen > sizeof(char *)); + + subname_len = strlen(subname); + rootname_len = path_join(buf, buflen, dirname, subname); + if ((rootname_len == -1) || + (rootname_len + MAXSUFFIXSIZE + 1 > buflen)) { + PyErr_Format(PyExc_ImportError, + "Module name too long: %.200s", subname); + rv = -1; + goto cleanup; + } + + /* Check for package import (buf holds a directory name, + and there's an __init__ module in that directory */ #ifdef HAVE_STAT - if (stat(buf, &statbuf) == 0 && /* it exists */ - S_ISDIR(statbuf.st_mode) && /* it's a directory */ - case_ok(buf, len, namelen, name)) { /* case matches */ - if (find_init_module(buf)) { /* and has __init__.py */ - return &fd_package; + if (stat_def_enc(buf, &statbuf) == 0 && /* it exists */ + S_ISDIR(statbuf.st_mode) && /* it's a directory */ + case_ok(buf, rootname_len, subname_len, subname)) { /*case matches*/ + if (find_init_module(buf)) { /* and has __init__.py */ + *p_fp = NULL; + *p_fdp = &fd_package; + rv = 1; + goto cleanup; + } else { + char warnstr[MAXPATHLEN+80]; + sprintf(warnstr, "Not importing directory " + "'%.*s': missing __init__.py", + MAXPATHLEN, buf); + if (PyErr_WarnEx(PyExc_ImportWarning, + warnstr, 1)) { + rv = -1; + goto cleanup; } - else { - char warnstr[MAXPATHLEN+80]; - sprintf(warnstr, "Not importing directory " - "'%.*s': missing __init__.py", - MAXPATHLEN, buf); - if (PyErr_WarnEx(PyExc_ImportWarning, - warnstr, 1)) { - return NULL; - } - } } + } #endif + #if defined(PYOS_OS2) - /* take a snapshot of the module spec for restoration - * after the 8 character DLL hackery - */ - saved_buf = strdup(buf); - saved_len = len; - saved_namelen = namelen; + /* take a snapshot of the module spec for restoration + * after the 8 character DLL hackery + */ + saved_buf = strdup(buf); + saved_rootname_len = rootname_len; #endif /* PYOS_OS2 */ - for (fdp = _PyImport_Filetab; fdp->suffix != NULL; fdp++) { + + /* Try all valid file extensions */ + for (fdp = _PyImport_Filetab; fdp->suffix != NULL; fdp++) { #if defined(PYOS_OS2) && defined(HAVE_DYNAMIC_LOADING) - /* OS/2 limits DLLs to 8 character names (w/o - extension) - * so if the name is longer than that and its a - * 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; - while (scan->suffix != NULL) { - if (!strcmp(scan->suffix, fdp->suffix)) - break; - else - scan++; - } - if (scan->suffix != NULL) { - /* yes, so truncate the name */ - namelen = 8; - len -= strlen(subname) - namelen; - buf[len] = '\0'; - } + /* OS/2 limits DLLs to 8 character names (w/o extension) + * so if the name is longer than that and its a + * 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; + while (scan->suffix != NULL) { + if (!strcmp(scan->suffix, fdp->suffix)) + break; + else + scan++; } + if (scan->suffix != NULL) { + /* yes, so truncate the name */ + rootname_len -= strlen(subname) - 8; + buf[rootname_len] = '\0'; + } + } #endif /* PYOS_OS2 */ - strcpy(buf+len, fdp->suffix); - if (Py_VerboseFlag > 1) - PySys_WriteStderr("# trying %s\n", buf); - filemode = fdp->mode; - if (filemode[0] == 'U') - filemode = "r" PY_STDIOTEXTMODE; - fp = fopen(buf, filemode); - if (fp != NULL) { - if (case_ok(buf, len, namelen, name)) - break; - else { /* continue search */ - fclose(fp); - fp = NULL; - } + + strcpy(buf+rootname_len, fdp->suffix); /*length checked above*/ + if (Py_VerboseFlag > 1) + PySys_WriteStderr("# trying %s\n", buf); + filemode = fdp->mode; + if (filemode[0] == 'U') + filemode = "r" PY_STDIOTEXTMODE; + + /* Convert to filesystem encoding */ + filename_fs_enc_bytes = def_enc_to_fs_enc(buf, + &filename_fs_enc); + if (filename_fs_enc_bytes == NULL) { + rv = -1; + goto cleanup; + } + + fp = fopen(filename_fs_enc, filemode); + if (fp != NULL) { + if (case_ok(buf, rootname_len, + subname_len, subname)) { + /* match */ + *p_fp = fp; + *p_fdp = fdp; + rv = 1; + goto cleanup; + } else { /* continue search */ + fclose(fp); + fp = NULL; } -#if defined(PYOS_OS2) - /* restore the saved snapshot */ - strcpy(buf, saved_buf); - len = saved_len; - namelen = saved_namelen; -#endif } + + Py_CLEAR(filename_fs_enc_bytes); + #if defined(PYOS_OS2) - /* don't need/want the module name snapshot anymore */ - if (saved_buf) - { - free(saved_buf); - saved_buf = NULL; - } + /* restore the saved snapshot */ + strcpy(buf, saved_buf); + rootname_len = saved_rootname_len; #endif - if (fp != NULL) - break; } - if (fp == NULL) { - PyErr_Format(PyExc_ImportError, - "No module named %.200s", name); - return NULL; - } - *p_fp = fp; - return fdp; -} -/* Helpers for main.c - * Find the source file corresponding to a named module - */ -struct filedescr * -_PyImport_FindModule(const char *name, PyObject *path, char *buf, - size_t buflen, FILE **p_fp, PyObject **p_loader) -{ - return find_module((char *) name, (char *) name, path, - buf, buflen, p_fp, p_loader); + /* not found */ + rv = 0; + *p_fp = NULL; + *p_fdp = NULL; + + cleanup: +#if defined(PYOS_OS2) + /* don't need/want the module name snapshot anymore */ + if (saved_buf) + { + free(saved_buf); + saved_buf = NULL; + } +#endif + Py_XDECREF(filename_fs_enc_bytes); + return rv; } -PyAPI_FUNC(int) _PyImport_IsScript(struct filedescr * fd) +PyAPI_FUNC(int) _PyImport_IsScript(const struct filedescr * fd) { return fd->type == PY_SOURCE || fd->type == PY_COMPILED; } @@ -1521,7 +1808,7 @@ #endif static int -case_ok(char *buf, Py_ssize_t len, Py_ssize_t namelen, char *name) +case_ok(const char *buf, Py_ssize_t len, Py_ssize_t namelen, const char *name) { /* Pick a platform-specific implementation; the sequence of #if's here should * match the sequence just above. @@ -1531,11 +1818,20 @@ #if defined(MS_WINDOWS) WIN32_FIND_DATA data; HANDLE h; + PyObject * fs_enc_bytes; + const char * fs_enc; + int rv; if (Py_GETENV("PYTHONCASEOK") != NULL) return 1; - h = FindFirstFile(buf, &data); + /* Convert buf to filesystem encoding */ + fs_enc_bytes = def_enc_to_fs_enc(buf, &fs_enc); + if (fs_enc_bytes == NULL) { + return 0; + } + h = FindFirstFile(fs_enc, &data); + Py_CLEAR(fs_enc_bytes); if (h == INVALID_HANDLE_VALUE) { PyErr_Format(PyExc_NameError, "Can't find file for module %.100s\n(filename %.300s)", @@ -1543,7 +1839,14 @@ return 0; } FindClose(h); - return strncmp(data.cFileName, name, namelen) == 0; + /* Convert name to filesystem encoding */ + fs_enc_bytes = def_enc_to_fs_enc(name, &fs_enc); + if (fs_enc_bytes == NULL) { + return 0; + } + rv = strncmp(data.cFileName, fs_enc, PyString_GET_SIZE(fs_enc_bytes)); + Py_DECREF(fs_enc_bytes); + return rv == 0; /* DJGPP */ #elif defined(DJGPP) @@ -1632,15 +1935,22 @@ #ifdef HAVE_STAT -/* Helper to look for __init__.py or __init__.py[co] in potential package */ +/* Helper to look for __init__.py or __init__.py[co] in potential package + dirname: package's filename (default encoding) +*/ static int -find_init_module(char *buf) +find_init_module(const char *dirname) { - const size_t save_len = strlen(buf); + char buf[MAXPATHLEN+1]; + const size_t save_len = strlen(dirname); size_t i = save_len; char *pname; /* pointer to start of __init__ */ struct stat statbuf; + if (save_len + 1 > sizeof(buf)) + return 0; /* too long */ + strcpy(buf, dirname); + /* For calling case_ok(buf, len, namelen, name): * /a/b/c/d/e/f/g/h/i/j/k/some_long_module_name.py\0 * ^ ^ ^ ^ @@ -1654,7 +1964,7 @@ buf[i++] = SEP; pname = buf + i; strcpy(pname, "__init__.py"); - if (stat(buf, &statbuf) == 0) { + if (stat_def_enc(buf, &statbuf) == 0) { if (case_ok(buf, save_len + 9, /* len("/__init__") */ 8, /* len("__init__") */ @@ -1665,7 +1975,7 @@ } i += strlen(pname); strcpy(buf+i, Py_OptimizeFlag ? "o" : "c"); - if (stat(buf, &statbuf) == 0) { + if (stat_def_enc(buf, &statbuf) == 0) { if (case_ok(buf, save_len + 9, /* len("/__init__") */ 8, /* len("__init__") */ @@ -1681,13 +1991,20 @@ #endif /* HAVE_STAT */ -static int init_builtin(char *); /* Forward */ +static int init_builtin(const char *, PyObject *); /* Forward */ /* Load an external module using the default search path and return - its module object WITH INCREMENTED REFERENCE COUNT */ + its module object WITH INCREMENTED REFERENCE COUNT + name: Fully qualified module name (default encoding) + fp: File open for reading or NULL + filename: module's __file__, or package's directory (default encoding) + type: As per struct filedescr.type + loader: this module's loader, or NULL +*/ static PyObject * -load_module(char *name, FILE *fp, char *buf, int type, PyObject *loader) +load_module(const char *name, FILE *fp, const char *filename, int type, + PyObject *loader) { PyObject *modules; PyObject *m; @@ -1708,31 +2025,31 @@ switch (type) { case PY_SOURCE: - m = load_source_module(name, buf, fp); + m = load_source_module(name, filename, fp, loader); break; case PY_COMPILED: - m = load_compiled_module(name, buf, fp); + m = load_compiled_module(name, filename, fp, loader); break; #ifdef HAVE_DYNAMIC_LOADING case C_EXTENSION: - m = _PyImport_LoadDynamicModule(name, buf, fp); + m = _PyImport_LoadDynamicModule(name, filename, fp, loader); break; #endif case PKG_DIRECTORY: - m = load_package(name, buf); + m = load_package(name, filename, loader); break; case C_BUILTIN: case PY_FROZEN: - if (buf != NULL && buf[0] != '\0') - name = buf; + if (filename != NULL && filename[0] != '\0') + name = filename; if (type == C_BUILTIN) - err = init_builtin(name); + err = init_builtin(name, loader); else - err = PyImport_ImportFrozenModule(name); + err = PyImport_ImportFrozenModule(name, loader); if (err < 0) return NULL; if (err == 0) { @@ -1778,24 +2095,48 @@ return m; } +/* Set a module's __loader__ + Return 1 for success, 0 if the module is not found, and -1 with + an exception set on error. + name: fully qualified module name (default encoding) + loader: this module's loader, or NULL + */ +static int +set_module_loader(const char *name, PyObject *loader) { + if (loader != NULL) { + PyObject *modules, *mod, *d; + modules = PyImport_GetModuleDict(); + mod = PyDict_GetItemString(modules, name); + if (mod == NULL) + return -1; + d = PyModule_GetDict(mod); + if (d == NULL) + return -1; + if (PyDict_SetItemString(d, "__loader__", loader) != 0) + return -1; + } + return 1; +} /* Initialize a built-in module. Return 1 for success, 0 if the module is not found, and -1 with - an exception set if the initialization failed. */ - + an exception set if the initialization failed. + name: fully qualified module name (default encoding) + loader: this module's loader, or NULL + */ static int -init_builtin(char *name) +init_builtin(const char *name, PyObject *loader) { struct _inittab *p; - if (_PyImport_FindExtension(name, name) != NULL) + if (_PyImport_FindExtension(name, name, loader) != NULL) return 1; for (p = PyImport_Inittab; p->name != NULL; p++) { if (strcmp(name, p->name) == 0) { if (p->initfunc == NULL) { PyErr_Format(PyExc_ImportError, - "Cannot re-init internal module %.200s", + "Cannot init internal module %.200s", name); return -1; } @@ -1806,6 +2147,8 @@ return -1; if (_PyImport_FixupExtension(name, name) == NULL) return -1; + if (set_module_loader(name, loader) != 1) + return -1; return 1; } } @@ -1815,8 +2158,12 @@ /* Frozen modules */ +/* Find a frozen module by name. + name: fully qualified module name (default encoding) + Return value: module object or NULL (no error set) if not found + */ static struct _frozen * -find_frozen(char *name) +find_frozen(const char *name) { struct _frozen *p; @@ -1829,8 +2176,9 @@ return p; } +/* name: fully qualified module name (default encoding) */ static PyObject * -get_frozen_object(char *name) +get_frozen_object(const char *name) { struct _frozen *p = find_frozen(name); int size; @@ -1856,10 +2204,13 @@ /* Initialize a frozen module. Return 1 for succes, 0 if the module is not found, and -1 with an exception set if the initialization failed. - This function is also used from frozenmain.c */ + This function is also used from frozenmain.c + name: fully qualified module name (default encoding) + loader: module's __loader__ object, or NULL +*/ int -PyImport_ImportFrozenModule(char *name) +PyImport_ImportFrozenModule(const char *name, PyObject *loader) { struct _frozen *p = find_frozen(name); PyObject *co; @@ -1907,7 +2258,7 @@ if (err != 0) goto err_return; } - m = PyImport_ExecCodeModuleEx(name, co, ""); + m = PyImport_ExecCodeModuleEx(name, co, "", loader); if (m == NULL) goto err_return; Py_DECREF(co); @@ -1920,7 +2271,9 @@ /* Import a module, either built-in, frozen, or external, and return - its module object WITH INCREMENTED REFERENCE COUNT */ + its module object WITH INCREMENTED REFERENCE COUNT + name: fully qualified module name (default encoding) +*/ PyObject * PyImport_ImportModule(const char *name) @@ -1942,7 +2295,7 @@ * never loaded before it loads it with PyImport_ImportModule() unless another * thread holds the import lock. In the latter case the function raises an * ImportError instead of blocking. - * + * name: fully qualified module name (default encoding) * Returns the module object with incremented ref count. */ PyObject * @@ -1987,16 +2340,35 @@ static PyObject *get_parent(PyObject *globals, char *buf, Py_ssize_t *p_buflen, int level); static PyObject *load_next(PyObject *mod, PyObject *altmod, - char **p_name, char *buf, Py_ssize_t *p_buflen); -static int mark_miss(char *name); + const char **p_name, char *buf, Py_ssize_t *p_buflen); +static int mark_miss(const char *name); static int ensure_fromlist(PyObject *mod, PyObject *fromlist, char *buf, Py_ssize_t buflen, int recursive); -static PyObject * import_submodule(PyObject *mod, char *name, char *fullname); +static PyObject * import_submodule(PyObject *mod, const char *name, + const char *fullname); -/* The Magnum Opus of dotted-name import :-) */ +/* The Magnum Opus of dotted-name import :-) + name: fully qualified module name (default encoding) + globals: current module's dict + locals: unused + fromlist: attributes/submodules to import + level: -1: absolute and relative, 0: absolute, +N: relative N levels + + Examples: + import spam -> (spam,,, None, 0) + import spam.eggs -> (spam.eggs,,, None, 0) + from spam import eggs -> (spam,,, ('eggs',), 0) + from spam import * -> (spam,,, ('*',), 0) + from . import eggs -> ('',,, ('eggs',), 1) + from .. import eggs -> ('',,, ('eggs',), 2) + from .spam import eggs -> (spam,,, ('eggs',), 1) + from ..spam.eggs import bacon -> (spam.eggs,,, ('bacon',) 2) + __import__('spam') -> (spam,,, None, -1) +*/ + static PyObject * -import_module_level(char *name, PyObject *globals, PyObject *locals, +import_module_level(const char *name, PyObject *globals, PyObject *locals, PyObject *fromlist, int level) { char buf[MAXPATHLEN+1]; @@ -2062,9 +2434,10 @@ return tail; } +/* name: fully qualified module name (default encoding) */ PyObject * -PyImport_ImportModuleLevel(char *name, PyObject *globals, PyObject *locals, - PyObject *fromlist, int level) +PyImport_ImportModuleLevel(const char *name, PyObject *globals, PyObject *locals, + PyObject *fromlist, int level) { PyObject *result; lock_import(); @@ -2083,8 +2456,8 @@ sys.modules entry for foo.bar. If globals is from a package's __init__.py, the package's entry in sys.modules is returned, as a borrowed reference. - The *name* of the returned package is returned in buf, with the length of - the name in *p_buflen. + The *fully qualified name* of the returned package is returned in + buf, with the length of the name in *p_buflen (default encoding). If globals doesn't come from a package or a module in a package, or a corresponding entry is not found in sys.modules, Py_None is returned. @@ -2135,6 +2508,7 @@ "Attempted relative import in non-package"); return NULL; } + /* level was -1, so fall back to absolute import */ return Py_None; } if (len > MAXPATHLEN) { @@ -2178,6 +2552,7 @@ return NULL; } if (lastdot == NULL) { + /* level was -1, so fall back to absolute import */ error = PyDict_SetItem(globals, pkgstr, Py_None); if (error) { PyErr_SetString(PyExc_ValueError, @@ -2207,6 +2582,7 @@ } } } + /* level may be -1 or >0 at this point */ while (--level > 0) { char *dot = strrchr(buf, '.'); if (dot == NULL) { @@ -2233,10 +2609,10 @@ /* altmod is either None or same as mod */ static PyObject * -load_next(PyObject *mod, PyObject *altmod, char **p_name, char *buf, +load_next(PyObject *mod, PyObject *altmod, const char **p_name, char *buf, Py_ssize_t *p_buflen) { - char *name = *p_name; + const char *name = *p_name; char *dot = strchr(name, '.'); size_t len; char *p; @@ -2304,8 +2680,9 @@ return result; } +/* name: fully qualified module name (default encoding) */ static int -mark_miss(char *name) +mark_miss(const char *name) { PyObject *modules = PyImport_GetModuleDict(); return PyDict_SetItemString(modules, name, Py_None); @@ -2355,23 +2732,10 @@ } hasit = PyObject_HasAttr(mod, item); if (!hasit) { - PyObject *item8; char *subname; PyObject *submod; char *p; - if (!Py_FileSystemDefaultEncoding) { - item8 = PyUnicode_EncodeASCII(PyUnicode_AsUnicode(item), - PyUnicode_GetSize(item), - NULL); - } else { - item8 = PyUnicode_AsEncodedString(item, - Py_FileSystemDefaultEncoding, NULL); - } - if (!item8) { - PyErr_SetString(PyExc_ValueError, "Cannot encode path item"); - return 0; - } - subname = PyString_AS_STRING(item8); + subname = PyUnicode_AsString(item); if (buflen + strlen(subname) >= MAXPATHLEN) { PyErr_SetString(PyExc_ValueError, "Module name too long"); @@ -2382,7 +2746,6 @@ *p++ = '.'; strcpy(p, subname); submod = import_submodule(mod, subname, buf); - Py_DECREF(item8); Py_XDECREF(submod); if (submod == NULL) { Py_DECREF(item); @@ -2396,8 +2759,8 @@ } static int -add_submodule(PyObject *mod, PyObject *submod, char *fullname, char *subname, - PyObject *modules) +add_submodule(PyObject *mod, PyObject *submod, const char *fullname, + const char *subname, PyObject *modules) { if (mod == Py_None) return 1; @@ -2430,7 +2793,7 @@ } static PyObject * -import_submodule(PyObject *mod, char *subname, char *fullname) +import_submodule(PyObject *mod, const char *subname, const char *fullname) { PyObject *modules = PyImport_GetModuleDict(); PyObject *m = NULL; @@ -2445,9 +2808,6 @@ } else { PyObject *path, *loader = NULL; - char buf[MAXPATHLEN+1]; - struct filedescr *fdp; - FILE *fp = NULL; if (mod == Py_None) path = NULL; @@ -2459,22 +2819,17 @@ return Py_None; } } - - buf[0] = '\0'; - fdp = find_module(fullname, subname, path, buf, MAXPATHLEN+1, - &fp, &loader); + loader = find_module(fullname, subname, path, 0); Py_XDECREF(path); - if (fdp == NULL) { + if (loader == NULL) { if (!PyErr_ExceptionMatches(PyExc_ImportError)) return NULL; PyErr_Clear(); Py_INCREF(Py_None); return Py_None; } - m = load_module(fullname, fp, buf, fdp->type, loader); + m = PyObject_CallMethod(loader, "load_module", "s", fullname); Py_XDECREF(loader); - if (fp) - fclose(fp); if (!add_submodule(mod, m, fullname, subname, modules)) { Py_XDECREF(m); m = NULL; @@ -2495,10 +2850,7 @@ PyObject *modules_reloading = interp->modules_reloading; PyObject *modules = PyImport_GetModuleDict(); PyObject *path = NULL, *loader = NULL, *existing_m = NULL; - char *name, *subname; - char buf[MAXPATHLEN+1]; - struct filedescr *fdp; - FILE *fp = NULL; + const char *name, *subname; PyObject *newm; if (modules_reloading == NULL) { @@ -2556,21 +2908,17 @@ if (path == NULL) PyErr_Clear(); } - buf[0] = '\0'; - fdp = find_module(name, subname, path, buf, MAXPATHLEN+1, &fp, &loader); + loader = find_module(name, subname, path, 0); Py_XDECREF(path); - if (fdp == NULL) { - Py_XDECREF(loader); + if (loader == NULL) { imp_modules_reloading_clear(); return NULL; } - newm = load_module(name, fp, buf, fdp->type, loader); + newm = PyObject_CallMethod(loader, "load_module", "s", name); Py_XDECREF(loader); - if (fp) - fclose(fp); if (newm == NULL) { /* load_module probably removed name from modules because of * the error. Put back the original module object. We're @@ -2706,24 +3054,42 @@ return list; } +static void +take_info_from_builtin_loader(PyObject *, FILE ** p_fp, char *, Py_ssize_t, + struct filedescr **); + +/* fullname: fully qualified module name (default encoding) + pathlist: sequence of directory names or Py_None or NULL +*/ static PyObject * -call_find_module(char *name, PyObject *path) +call_find_module(const char *fullname, PyObject *pathlist) { extern int fclose(FILE *); PyObject *fob, *ret; - struct filedescr *fdp; - char pathname[MAXPATHLEN+1]; + struct filedescr *fdp = NULL; + char filename[MAXPATHLEN+1]; /* default encoding */ FILE *fp = NULL; int fd = -1; char *found_encoding = NULL; char *encoding = NULL; + PyObject * loader = NULL; + const char * subname; - pathname[0] = '\0'; - if (path == Py_None) - path = NULL; - fdp = find_module(NULL, name, path, pathname, MAXPATHLEN+1, &fp, NULL); - if (fdp == NULL) + if (pathlist == Py_None) + pathlist = NULL; + subname = PyImport_GetSubname(fullname); + /* 1 => only use builtin loaders */ + loader = find_module(fullname, subname, pathlist, 1); + if (loader == NULL) return NULL; + take_info_from_builtin_loader(loader, &fp, filename, sizeof(filename), + &fdp); + Py_DECREF(loader); + if (fdp == NULL) { + PyErr_Format(PyExc_ImportError, + "No module named %.200s", fullname); + return NULL; + } if (fp != NULL) { fd = fileno(fp); if (fd != -1) @@ -2740,7 +3106,7 @@ encoding = (found_encoding != NULL) ? found_encoding : (char*)PyUnicode_GetDefaultEncoding(); } - fob = PyFile_FromFd(fd, pathname, fdp->mode, -1, + fob = PyFile_FromFd(fd, filename, fdp->mode, -1, (char*)encoding, NULL, NULL, 1); if (fob == NULL) { close(fd); @@ -2753,7 +3119,7 @@ Py_INCREF(fob); } ret = Py_BuildValue("Os(ssi)", - fob, pathname, fdp->suffix, fdp->mode, fdp->type); + fob, filename, fdp->suffix, fdp->mode, fdp->type); Py_DECREF(fob); PyMem_FREE(found_encoding); @@ -2763,22 +3129,22 @@ static PyObject * imp_find_module(PyObject *self, PyObject *args) { - char *name; - PyObject *path = NULL; - if (!PyArg_ParseTuple(args, "s|O:find_module", &name, &path)) + const char *name; + PyObject *pathlist = NULL; + if (!PyArg_ParseTuple(args, "s|O:find_module", &name, &pathlist)) return NULL; - return call_find_module(name, path); + return call_find_module(name, pathlist); } static PyObject * imp_init_builtin(PyObject *self, PyObject *args) { - char *name; + const char *name; int ret; PyObject *m; if (!PyArg_ParseTuple(args, "s:init_builtin", &name)) return NULL; - ret = init_builtin(name); + ret = init_builtin(name, NULL); if (ret < 0) return NULL; if (ret == 0) { @@ -2793,12 +3159,12 @@ static PyObject * imp_init_frozen(PyObject *self, PyObject *args) { - char *name; + const char *name; int ret; PyObject *m; if (!PyArg_ParseTuple(args, "s:init_frozen", &name)) return NULL; - ret = PyImport_ImportFrozenModule(name); + ret = PyImport_ImportFrozenModule(name, NULL); if (ret < 0) return NULL; if (ret == 0) { @@ -2813,7 +3179,7 @@ static PyObject * imp_get_frozen_object(PyObject *self, PyObject *args) { - char *name; + const char *name; if (!PyArg_ParseTuple(args, "s:get_frozen_object", &name)) return NULL; @@ -2823,7 +3189,7 @@ static PyObject * imp_is_builtin(PyObject *self, PyObject *args) { - char *name; + const char *name; if (!PyArg_ParseTuple(args, "s:is_builtin", &name)) return NULL; return PyLong_FromLong(is_builtin(name)); @@ -2832,7 +3198,7 @@ static PyObject * imp_is_frozen(PyObject *self, PyObject *args) { - char *name; + const char *name; struct _frozen *p; if (!PyArg_ParseTuple(args, "s:is_frozen", &name)) return NULL; @@ -2840,14 +3206,30 @@ return PyBool_FromLong((long) (p == NULL ? 0 : p->size)); } +/* Open a file in a certain mode + pathname: filename to open if fop=NULL (default encoding) + fob: open file descriptor, or NULL + mode: file mode to use for opening + Return value: open file or NULL +*/ static FILE * -get_file(char *pathname, PyObject *fob, char *mode) +get_file(const char *pathname, PyObject *fob, const char *mode) { FILE *fp; + assert(pathname || fob); if (mode[0] == 'U') mode = "r" PY_STDIOTEXTMODE; if (fob == NULL) { - fp = fopen(pathname, mode); + /* Convert to filesystem encoding */ + PyObject * pathname_fs_enc_bytes; + const char * pathname_fs_enc; + pathname_fs_enc_bytes = def_enc_to_fs_enc(pathname, + &pathname_fs_enc); + if (pathname_fs_enc_bytes == NULL) { + return NULL; + } + fp = fopen(pathname_fs_enc, mode); + Py_DECREF(pathname_fs_enc_bytes); } else { int fd = PyObject_AsFileDescriptor(fob); @@ -2865,8 +3247,8 @@ static PyObject * imp_load_compiled(PyObject *self, PyObject *args) { - char *name; - char *pathname; + const char *name; + const char *pathname; PyObject *fob = NULL; PyObject *m; FILE *fp; @@ -2876,7 +3258,7 @@ fp = get_file(pathname, fob, "rb"); if (fp == NULL) return NULL; - m = load_compiled_module(name, pathname, fp); + m = load_compiled_module(name, pathname, fp, NULL); if (fob == NULL) fclose(fp); return m; @@ -2887,8 +3269,8 @@ static PyObject * imp_load_dynamic(PyObject *self, PyObject *args) { - char *name; - char *pathname; + const char *name; + const char *pathname; PyObject *fob = NULL; PyObject *m; FILE *fp = NULL; @@ -2900,7 +3282,7 @@ if (fp == NULL) return NULL; } - m = _PyImport_LoadDynamicModule(name, pathname, fp); + m = _PyImport_LoadDynamicModule(name, pathname, fp, NULL); return m; } @@ -2909,8 +3291,8 @@ static PyObject * imp_load_source(PyObject *self, PyObject *args) { - char *name; - char *pathname; + const char *name; + const char *pathname; PyObject *fob = NULL; PyObject *m; FILE *fp; @@ -2920,7 +3302,7 @@ fp = get_file(pathname, fob, "r"); if (fp == NULL) return NULL; - m = load_source_module(name, pathname, fp); + m = load_source_module(name, pathname, fp, NULL); if (fob == NULL) fclose(fp); return m; @@ -2929,11 +3311,11 @@ static PyObject * imp_load_module(PyObject *self, PyObject *args) { - char *name; + const char *name; PyObject *fob; - char *pathname; - char *suffix; /* Unused */ - char *mode; + const char *pathname; + const char *suffix; /* Unused */ + const char *mode; int type; FILE *fp; @@ -2965,17 +3347,17 @@ static PyObject * imp_load_package(PyObject *self, PyObject *args) { - char *name; - char *pathname; + const char *name; + const char *pathname; if (!PyArg_ParseTuple(args, "ss:load_package", &name, &pathname)) return NULL; - return load_package(name, pathname); + return load_package(name, pathname, NULL); } static PyObject * imp_new_module(PyObject *self, PyObject *args) { - char *name; + const char *name; if (!PyArg_ParseTuple(args, "s:new_module", &name)) return NULL; return PyModule_New(name); @@ -3078,16 +3460,59 @@ return err; } +/* Call stat() on a sys.path or package __path__ entry, with + Windows-specific logic to handle the case where the directory name + has a trailing slash. We can't just remove the trailing slash when + creating Importer objects because that might change the semantics + (e.g. "C:\" vs "C:"). + + path: filename (default encoding) + statbuf: pointer to allocated structure + (return value) filled in by stat() call + Returns: as stat() +*/ +static int stat_path_entry(const char * path, struct stat * statbuf) { + int rv; + Py_ssize_t pathlen; + + pathlen = strlen(path); + + rv = stat_def_enc(path, statbuf); +#ifdef MS_WINDOWS + /* MS Windows stat() chokes on paths like C:\path\. Try to + * recover *one* time by stripping off a trailing slash or + * backslash. http://bugs.python.org/issue1293 + */ + if (rv != 0 && pathlen <= MAXPATHLEN && + (path[pathlen-1] == '/' || path[pathlen-1] == '\\')) { + char mangled[MAXPATHLEN+1]; + + strcpy(mangled, path); + mangled[pathlen-1] = '\0'; + + rv = stat_def_enc(mangled, statbuf); + } +#endif + return rv; +} + +/* NullImporter: Originally a performance optimization, now obsolete. */ + typedef struct { PyObject_HEAD } NullImporter; +/* Initialize NullImporter only if path argument refers to a path that + does not exist or is not a directory. Otherwise throw ImportError. + */ static int NullImporter_init(NullImporter *self, PyObject *args, PyObject *kwds) { - char *path; + const char *path; /* default encoding */ Py_ssize_t pathlen; - + struct stat statbuf; + int rv; + if (!_PyArg_NoKeywords("NullImporter()", kwds)) return -1; @@ -3099,34 +3524,17 @@ if (pathlen == 0) { PyErr_SetString(PyExc_ImportError, "empty pathname"); return -1; - } else { - struct stat statbuf; - int rv; + } - rv = stat(path, &statbuf); -#ifdef MS_WINDOWS - /* MS Windows stat() chokes on paths like C:\path\. Try to - * recover *one* time by stripping off a trailing slash or - * backslash. http://bugs.python.org/issue1293 - */ - if (rv != 0 && pathlen <= MAXPATHLEN && - (path[pathlen-1] == '/' || path[pathlen-1] == '\\')) { - char mangled[MAXPATHLEN+1]; - - strcpy(mangled, path); - mangled[pathlen-1] = '\0'; - rv = stat(mangled, &statbuf); + rv = stat_path_entry(path, &statbuf); + if (rv == 0) { + /* it exists */ + if (S_ISDIR(statbuf.st_mode)) { + /* it's a directory */ + PyErr_SetString(PyExc_ImportError, + "existing directory"); + return -1; } -#endif - if (rv == 0) { - /* it exists */ - if (S_ISDIR(statbuf.st_mode)) { - /* it's a directory */ - PyErr_SetString(PyExc_ImportError, - "existing directory"); - return -1; - } - } } return 0; } @@ -3186,7 +3594,955 @@ PyType_GenericNew /* tp_new */ }; +/* ModuleInfo: A helper object to hold information between find_module() + and load_module() to avoid redundant system calls. +*/ +typedef struct { + PyObject_HEAD + PyObject * fullname; + PyObject * filename; + FILE * fp; + struct filedescr * fdp; +} ModuleInfo; + +/* Allocate a new instance. + fullname: fully qualified module name + filename: module's filename for load_module +*/ +static PyObject * +ModuleInfo_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject * fullname; + PyObject * filename; + ModuleInfo *self; + + if (!PyArg_ParseTuple(args, "UU:ModuleInfo_new", &fullname, + &filename)) { + return NULL; + } + + self = (ModuleInfo *)type->tp_alloc(type, 0); + if (self != NULL) { + Py_INCREF(fullname); + self->fullname = fullname; + Py_INCREF(filename); + self->filename = filename; + self->fp = NULL; + self->fdp = NULL; + } + return (PyObject *)self; +} + +/* Deallocate an instance */ +static void +ModuleInfo_dealloc(ModuleInfo* self) +{ + Py_CLEAR(self->fullname); + Py_CLEAR(self->filename); + if (self->fp != NULL) { + fclose(self->fp); + self->fp = NULL; + } + self->fdp = NULL; + Py_TYPE(self)->tp_free((PyObject*)self); +} + +PyTypeObject PyModuleInfo_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "imp.ModuleInfo", /*tp_name*/ + sizeof(ModuleInfo), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)ModuleInfo_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "Helper for BuiltinImporter and DirectoryImporter", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + ModuleInfo_new /* tp_new */ +}; + +#define PyModuleInfo_CheckExact(op) (Py_TYPE(op) == &PyModuleInfo_Type) + +/* Create a new instance + fullname: fully qualified module name + filename: module's filename for load_module + fp: File object open for reading, or NULL + fdp: Module descriptor + Return value: newly created instance, or NULL. Caller owns the reference. +*/ +static PyObject * +create_module_info(PyObject * fullname, PyObject * filename, FILE * fp, + struct filedescr * fdp) +{ + ModuleInfo * module_info; + module_info = (ModuleInfo *) PyObject_CallFunctionObjArgs( + (PyObject *)&PyModuleInfo_Type, fullname, filename, NULL); + if (module_info != NULL) { + module_info->fp = fp; + module_info->fdp = fdp; + } + return (PyObject *) module_info; +} + +/* Get module information from a cached ModuleInfo instance if possible, + otherwise invoke callback. In either case, clear the cache to + ensure prompt file closing. + + cache: Pointer to cache, may be ModuleInfo instance or Py_None + fullname: fully qualified module name + pathname: List of path entries or other object or NULL + importer: Importer object to invoke callback upon + cb: Callback to invoke + Return value: ModuleInfo, or NULL if module not found or other error. +*/ + +typedef PyObject * require_module_callback(PyObject *, PyObject *, PyObject *); + +static ModuleInfo * +require_module(PyObject ** cache, PyObject * fullname, PyObject * pathname, + PyObject * importer, require_module_callback * cb) +{ + PyObject * info; + + /* Remove from cache */ + info = *cache; + Py_INCREF(Py_None); + *cache = Py_None; + + /* Module already looked up by find_module()? */ + if (PyModuleInfo_CheckExact(info)) { + ModuleInfo * module_info = (ModuleInfo *) info; + if (PyUnicode_Compare(fullname, module_info->fullname) == 0) { + /* Give cached reference to caller */ + return module_info; + } + } + + /* Discard previous info */ + Py_DECREF(info); + info = (*cb)(importer, fullname, pathname); + if (info == NULL) { + return NULL; /* misc error */ + } else if (info == Py_None) { + PyErr_Format(PyExc_ImportError, "No module named %.200s", + PyUnicode_AsString(fullname)); + Py_DECREF(info); + return NULL; + } else { + assert(PyModuleInfo_CheckExact(info)); + return (ModuleInfo *) info; + } +} + +/* BuiltinImporter: An importer object that encapsulates the standard + functionality of finding builtin and frozen modules. + + Each instance keeps a cache of the last open file object created by it's + find_module() method. The cache is consumed, and the FILE closed, by any + subsequent load_module() or get_X() method. The idea is that in the + standard case of find_module()+load_module(), load_module() will not repeat + the system calls of find_module() and FILE objects will be closed promptly. + */ + +typedef struct { + PyObject_HEAD + PyObject * cached_info; /* ModuleInfo instance or Py_None */ +} BuiltinImporter; + +/* Allocate new instance */ +static PyObject * +BuiltinImporter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + BuiltinImporter *self; + self = (BuiltinImporter *)type->tp_alloc(type, 0); + if (self != NULL) { + Py_INCREF(Py_None); + self->cached_info = Py_None; + } + return (PyObject *)self; +} + +/* Deallocate instance */ +static void +BuiltinImporter_dealloc(BuiltinImporter* self) +{ + Py_DECREF(self->cached_info); + Py_TYPE(self)->tp_free((PyObject*)self); +} + +/* Find a builtin or frozen module. + fullname: fully qualified module name + pathlist: List of path entries, single string object, or NULL + Return value: ModuleInfo, or Py_None if module not found, or NULL on error. +*/ +static PyObject * +BuiltinImporter_find_module_internal(BuiltinImporter * self, + PyObject * fullname, PyObject *pathlist) +{ + static struct filedescr fd_frozen = {"", "", PY_FROZEN}; + static struct filedescr fd_builtin = {"", "", C_BUILTIN}; + const char * fullname_def_enc; + PyObject * filename = NULL; + const char * subname; /* unqualified module name (default encoding) */ + PyObject * module_info = NULL; + + fullname_def_enc = PyUnicode_AsString(fullname); + if (fullname_def_enc == NULL) + return NULL; + + /* __main__ is a difficult case, since it's listed as a "builtin" but + this importer can't actually load it. */ + if (strcmp(fullname_def_enc, "__main__") == 0) { + Py_RETURN_NONE; + } + /* The only type of submodules allowed inside a "frozen" package are + other frozen modules or packages. A frozen package is + distinguished by having a __path__ attribute which is a string, + rather than a list. That string is the package name. */ + if ((pathlist != NULL) && PyUnicode_Check(pathlist)) { + subname = PyImport_GetSubname(fullname_def_enc); + filename = PyUnicode_FromFormat("%S.%s", pathlist, subname); + if (filename == NULL) + return NULL; + module_info = create_module_info(fullname, filename, NULL, + &fd_frozen); + Py_DECREF(filename); + } else if ((pathlist == NULL) || (pathlist == Py_None)) { + /* Look for builtin or frozen module */ + if (is_builtin(fullname_def_enc)) { + module_info = create_module_info(fullname, fullname, + NULL, &fd_builtin); + } else if (find_frozen(fullname_def_enc) != NULL) { + module_info = create_module_info(fullname, fullname, + NULL, &fd_frozen); + } else { +#ifdef MS_COREDLL + char buf[MAXPATHLEN+1]; + struct filedescr *fdp = NULL; + FILE *fp = NULL; + /* buf gets the module's filename (fs encoding) */ + fp = PyWin_FindRegisteredModule(fullname_def_enc, &fdp, + buf, sizeof(buf)); + if (fp != NULL) { + filename = PyUnicode_DecodeFSDefault(buf); + if (filename == NULL) + return NULL; + module_info = create_module_info( + fullname, filename, fp, fdp); + Py_DECREF(filename); + } +#endif + } + } + + if (module_info == NULL) + Py_RETURN_NONE; + return module_info; +} + +/* As PEP 302 importer.find_module() */ +static PyObject * +BuiltinImporter_find_module(BuiltinImporter *self, PyObject *args) +{ + PyObject * fullname; + PyObject * pathlist = NULL; + PyObject * module_info; + if (!PyArg_ParseTuple(args, "U|O:BuiltinImporter.find_module", + &fullname, &pathlist)) { + return NULL; + } + module_info = BuiltinImporter_find_module_internal(self, fullname, + pathlist); + + /* Save this information in the cache so that load_module doesn't have + to redo all the system calls */ + Py_XDECREF(self->cached_info); + if (module_info == NULL) { + Py_INCREF(Py_None); + self->cached_info = Py_None; + return NULL; /* propagate error */ + } else { + self->cached_info = module_info; + if (module_info == Py_None) { + Py_RETURN_NONE; /* module not found */ + } else { + Py_INCREF(self); + return (PyObject *) self; + } + } +} + +/* Get module information for named module. + fullname: fully qualified module name + pathname: List of path entries or other object or NULL + Return value: ModuleInfo, or NULL if module not found or other error. +*/ +static ModuleInfo * +BuiltinImporter_require_module(BuiltinImporter *self, PyObject * fullname) +{ + return require_module( + &self->cached_info, fullname, Py_None, (PyObject *) self, + (require_module_callback *) &BuiltinImporter_find_module_internal); +} + +/* As PEP 302 importer.load_module() */ +static PyObject * +BuiltinImporter_load_module(BuiltinImporter *self, PyObject *args) +{ + PyObject * fullname; + ModuleInfo * module_info; + PyObject * m; + + if (!PyArg_ParseTuple(args, "U:BuiltinImporter.load_module", + &fullname)) { + return NULL; + } + module_info = BuiltinImporter_require_module(self, fullname); + if (module_info == NULL) + return NULL; + m = load_module(PyUnicode_AsString(module_info->fullname), + module_info->fp, + PyUnicode_AsString(module_info->filename), + module_info->fdp->type, (PyObject *) self); + Py_DECREF(module_info); + return m; +} + +/* As PEP 302 importer.get_data() + This function always raises an IOError. +*/ +static PyObject * +BuiltinImporter_get_data(BuiltinImporter *self, PyObject *args) +{ + PyObject * filename; + if (!PyArg_ParseTuple(args, "U:BuiltinImporter.get_data", &filename)) { + return NULL; + } + PyErr_Format(PyExc_IOError, "BuiltinImporter: can not open file %s", + PyUnicode_AsString(filename)); + return NULL; +} + +/* As PEP 302 importer.is_package() */ +static PyObject * +BuiltinImporter_is_package(BuiltinImporter *self, PyObject *args) +{ + PyObject * fullname; + ModuleInfo * module_info; + + if (!PyArg_ParseTuple(args, "U:BuiltinImporter.is_package", &fullname)){ + return NULL; + } + module_info = BuiltinImporter_require_module(self, fullname); + if (module_info == NULL) + return NULL; + + if (module_info->fdp->type == PY_FROZEN) { + const char * fullname_def_enc; + struct _frozen *p; + int ispackage; + fullname_def_enc = PyUnicode_AsString(module_info->fullname); + p = find_frozen(fullname_def_enc); + if (p == NULL) { + PyErr_Format(PyExc_ImportError, "No module named %.200s", + fullname_def_enc); + Py_DECREF(module_info); + return NULL; + } + ispackage = (p->size < 0); + if (ispackage) { + Py_DECREF(module_info); + Py_RETURN_TRUE; + } + } + Py_DECREF(module_info); + Py_RETURN_FALSE; +} + +/* As PEP 302 importer.get_code() */ +static PyObject * +BuiltinImporter_get_code(BuiltinImporter *self, PyObject *args) +{ + PyObject * fullname; + ModuleInfo * module_info; + PyObject * co; + + if (!PyArg_ParseTuple(args, "U:BuiltinImporter.get_code", &fullname)) { + return NULL; + } + module_info = BuiltinImporter_require_module(self, fullname); + if (module_info == NULL) + return NULL; + + if (module_info->fdp->type == PY_FROZEN) { + co = get_frozen_object(PyUnicode_AsString(fullname)); + } else { + Py_INCREF(Py_None); + co = Py_None; + } + Py_DECREF(module_info); + return co; +} + +/* As PEP 302 importer.get_source() */ +static PyObject * +BuiltinImporter_get_source(BuiltinImporter *self, PyObject *args) +{ + PyObject * fullname; + ModuleInfo * module_info; + + if (!PyArg_ParseTuple(args, "U:BuiltinImporter.get_source", &fullname)){ + return NULL; + } + module_info = BuiltinImporter_require_module(self, fullname); + if (module_info == NULL) + return NULL; + Py_DECREF(module_info); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(doc_importer_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 importer\n\ +instance itself if the module was found, or None if it wasn't."); + +PyDoc_STRVAR(doc_importer_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 ImportError if it wasn't found."); + +PyDoc_STRVAR(doc_importer_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_importer_is_package, +"is_package(fullname) -> bool.\n\ +\n\ +Return True if the module specified by fullname is a package.\n\ +Raise ImportError is the module couldn't be found."); + +PyDoc_STRVAR(doc_importer_get_code, +"get_code(fullname) -> code object.\n\ +\n\ +Return the code object for the specified module. Raise ImportError\n\ +if the module couldn't be found."); + +PyDoc_STRVAR(doc_importer_get_source, +"get_source(fullname) -> source string.\n\ +\n\ +Return the source code for the specified module. Raise ImportError\n\ +if the module couldn't be found, return None if the source is\n\ +not available."); + +PyDoc_STRVAR(doc_importer_get_filename, +"get_filename(fullname) -> filename.\n\ +\n\ +Return the filename from which the specified module would be loaded.\n\ +Raise ImportError if the module couldn't be found, return None if\n\ +the module is not associated with a file."); + +static PyMethodDef BuiltinImporter_methods[] = { + {"find_module", (PyCFunction)BuiltinImporter_find_module, METH_VARARGS, + doc_importer_find_module, + }, + {"load_module", (PyCFunction)BuiltinImporter_load_module, METH_VARARGS, + doc_importer_load_module, + }, + {"get_data", (PyCFunction)BuiltinImporter_get_data, METH_VARARGS, + doc_importer_get_data, + }, + {"is_package", (PyCFunction)BuiltinImporter_is_package, METH_VARARGS, + doc_importer_is_package, + }, + {"get_code", (PyCFunction)BuiltinImporter_get_code, METH_VARARGS, + doc_importer_get_code, + }, + {"get_source", (PyCFunction)BuiltinImporter_get_source, METH_VARARGS, + doc_importer_get_source, + }, + {NULL} /* Sentinel */ +}; + + +PyTypeObject PyBuiltinImporter_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "imp.BuiltinImporter", /*tp_name*/ + sizeof(BuiltinImporter), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)BuiltinImporter_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "Importer for builtins and frozen modules", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + BuiltinImporter_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + BuiltinImporter_new /* tp_new */ +}; + +#define PyBuiltinImporter_CheckExact(op) (Py_TYPE(op) == &PyBuiltinImporter_Type) + +/* DirectoryImporter: An importer object that encapsulates the standard + functionality of loading modules from .py/.pyc/.pyo files in a + particular directory. + */ + +typedef struct { + PyObject_HEAD + PyObject * cached_info; /* ModuleInfo instance or Py_None */ + /* Directory name from sys.path or __path__. Empty string means + current working directory. */ + PyObject * dirname; +} DirectoryImporter; + +/* Allocate new instance */ +static PyObject * +DirectoryImporter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + DirectoryImporter *self; + self = (DirectoryImporter *)type->tp_alloc(type, 0); + if (self != NULL) { + Py_INCREF(Py_None); + self->cached_info = Py_None; + Py_INCREF(Py_None); + self->dirname = Py_None; + } + return (PyObject *)self; +} + +/* Deallocate instance */ +static void +DirectoryImporter_dealloc(DirectoryImporter* self) +{ + Py_CLEAR(self->cached_info); + Py_CLEAR(self->dirname); + Py_TYPE(self)->tp_free((PyObject*)self); +} + + +/* Initialize DirectoryImporter only if path argument refers to a path + that exists and is a directory. Otherwise throw ImportError. + dirname: Path entry to find and load modules from + */ +static int +DirectoryImporter_init(DirectoryImporter *self, PyObject *args, PyObject *kwds) +{ + PyObject * dirname; + const char * dirname_def_enc; + struct stat statbuf; + int rv; + PyObject * tmp; + + if (!PyArg_ParseTuple(args, "U:DirectoryImporter", &dirname)) + return -1; + + /* Empty string means current working directory, which we assume + exists without stat()ing it. */ + if (PyUnicode_GET_SIZE(dirname) > 0) { + dirname_def_enc = PyUnicode_AsString(dirname); + rv = stat_path_entry(dirname_def_enc, &statbuf); + if (rv != 0) { + PyErr_SetString(PyExc_ImportError, + "directory does not exist"); + return -1; + } else if (!S_ISDIR(statbuf.st_mode)) { + PyErr_SetString(PyExc_ImportError, + "not a directory"); + return -1; + } + } + + tmp = self->dirname; + Py_INCREF(dirname); + self->dirname = dirname; + Py_DECREF(tmp); + + return 0; +} + +/* Find a source file, compiled bytecode file, package directory, or dynamic + library. + fullname: fully qualified module name + pathlist: List of path entries, or NULL + Return value: ModuleInfo, or Py_None if module not found, or NULL on error. +*/ +static PyObject * +DirectoryImporter_find_module_internal(DirectoryImporter *self, PyObject * fullname, + PyObject * unused_pathlist) +{ + const char *fullname_def_enc; + const char *dirname_def_enc; /*directory to search (default encoding)*/ + const char *subname; /* unqualified module name (default encoding) */ + char buf[MAXPATHLEN+1]; /* module filename (default encoding) */ + struct filedescr * fdp; + FILE * fp; + PyObject * module_info = NULL; + int rv; + PyObject * filename; + + dirname_def_enc = PyUnicode_AsString(self->dirname); + if (dirname_def_enc == NULL) + return NULL; + fullname_def_enc = PyUnicode_AsString(fullname); + if (fullname_def_enc == NULL) + return NULL; + subname = PyImport_GetSubname(fullname_def_enc); + rv = find_module_in_directory(dirname_def_enc, subname, &fp, + buf, sizeof(buf), &fdp); + if (rv == -1) { + return NULL; + } else if (rv == 0) { + Py_RETURN_NONE; + } + + filename = PyUnicode_FromString(buf); + if (filename == NULL) + return NULL; + module_info = create_module_info(fullname, filename, fp, fdp); + Py_DECREF(filename); + + return module_info; +} + +/* As PEP 302 importer.find_module() */ +static PyObject * +DirectoryImporter_find_module(DirectoryImporter *self, PyObject *args) +{ + PyObject * fullname; + PyObject * pathlist = NULL; + PyObject * module_info; + + if (!PyArg_ParseTuple(args, "U|O:DirectoryImporter.find_module", + &fullname, &pathlist)) + return NULL; + + module_info = DirectoryImporter_find_module_internal(self, fullname, + pathlist); + + /* Save this information in the cache so that load_module doesn't have + to redo all the system calls */ + Py_DECREF(self->cached_info); + if (module_info == NULL) { + Py_INCREF(Py_None); + self->cached_info = Py_None; + return NULL; /* propagate error */ + } else { + self->cached_info = module_info; + if (module_info == Py_None) { + Py_RETURN_NONE; /* module not found */ + } else { + Py_INCREF(self); + return (PyObject *) self; + } + } +} + +/* Get module information for named module. + fullname: fully qualified module name + pathname: List of path entries or other object or NULL + Return value: ModuleInfo, or NULL if module not found or other error. +*/ +static ModuleInfo * +DirectoryImporter_require_module(DirectoryImporter *self, PyObject * fullname) +{ + return require_module( + &self->cached_info, fullname, Py_None, (PyObject *) self, + (require_module_callback *) &DirectoryImporter_find_module_internal); +} + +/* As PEP 302 importer.load_module() */ +static PyObject * +DirectoryImporter_load_module(DirectoryImporter *self, PyObject *args) +{ + PyObject * fullname; + ModuleInfo * module_info; + PyObject * m; + + if (!PyArg_ParseTuple(args, "U:DirectoryImporter.load_module", + &fullname)) { + return NULL; + } + module_info = DirectoryImporter_require_module(self, fullname); + if (module_info == NULL) + return NULL; + m = load_module(PyUnicode_AsString(module_info->fullname), + module_info->fp, + PyUnicode_AsString(module_info->filename), + module_info->fdp->type, (PyObject *) self); + Py_DECREF(module_info); + return m; +} + +/* As PEP 302 importer.is_package() */ +static PyObject * +DirectoryImporter_is_package(DirectoryImporter *self, PyObject *args) +{ + PyObject * fullname; + ModuleInfo * module_info; + int type; + + if (!PyArg_ParseTuple(args, "U:DirectoryImporter.is_package", + &fullname)) + return NULL; + + module_info = DirectoryImporter_require_module(self, fullname); + if (module_info == NULL) + return NULL; + + type = module_info->fdp->type; + Py_DECREF(module_info); + if (type == PKG_DIRECTORY) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + +/* As PEP 302 importer.get_code() */ +static PyObject * +DirectoryImporter_get_code(DirectoryImporter *self, PyObject *args) +{ + PyObject * fullname; + ModuleInfo * module_info; + PyObject * co; + int type; + FILE * fp; + + if (!PyArg_ParseTuple(args, "U:DirectoryImporter.get_code", &fullname)) + return NULL; + + module_info = DirectoryImporter_require_module(self, fullname); + if (module_info == NULL) + return NULL; + + type = module_info->fdp->type; + fp = module_info->fp; + if (type == PY_SOURCE) { + const char * filename; + char buf[MAXPATHLEN+1]; + const char * final_filename; + filename = PyUnicode_AsString(module_info->filename); + final_filename = filename; + co = get_code_source_module( + PyUnicode_AsString(module_info->fullname), + filename, fp, buf, sizeof(buf), &final_filename); + } else if (type == PY_COMPILED) { + co = get_code_compiled_module( + PyUnicode_AsString(module_info->fullname), + PyUnicode_AsString(module_info->filename), fp); + } else { + Py_INCREF(Py_None); + co = Py_None; + } + Py_DECREF(module_info); + return co; +} + +/* Return the filename associated with the named module */ +static PyObject * +DirectoryImporter_get_filename(DirectoryImporter *self, PyObject *args) +{ + PyObject * fullname; + ModuleInfo * module_info; + PyObject * filename; + + if (!PyArg_ParseTuple(args, "U:DirectoryImporter.get_filename", + &fullname)) + return NULL; + + module_info = DirectoryImporter_require_module(self, fullname); + if (module_info == NULL) + return NULL; + filename = module_info->filename; + Py_INCREF(filename); + Py_DECREF(module_info); + return filename; +} + +static PyMethodDef DirectoryImporter_methods[] = { + {"find_module", (PyCFunction)DirectoryImporter_find_module, METH_VARARGS, + doc_importer_find_module, + }, + {"load_module", (PyCFunction)DirectoryImporter_load_module, METH_VARARGS, + doc_importer_load_module, + }, + {"is_package", (PyCFunction)DirectoryImporter_is_package, METH_VARARGS, + doc_importer_is_package, + }, + {"get_code", (PyCFunction)DirectoryImporter_get_code, METH_VARARGS, + doc_importer_get_code, + }, + {"get_filename", (PyCFunction)DirectoryImporter_get_filename, METH_VARARGS, + doc_importer_get_filename, + }, + {NULL} /* Sentinel */ +}; + +PyTypeObject PyDirectoryImporter_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "imp.DirectoryImporter", /*tp_name*/ + sizeof(DirectoryImporter), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)DirectoryImporter_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "Standard file importer object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + DirectoryImporter_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)DirectoryImporter_init, /* tp_init */ + 0, /* tp_alloc */ + DirectoryImporter_new /* tp_new */ +}; + +#define PyDirectoryImporter_CheckExact(op) (Py_TYPE(op) == &PyDirectoryImporter_Type) + +/* Retrieve the open FILE from the loader object if loading from a standard + importer. This avoids doing redundant system calls in the normal case. + loader: a loader that has just been returned from importer.find_module() + p_fp: (return value) opened source/bytecode file + buf: pointer to caller-allocated storage + (return value) filename of module (default encoding) + buflen: allocated size of buf in bytes + p_fdp: (return value) type of module found + Return value: p_fp is NULL on error. +*/ +static void +take_info_from_builtin_loader(PyObject *loader, FILE ** p_fp, char * buf, + Py_ssize_t buflen, struct filedescr ** p_fdp) +{ + PyObject ** cache = NULL; + PyObject * cached_info = NULL; + + /* Take possession of cached entry */ + if (PyBuiltinImporter_CheckExact(loader)) { + cache = &((BuiltinImporter *) loader)->cached_info; + } else if (PyDirectoryImporter_CheckExact(loader)) { + cache = &((DirectoryImporter *) loader)->cached_info; + } + if (cache) { + cached_info = *cache; + Py_INCREF(Py_None); + *cache = Py_None; + } + + *p_fp = NULL; + *p_fdp = NULL; + buf[0] = '\0'; + + /* Take possession of file pointer and give it to caller */ + if ((cached_info != NULL) && PyModuleInfo_CheckExact(cached_info)) { + ModuleInfo * module_info = (ModuleInfo *) cached_info; + const char * filename_def_enc; + Py_ssize_t filename_len; + + filename_def_enc = PyUnicode_AsStringAndSize( + module_info->filename, &filename_len); + if (filename_def_enc == NULL) { + Py_DECREF(cached_info); + return; + } + if (filename_len + 1 > buflen) { + PyErr_SetString(PyExc_ValueError, + "Module name too long"); + Py_DECREF(cached_info); + return; + } + strcpy(buf, filename_def_enc); + + *p_fp = module_info->fp; + module_info->fp = NULL; + *p_fdp = module_info->fdp; + module_info->fdp = NULL; + + Py_DECREF(cached_info); + } +} + +/* Initialize this module */ + PyMODINIT_FUNC initimp(void) { @@ -3194,6 +4550,12 @@ if (PyType_Ready(&PyNullImporter_Type) < 0) goto failure; + if (PyType_Ready(&PyModuleInfo_Type) < 0) + goto failure; + if (PyType_Ready(&PyBuiltinImporter_Type) < 0) + goto failure; + if (PyType_Ready(&PyDirectoryImporter_Type) < 0) + goto failure; m = Py_InitModule4("imp", imp_methods, doc_imp, NULL, PYTHON_API_VERSION); @@ -3216,6 +4578,12 @@ Py_INCREF(&PyNullImporter_Type); PyModule_AddObject(m, "NullImporter", (PyObject *)&PyNullImporter_Type); + Py_INCREF(&PyBuiltinImporter_Type); + PyModule_AddObject(m, "BuiltinImporter", + (PyObject *)&PyBuiltinImporter_Type); + Py_INCREF(&PyDirectoryImporter_Type); + PyModule_AddObject(m, "DirectoryImporter", + (PyObject *)&PyDirectoryImporter_Type); failure: ; } Index: Python/importdl.c =================================================================== --- Python/importdl.c (revision 60883) +++ Python/importdl.c (working copy) @@ -19,14 +19,15 @@ PyObject * -_PyImport_LoadDynamicModule(char *name, char *pathname, FILE *fp) +_PyImport_LoadDynamicModule(const char *name, const char *pathname, FILE *fp, + PyObject *loader) { PyObject *m; PyObject *path; - char *lastdot, *shortname, *packagecontext, *oldcontext; + const char *lastdot, *shortname, *packagecontext, *oldcontext; dl_funcptr p; - if ((m = _PyImport_FindExtension(name, pathname)) != NULL) { + if ((m = _PyImport_FindExtension(name, pathname, loader)) != NULL) { Py_INCREF(m); return m; } @@ -67,6 +68,12 @@ if (PyModule_AddObject(m, "__file__", path) < 0) PyErr_Clear(); /* Not important enough to report */ + if (loader != NULL) { + Py_INCREF(loader); /* stolen by PyModule_AddObject */ + if (PyModule_AddObject(m, "__loader__", loader) < 0) + return NULL; + } + if (_PyImport_FixupExtension(name, pathname) == NULL) return NULL; if (Py_VerboseFlag) Index: Python/importdl.h =================================================================== --- Python/importdl.h (revision 60883) +++ Python/importdl.h (working copy) @@ -28,8 +28,9 @@ extern struct filedescr * _PyImport_Filetab; extern const struct filedescr _PyImport_DynLoadFiletab[]; -extern PyObject *_PyImport_LoadDynamicModule(char *name, char *pathname, - FILE *); +extern PyObject * +_PyImport_LoadDynamicModule(const char *name, const char *pathname, FILE *, + PyObject *); /* Max length of module suffix searched for -- accommodates "module.slb" */ #define MAXSUFFIXSIZE 12 Index: Python/getmtime.c =================================================================== --- Python/getmtime.c (revision 60883) +++ Python/getmtime.c (working copy) @@ -11,7 +11,7 @@ #endif time_t -PyOS_GetLastModificationTime(char *path, FILE *fp) +PyOS_GetLastModificationTime(const char *path, FILE *fp) { struct stat st; if (fstat(fileno(fp), &st) != 0) Index: Python/modsupport.c =================================================================== --- Python/modsupport.c (revision 60883) +++ Python/modsupport.c (working copy) @@ -9,7 +9,7 @@ static PyObject *va_build_value(const char *, va_list, int); /* Package context -- the full module name for package imports */ -char *_Py_PackageContext = NULL; +const char *_Py_PackageContext = NULL; /* Py_InitModule4() parameters: - name is the module name Index: Include/unicodeobject.h =================================================================== --- Include/import.h (revision 60883) +++ Include/import.h (working copy) @@ -8,14 +8,15 @@ #endif PyAPI_FUNC(long) PyImport_GetMagicNumber(void); -PyAPI_FUNC(PyObject *) PyImport_ExecCodeModule(char *name, PyObject *co); +PyAPI_FUNC(PyObject *) PyImport_ExecCodeModule( + const char *name, PyObject *co); PyAPI_FUNC(PyObject *) PyImport_ExecCodeModuleEx( - char *name, PyObject *co, char *pathname); + const char *name, PyObject *co, const char *pathname, PyObject *loader); PyAPI_FUNC(PyObject *) PyImport_GetModuleDict(void); PyAPI_FUNC(PyObject *) PyImport_AddModule(const char *name); PyAPI_FUNC(PyObject *) PyImport_ImportModule(const char *name); PyAPI_FUNC(PyObject *) PyImport_ImportModuleNoBlock(const char *); -PyAPI_FUNC(PyObject *) PyImport_ImportModuleLevel(char *name, +PyAPI_FUNC(PyObject *) PyImport_ImportModuleLevel(const char *name, PyObject *globals, PyObject *locals, PyObject *fromlist, int level); #define PyImport_ImportModuleEx(n, g, l, f) \ @@ -25,15 +26,15 @@ PyAPI_FUNC(PyObject *) PyImport_Import(PyObject *name); PyAPI_FUNC(PyObject *) PyImport_ReloadModule(PyObject *m); PyAPI_FUNC(void) PyImport_Cleanup(void); -PyAPI_FUNC(int) PyImport_ImportFrozenModule(char *); +PyAPI_FUNC(int) PyImport_ImportFrozenModule(const char *, PyObject *); -PyAPI_FUNC(struct filedescr *) _PyImport_FindModule( - const char *, PyObject *, char *, size_t, FILE **, PyObject **); -PyAPI_FUNC(int) _PyImport_IsScript(struct filedescr *); +struct filedescr; +PyAPI_FUNC(int) _PyImport_IsScript(const struct filedescr *); PyAPI_FUNC(void) _PyImport_ReInitLock(void); -PyAPI_FUNC(PyObject *)_PyImport_FindExtension(char *, char *); -PyAPI_FUNC(PyObject *)_PyImport_FixupExtension(char *, char *); +PyAPI_FUNC(PyObject *)_PyImport_FindExtension(const char *, const char *, + PyObject *); +PyAPI_FUNC(PyObject *)_PyImport_FixupExtension(const char *, const char *); struct _inittab { char *name; @@ -41,15 +42,18 @@ }; PyAPI_DATA(PyTypeObject) PyNullImporter_Type; +PyAPI_DATA(PyTypeObject) PyModuleInfo_Type; +PyAPI_DATA(PyTypeObject) PyBuiltinImporter_Type; +PyAPI_DATA(PyTypeObject) PyDirectoryImporter_Type; PyAPI_DATA(struct _inittab *) PyImport_Inittab; PyAPI_FUNC(int) PyImport_AppendInittab(char *name, void (*initfunc)(void)); PyAPI_FUNC(int) PyImport_ExtendInittab(struct _inittab *newtab); struct _frozen { - char *name; - unsigned char *code; - int size; + char *name; /* Fully qualified module name (default encoding) */ + unsigned char *code; /* Raw bytecode */ + int size; /* Length of code in bytes */ }; /* Embedding apps may change this pointer to point to their favorite Index: Include/modsupport.h =================================================================== --- Include/modsupport.h (revision 60883) +++ Include/modsupport.h (working copy) @@ -126,7 +126,7 @@ Py_InitModule4(name, methods, doc, (PyObject *)NULL, \ PYTHON_API_VERSION) -PyAPI_DATA(char *) _Py_PackageContext; +PyAPI_DATA(const char *) _Py_PackageContext; #ifdef __cplusplus } Index: Doc/extending/newtypes.rst =================================================================== --- Doc/c-api/import.rst (revision 60883) +++ Doc/c-api/import.rst (working copy) @@ -39,7 +39,7 @@ :exc:`ImportError`. -.. cfunction:: PyObject* PyImport_ImportModuleEx(char *name, PyObject *globals, PyObject *locals, PyObject *fromlist) +.. cfunction:: PyObject* PyImport_ImportModuleEx(const char *name, PyObject *globals, PyObject *locals, PyObject *fromlist) .. index:: builtin: __import__ @@ -57,7 +57,7 @@ :cfunc:`PyImport_ImportModule`. -.. cfunction:: PyObject* PyImport_ImportModuleLevel(char *name, PyObject *globals, PyObject *locals, PyObject *fromlist, int level) +.. cfunction:: PyObject* PyImport_ImportModuleLevel(const char *name, PyObject *globals, PyObject *locals, PyObject *fromlist, int level) Import a module. This is best described by referring to the built-in Python function :func:`__import__`, as the standard :func:`__import__` function calls @@ -99,7 +99,7 @@ dotted name for *name* are not created if not already present. -.. cfunction:: PyObject* PyImport_ExecCodeModule(char *name, PyObject *co) +.. cfunction:: PyObject* PyImport_ExecCodeModule(const char *name, PyObject *co) .. index:: builtin: compile @@ -149,23 +149,24 @@ Finalize the import mechanism. For internal use only. -.. cfunction:: PyObject* _PyImport_FindExtension(char *, char *) +.. cfunction:: PyObject* _PyImport_FindExtension(const char *, const char *, PyObject *) For internal use only. -.. cfunction:: PyObject* _PyImport_FixupExtension(char *, char *) +.. cfunction:: PyObject* _PyImport_FixupExtension(const char *, const char *) For internal use only. -.. cfunction:: int PyImport_ImportFrozenModule(char *name) +.. cfunction:: int PyImport_ImportFrozenModule(char *name, PyObject *loader) Load a frozen module named *name*. Return ``1`` for success, ``0`` if the module is not found, and ``-1`` with an exception set if the initialization failed. To access the imported module on a successful load, use :cfunc:`PyImport_ImportModule`. (Note the misnomer --- this function would - reload the module if it was already imported.) + reload the module if it was already imported.) If *loader* is not *NULL*, + set the module's ``__loader__`` to *loader*. .. ctype:: struct _frozen Index: Doc/c-api/sys.rst =================================================================== --- Doc/c-api/sys.rst (revision 60883) +++ Doc/c-api/sys.rst (working copy) @@ -15,7 +15,7 @@ one of the strings ``''`` or ``'???'``. -.. cfunction:: long PyOS_GetLastModificationTime(char *filename) +.. cfunction:: long PyOS_GetLastModificationTime(const char *filename) Return the time of last modification of the file *filename*. The result is encoded in the same way as the timestamp returned by the standard C library Index: Doc/library/imp.rst =================================================================== --- Doc/library/imp.rst (revision 60883) +++ Doc/library/imp.rst (working copy) @@ -53,8 +53,8 @@ contained in the list returned by :func:`get_suffixes` describing the kind of module found. - If the module does not live in a file, the returned *file* is ``None``, - *pathname* is the empty string, and the *description* tuple contains empty + If the module is a builtin or frozen module, the returned *file* is ``None``, + *pathname* is the module name, and the *description* tuple contains empty strings for its suffix and mode; the module type is indicated as given in parentheses above. If the search is unsuccessful, :exc:`ImportError` is raised. Other exceptions indicate problems with the arguments or @@ -84,7 +84,8 @@ what kind of module must be loaded. If the load is successful, the return value is the module object; otherwise, - an exception (usually :exc:`ImportError`) is raised. + an exception (usually :exc:`ImportError`) is raised. The module will not + have a ``__loader__`` attribute. **Important:** the caller is responsible for closing the *file* argument, if it was not ``None``, even when an exception is raised. This is best done @@ -254,7 +255,7 @@ will be initialized *again*. Re-initialization involves the copying of the built-in module's ``__dict__`` from the cached module over the module's entry in ``sys.modules``. If there is no built-in module called *name*, ``None`` is - returned. + returned. The module will not have a ``__loader__`` attribute. .. function:: init_frozen(name) @@ -264,7 +265,8 @@ is no frozen module called *name*, ``None`` is returned. (Frozen modules are modules written in Python whose compiled byte-code object is incorporated into a custom-built Python interpreter by Python's :program:`freeze` - utility. See :file:`Tools/freeze/` for now.) + utility. See :file:`Tools/freeze/` for now.) The module will not have a + ``__loader__`` attribute. .. function:: is_builtin(name) @@ -291,9 +293,8 @@ object. The *pathname* argument points to the byte-compiled code file. The *file* argument is the byte-compiled code file, open for reading in binary mode, from the beginning. It must currently be a real file object, not a user-defined - class emulating a file. + class emulating a file. The module will not have a ``__loader__`` attribute. - .. function:: load_dynamic(name, pathname[, file]) Load and initialize a module implemented as a dynamically loadable shared @@ -305,9 +306,8 @@ initialization function: an external C function called ``initname()`` in the shared library is called. The optional *file* argument is ignored. (Note: using shared libraries is highly system dependent, and not all systems support - it.) + it.) The module will not have a ``__loader__`` attribute. - .. function:: load_source(name, pathname[, file]) Load and initialize a module implemented as a Python source file and return its @@ -317,9 +317,9 @@ source file, open for reading as text, from the beginning. It must currently be a real file object, not a user-defined class emulating a file. Note that if a properly matching byte-compiled file (with suffix :file:`.pyc` or :file:`.pyo`) - exists, it will be used instead of parsing the given source file. + exists, it will be used instead of parsing the given source file. The module + will not have a ``__loader__`` attribute. - .. class:: NullImporter(path_string) The :class:`NullImporter` type is a :pep:`302` import hook that handles @@ -337,7 +337,25 @@ This method always returns ``None``, indicating that the requested module could not be found. +.. class:: BuiltinImporter(path_string) + The :class:`BuiltinImporter` type is a :pep:`302` import hook that handles + builtin and frozen modules. The path string is ignored. + + Python adds an instance of this type to ``sys.meta_path`` and + ``sys.builtin_meta_path``. + +.. class:: DirectoryImporter(path_string) + + The :class:`DirectoryImporter` type is a :pep:`302` import hook + that handles source files, compiled byte-code files, package + directories, and C extension modules found in a single, particular + directory (*path_string*). Calling this type with a path that + doesn't exist or isn't a directory raises :exc:`ImportError`. + Otherwise, a :class:`DirectoryImporter` instance is returned. + + Python adds this type to ``sys.path_hooks`` and ``sys.builtin_path_hooks``. + .. _examples-imp: Examples Index: Doc/data/refcounts.dat =================================================================== --- Doc/data/refcounts.dat (revision 60883) +++ Doc/data/refcounts.dat (working copy) @@ -463,6 +463,7 @@ PyImport_ImportFrozenModule:int::: PyImport_ImportFrozenModule:char*::: +PyImport_ImportFrozenModule:PyObject*::+1: PyImport_ImportModule:PyObject*::+1: PyImport_ImportModule:char*:name:: @@ -842,7 +843,7 @@ PyNumber_Xor:PyObject*:o2:0: PyOS_GetLastModificationTime:long::: -PyOS_GetLastModificationTime:char*:filename:: +PyOS_GetLastModificationTime:const char*:filename:: PyObject_AsFileDescriptor:int::: PyObject_AsFileDescriptor:PyObject*:o:0: Index: Lib/site.py =================================================================== --- Lib/test/test_importhooks.py (revision 60883) +++ Lib/test/test_importhooks.py (working copy) @@ -3,8 +3,8 @@ import os import unittest from test import test_support +import __future__ # Implicitly used below - test_src = """\ def get_name(): return __name__ @@ -60,7 +60,7 @@ def __init__(self, path=test_path): if path != test_path: - # if out class is on sys.path_hooks, we must raise + # if our class is on sys.path_hooks, we must raise # ImportError for any path item that we can't handle. raise ImportError self.path = path @@ -227,7 +227,7 @@ self.doTestImports(i) def testPathHook(self): - sys.path_hooks.append(PathImporter) + sys.path_hooks.insert(0, PathImporter) sys.path.append(test_path) self.doTestImports() @@ -245,8 +245,8 @@ def testImpWrapper(self): i = ImpWrapper() - sys.meta_path.append(i) - sys.path_hooks.append(ImpWrapper) + sys.meta_path.insert(0, i) + sys.path_hooks.insert(0, ImpWrapper) mnames = ("colorsys", "urlparse", "distutils.core") for mname in mnames: parent = mname.split(".")[0] @@ -255,7 +255,8 @@ del sys.modules[n] for mname in mnames: m = __import__(mname, globals(), locals(), ["__dummy__"]) - m.__loader__ # to make sure we actually handled the import + # make sure we actually handled the import + self.assertEqual(type(m.__loader__), ImpLoader) # Delete urllib from modules because urlparse was imported above. # Without this hack, test_socket_ssl fails if run in this order: # regrtest.py test_codecmaps_tw test_importhooks test_socket_ssl Index: Lib/test/test_frozen.py =================================================================== --- Lib/test/test_frozen.py (revision 60883) +++ Lib/test/test_frozen.py (working copy) @@ -12,7 +12,10 @@ except ImportError as x: self.fail("import __hello__ failed:" + str(x)) self.assertEqual(__hello__.initialized, True) - self.assertEqual(len(dir(__hello__)), 6, dir(__hello__)) + self.assertEqual( + sorted(dir(__hello__)), + ['__builtins__', '__doc__', '__file__', '__loader__', + '__name__', '__package__', 'initialized']) try: import __phello__ @@ -20,18 +23,30 @@ self.fail("import __phello__ failed:" + str(x)) self.assertEqual(__phello__.initialized, True) if not "__phello__.spam" in sys.modules: - self.assertEqual(len(dir(__phello__)), 7, dir(__phello__)) + self.assertEqual( + sorted(dir(__phello__)), + ['__builtins__', '__doc__', '__file__', '__loader__', + '__name__', '__package__', '__path__', 'initialized']) else: - self.assertEqual(len(dir(__phello__)), 8, dir(__phello__)) - + self.assertEqual( + sorted(dir(__phello__)), + ['__builtins__', '__doc__', '__file__', '__loader__', + '__name__', '__package__', '__path__', 'initialized', 'spam']) + try: import __phello__.spam except ImportError as x: self.fail("import __phello__.spam failed:" + str(x)) self.assertEqual(__phello__.spam.initialized, True) - self.assertEqual(len(dir(__phello__.spam)), 6) - self.assertEqual(len(dir(__phello__)), 8) - + self.assertEqual( + sorted(dir(__phello__.spam)), + ['__builtins__', '__doc__', '__file__', '__loader__', + '__name__', '__package__', 'initialized']) + self.assertEqual( + sorted(dir(__phello__)), + ['__builtins__', '__doc__', '__file__', '__loader__', + '__name__', '__package__', '__path__', 'initialized', 'spam']) + try: import __phello__.foo except ImportError: Index: Lib/test/test_pkg.py =================================================================== --- Lib/test/test_pkg.py (revision 60883) +++ Lib/test/test_pkg.py (working copy) @@ -191,14 +191,14 @@ import t5 self.assertEqual(fixdir(dir(t5)), - ['__doc__', '__file__', '__name__', + ['__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', 'foo', 'string', 't5']) self.assertEqual(fixdir(dir(t5.foo)), - ['__doc__', '__file__', '__name__', '__package__', - 'string']) + ['__doc__', '__file__', '__loader__', '__name__', + '__package__', 'string']) self.assertEqual(fixdir(dir(t5.string)), - ['__doc__', '__file__', '__name__','__package__', - 'spam']) + ['__doc__', '__file__', '__loader__', '__name__', + '__package__', 'spam']) def test_6(self): hier = [ @@ -213,13 +213,13 @@ import t6 self.assertEqual(fixdir(dir(t6)), - ['__all__', '__doc__', '__file__', + ['__all__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__']) s = """ import t6 from t6 import * self.assertEqual(fixdir(dir(t6)), - ['__all__', '__doc__', '__file__', + ['__all__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', 'eggs', 'ham', 'spam']) self.assertEqual(dir(), ['eggs', 'ham', 'self', 'spam', 't6']) @@ -247,18 +247,18 @@ t7, sub, subsub = None, None, None import t7 as tas self.assertEqual(fixdir(dir(tas)), - ['__doc__', '__file__', '__name__', + ['__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__']) self.failIf(t7) from t7 import sub as subpar self.assertEqual(fixdir(dir(subpar)), - ['__doc__', '__file__', '__name__', + ['__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__']) self.failIf(t7) self.failIf(sub) from t7.sub import subsub as subsubsub self.assertEqual(fixdir(dir(subsubsub)), - ['__doc__', '__file__', '__name__', + ['__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', 'spam']) self.failIf(t7) self.failIf(sub) Index: Lib/test/test_DirectoryImporter.py =================================================================== --- Lib/test/test_DirectoryImporter.py (revision 0) +++ Lib/test/test_DirectoryImporter.py (revision 0) @@ -0,0 +1,325 @@ +"""Unit tests for imp.DirectoryImporter""" + +import unittest +import imp +import os +import types +import sys +import test.test_support + +# Assumptions: +# string source/byte-compiled module +# email: package directory +# email.encoders source/byte-compiled module +# array dynamic module (may not be present) +# _types: builtin module + +def del_loader(modname): + """Remove __loader__ attribute from named module if any""" + m = sys.modules.get(modname) + if m and hasattr(m, '__loader__'): + delattr(m, '__loader__') + +def find_module(modname, modtype): + """Find a module manually. + + Return value: (directory, filename, descriptor) or (None, None) + """ + for dirname in sys.path: + for descr in imp.get_suffixes() + [("", "", imp.PKG_DIRECTORY)]: + if modtype == descr[2]: + filename = os.path.join(dirname, modname + descr[0]) + if os.path.exists(filename): + return dirname, filename, descr + return (None, None, None) + +def module_setUp(): + """Decide whether to run dynamic module loading tests or not.""" + + # Find directories and filenames heuristically + (TestCase.string_dirname, TestCase.string_source_filename, + TestCase.py_source_descr) = ( + find_module('string', imp.PY_SOURCE)) + (d, TestCase.string_compiled_filename, TestCase.py_compiled_descr) = ( + find_module('string', imp.PY_COMPILED)) + assert(d == TestCase.string_dirname, d) + (TestCase.email_dirname, TestCase.email_dir_filename, + TestCase.pkg_directory_descr) = ( + find_module('email', imp.PKG_DIRECTORY)) + TestCase.encoders_dirname = os.path.join(TestCase.email_dirname, "email") + (TestCase.dynamic_dirname, TestCase.dynamic_module_filename, + TestCase.dynamic_module_descr) = ( + find_module('array', imp.C_EXTENSION)) + + if (not TestCase.string_dirname or + not TestCase.email_dirname or + not TestCase.encoders_dirname): + raise test.test_support.TestFailed("Can't find source or bytecode files") + if TestCase.dynamic_module_filename: + TestCase.dynamic_tests_enabled = True + else: + if test.test_support.verbose: + print("Skipping dynamic module import tests", file=sys.stderr) + +def clean_modules(): + test.test_support.unload('string') + test.test_support.unload('email') + test.test_support.unload('email.encoders') + test.test_support.unload('array') + test.test_support.unload('_types') + +def module_tearDown(): + clean_modules() + __import__('string') + __import__('email') + __import__('email.encoders') + __import__('array') + __import__('_types') + +class TestCase(unittest.TestCase): + + # Constants initialized by module_setUp() + string_dirname = None + string_source_filename = None + py_source_descr = None + string_compiled_filename = None + py_compiled_descr = None + email_dirname = None # Parent directory of "email" directory + email_dir_filename = None + encoders_dirname = None + pkg_directory_descr = None + dynamic_tests_enabled = False + dynamic_dirname = None + dynamic_module_filename = None + dynamic_module_descr = None + + def setUp(self): + clean_modules() + self.string_imp = imp.DirectoryImporter(TestCase.string_dirname) + self.email_imp = imp.DirectoryImporter(TestCase.email_dirname) + self.encoders_imp = imp.DirectoryImporter(TestCase.encoders_dirname) + if TestCase.dynamic_tests_enabled: + self.dyn_imp = imp.DirectoryImporter(TestCase.dynamic_dirname) + + def test_import(self): + m = __import__('string') + self.assertEqual(type(m.__loader__), imp.DirectoryImporter) + m = __import__('email') + self.assertEqual(type(m.__loader__), imp.DirectoryImporter) + m = __import__('email.encoders') + self.assertEqual(type(m.__loader__), imp.DirectoryImporter) + self.assertEqual(type(m.encoders.__loader__), imp.DirectoryImporter) + if TestCase.dynamic_tests_enabled: + m = __import__('array') + self.assertEqual(type(m.__loader__), imp.DirectoryImporter) + m = __import__('_types') + self.assertNotEqual(type(getattr(m, '__loader__', None)), + imp.DirectoryImporter) + self.assertRaises(ImportError, __import__, 'no_module_by_this_name') + + def test_find_module(self): + l = self.string_imp.find_module('string') + self.assertEqual(type(l), imp.DirectoryImporter) + l = self.string_imp.find_module('string', ['path_is_ignored']) + self.assertEqual(type(l), imp.DirectoryImporter) + l = self.email_imp.find_module('email') + self.assertEqual(type(l), imp.DirectoryImporter) + l = self.encoders_imp.find_module('email.encoders') + self.assertEqual(type(l), imp.DirectoryImporter) + if TestCase.dynamic_tests_enabled: + l = self.dyn_imp.find_module('array') + self.assertEqual(type(l), imp.DirectoryImporter) + l = self.string_imp.find_module('_types') + self.assertEqual(l, None) + + def test_load_module(self): + del_loader('string') + m = self.string_imp.load_module('string') + self.assertEqual(type(m.__loader__), imp.DirectoryImporter) + + del_loader('email') + m = self.email_imp.load_module('email') + self.assertEqual(type(m.__loader__), imp.DirectoryImporter) + + del_loader('email.encoders') + m = self.encoders_imp.load_module('email.encoders') + self.assertEqual(m.__name__, 'email.encoders') + self.assertEqual(type(m.__loader__), imp.DirectoryImporter) + + if TestCase.dynamic_tests_enabled: + del_loader('array') + m = self.dyn_imp.load_module('array') + self.assertEqual(type(m.__loader__), imp.DirectoryImporter) + + self.assertRaises(ImportError, self.string_imp.load_module, '_types') + + def test_get_data(self): + # XXX PEP 302 says the filename should include the importer + # path (i.e. absolute filename) but zipimporter expects that + # the filename not include the importer path (i.e. relative + # filename). ImpLoader takes either but resolves filenames + # relative to CWD, not the importer directory. + + if 0: # XXX Not implemented yet + # Absolute path + d = self.string_imp.get_data(TestCase.string_source_filename) + self.assert_(len(d) > 0) + d = self.email_imp.get_data('email/encoders.py') + self.assert_(len(d) > 0) + if TestCase.dynamic_tests_enabled: + d = self.dyn_imp.get_data(TestCase.dynamic_module_filename) + self.assert_(len(d) > 0) + self.assertRaises(IOError, self.string_imp.get_data, '_types') + + def test_is_package(self): + self.assertFalse(self.string_imp.is_package('string')) + self.assertTrue(self.email_imp.is_package('email')) + self.assertFalse(self.encoders_imp.is_package('email.encoders')) + if TestCase.dynamic_tests_enabled: + self.assertFalse(self.dyn_imp.is_package('array')) + self.assertRaises(ImportError, self.string_imp.is_package, '_types') + + def test_get_code(self): + self.assertEqual(type(self.string_imp.get_code('string')), types.CodeType) + if 0: # XXX Not implemented for PKG_DIRECTORY + self.assertEqual(type(self.email_imp.get_code('email')), types.CodeType) + self.assertEqual(type(self.encoders_imp.get_code('email.encoders')), types.CodeType) + if TestCase.dynamic_tests_enabled: + self.assertEqual(None, self.dyn_imp.get_code('array')) + self.assertRaises(ImportError, self.string_imp.get_code, '_types') + + def test_get_source(self): + if 0: # XXX Not implemented yet + self.assertEqual(len(self.string_imp.get_source('string')) > 0) + self.assertEqual(len(self.email_imp.get_source('email')) > 0) + self.assertEqual(len(self.encoders_imp.get_source('email.encoders')) > 0) + if TestCase.dynamic_tests_enabled: + self.assertEqual(None, self.dyn_imp.get_source('array')) + self.assertRaises(ImportError, self.string_imp.get_source, '_types') + + def test_imp_find_module_load_module(self): + def find_load_test(modname, path, types): + del_loader(modname) + info = imp.find_module(modname, path) + self.assert_(info[2][2] in types, (modname, info, types)) + m = imp.load_module(modname, info[0], info[1], info[2]) + self.assertEqual(m.__name__, modname) + self.assertFalse(hasattr(m, '__loader__'), modname) + + find_load_test('string', None, (imp.PY_SOURCE, imp.PY_COMPILED)) + self.assertRaises(ImportError, imp.find_module, 'string', + ['no_such_directory']) + find_load_test('email', None, (imp.PKG_DIRECTORY,)) + find_load_test('email.encoders', [TestCase.encoders_dirname], + (imp.PY_SOURCE, imp.PY_COMPILED)) + if TestCase.dynamic_tests_enabled: + find_load_test('array', None, (imp.C_EXTENSION,)) + + self.assertRaises(ImportError, imp.find_module, 'no_module_by_this_name') + + def test_imp_reload(self): + def reload_test(loader, modname, attrname): + del_loader(modname) + test.test_support.unload(modname) + m = loader.load_module(modname) + self.assertEqual(m.__name__, modname) + self.assertEqual(m.__loader__, loader) + attrval = getattr(m, attrname) + self.assert_(attrval) + delattr(m, attrname) + imp.reload(m) + self.assertEqual(m.__name__, modname) + self.assertEqual(getattr(m, attrname), attrval) + + reload_test(self.string_imp, 'string', '__file__') + reload_test(self.email_imp, 'email', '__file__') + reload_test(self.encoders_imp, 'email.encoders', '__file__') + if TestCase.dynamic_tests_enabled: + reload_test(self.dyn_imp, 'array', '__file__') + + def test_imp_load_compiled(self): + def load_compiled_test(modname, descr, filename): + """Try once without open file, then again with open file.""" + (suffix, mode, modtype) = descr + m = imp.load_compiled(modname, filename) + self.assertEqual(m.__name__, modname) + self.assertFalse(hasattr(m, '__loader__'), modname) + f = open(filename, mode) + m = imp.load_compiled(modname, filename, f) + self.assertEqual(m.__name__, modname) + self.assertFalse(hasattr(m, '__loader__'), modname) + f.close() + + load_compiled_test('string', TestCase.py_compiled_descr, + os.path.join(TestCase.string_dirname, + 'string' + TestCase.py_compiled_descr[0])) + load_compiled_test('email.encoders', TestCase.py_compiled_descr, + os.path.join(TestCase.encoders_dirname, + 'encoders' + TestCase.py_compiled_descr[0])) + + self.assertRaises((ImportError, IOError), imp.load_compiled, 'email', + os.path.join(TestCase.email_dirname, "email")) + self.assertRaises(IOError, imp.load_compiled, 'no_module_by_this_name', + 'no_module_by_this_name') + + def test_imp_load_source(self): + def load_source_test(modname, descr, filename): + """Try once without open file, then again with open file.""" + (suffix, mode, modtype) = descr + m = imp.load_source(modname, filename) + self.assertEqual(m.__name__, modname) + self.assertFalse(hasattr(m, '__loader__'), modname) + f = open(filename, mode) + m = imp.load_source(modname, filename, f) + self.assertEqual(m.__name__, modname) + self.assertFalse(hasattr(m, '__loader__'), modname) + f.close() + + load_source_test('string', TestCase.py_source_descr, + os.path.join(TestCase.string_dirname, + 'string' + TestCase.py_source_descr[0])) + load_source_test('email.encoders', TestCase.py_source_descr, + os.path.join(TestCase.encoders_dirname, + 'encoders' + TestCase.py_source_descr[0])) + + if 0: + # XXX Python doesn't currently complain if you try to load + # a directory as a source file. + self.assertRaises(ImportError, imp.load_source, 'email', + os.path.join(TestCase.email_dirname, "email")) + self.assertFalse(os.path.exists(TestCase.email_dir_filename + 'c')) + self.assertRaises(IOError, imp.load_source, 'no_module_by_this_name', + 'no_module_by_this_name') + + def test_imp_load_dynamic(self): + def load_dynamic_test(modname, descr, filename): + """Try once without open file, then again with open file.""" + (suffix, mode, modtype) = descr + m = imp.load_dynamic(modname, filename) + self.assertEqual(m.__name__, modname) + self.assertFalse(hasattr(m, '__loader__'), modname) + f = open(filename, mode) + m = imp.load_dynamic(modname, filename, f) + self.assertEqual(m.__name__, modname) + self.assertFalse(hasattr(m, '__loader__'), modname) + f.close() + + if TestCase.dynamic_tests_enabled: + load_dynamic_test('array', TestCase.dynamic_module_descr, + TestCase.dynamic_module_filename) + + self.assertRaises(ImportError, imp.load_dynamic, 'string', + TestCase.string_source_filename) + self.assertRaises(ImportError, imp.load_dynamic, 'no_module_by_this_name', + 'no_module_by_this_name') + + +def test_main(verbose=None): + module_setUp() + try: + test.test_support.run_unittest(TestCase) + finally: + module_tearDown() + +if __name__ == '__main__': + test_main() Property changes on: Lib/test/test_DirectoryImporter.py ___________________________________________________________________ Name: svn:executable + * Index: Lib/test/test_BuiltinImporter.py =================================================================== --- Lib/test/test_BuiltinImporter.py (revision 0) +++ Lib/test/test_BuiltinImporter.py (revision 0) @@ -0,0 +1,238 @@ +"""Unit tests for imp.BuiltinImporter""" + +import unittest +import imp +import os +import types +import sys +import test.test_support + +# Assumptions: +# sys: builtin module, no initfunc, already loaded +# _types: builtin module, has initfunc, not loaded +# __hello__: frozen module, not loaded +# __phello__: frozen package, not loaded +# __phello__.spam: frozen module, not loaded +# encodings: not builtin or frozen module + +def del_loader(modname): + """Remove __loader__ attribute from named module if any""" + m = sys.modules.get(modname) + if m and hasattr(m, '__loader__'): + delattr(m, '__loader__') + +class TestCase(unittest.TestCase): + def setUp(self): + self.sys_path = sys.path[:] + test.test_support.unload('_types') + test.test_support.unload('__hello__') + test.test_support.unload('__phello__') + test.test_support.unload('__phello__.spam') + + def tearDown(self): + test.test_support.unload('_types') + test.test_support.unload('__hello__') + test.test_support.unload('__phello__') + test.test_support.unload('__phello__.spam') + self.assertEqual(sys.path, self.sys_path) + if hasattr(sys, "setdefaultencoding"): + del sys.setdefaultencoding + + def test_01_loader(self): + # Order dependency: This must run before the sys module is reloaded + m = sys.modules['sys'] + # sys bypasses init_builtin, so doesn't have a __loader__ + self.assertFalse(hasattr(m, '__loader__')) + + def test_import(self): + m = __import__('_types') + self.assertEqual(type(m.__loader__), imp.BuiltinImporter) + m = __import__('__hello__') + self.assertEqual(type(m.__loader__), imp.BuiltinImporter) + m = __import__('__phello__') + self.assertEqual(type(m.__loader__), imp.BuiltinImporter) + m = __import__('__phello__.spam') + self.assertEqual(type(m.__loader__), imp.BuiltinImporter) + self.assertEqual(type(m.spam.__loader__), imp.BuiltinImporter) + m = __import__('encodings') + self.assertNotEqual(type(getattr(m, '__loader__', None)), + imp.BuiltinImporter) + self.assertRaises(ImportError, __import__, 'no_module_by_this_name') + + def test_find_module(self): + i = imp.BuiltinImporter() + l = i.find_module('sys') + self.assertEqual(type(l), imp.BuiltinImporter) + l = i.find_module('sys', ['path_is_not_ignored']) + self.assertEqual(l, None) + l = i.find_module('_types') + self.assertEqual(type(l), imp.BuiltinImporter) + l = i.find_module('__hello__') + self.assertEqual(type(l), imp.BuiltinImporter) + l = i.find_module('__phello__') + self.assertEqual(type(l), imp.BuiltinImporter) + l = i.find_module('__phello__.spam') + self.assertEqual(type(l), imp.BuiltinImporter) + l = i.find_module('encodings') + self.assertEqual(l, None) + + def test_load_module(self): + i = imp.BuiltinImporter() + + del_loader('sys') + m = i.load_module('sys') + self.assertEqual(type(m.__loader__), imp.BuiltinImporter) + + del_loader('_types') + m = i.load_module('_types') + self.assertEqual(type(m.__loader__), imp.BuiltinImporter) + + del_loader('__hello__') + m = i.load_module('__hello__') + self.assertEqual(type(m.__loader__), imp.BuiltinImporter) + + del_loader('__phello__') + m = i.load_module('__phello__') + self.assertEqual(type(m.__loader__), imp.BuiltinImporter) + + del_loader('__phello__.spam') + m = i.load_module('__phello__.spam') + self.assertEqual(m.__name__, '__phello__.spam') + self.assertEqual(type(m.__loader__), imp.BuiltinImporter) + + self.assertRaises(ImportError, i.load_module, 'encodings') + + def test_get_data(self): + # get_data does nothing for builtins + i = imp.BuiltinImporter() + self.assertRaises(IOError, i.get_data, 'sys') + self.assertRaises(IOError, i.get_data, '_types') + self.assertRaises(IOError, i.get_data, '__hello__') + self.assertRaises(IOError, i.get_data, '__phello__') + self.assertRaises(IOError, i.get_data, '__phello__.spam') + self.assertRaises(IOError, i.get_data, '__phello__' + os.sep + 'spam') + self.assertRaises(IOError, i.get_data, 'encodings') + + def test_is_package(self): + i = imp.BuiltinImporter() + self.assertFalse(i.is_package('sys')) + self.assertFalse(i.is_package('_types')) + self.assertFalse(i.is_package('__hello__')) + self.assertTrue(i.is_package('__phello__')) + self.assertFalse(i.is_package('__phello__.spam')) + self.assertRaises(ImportError, i.is_package, 'encodings') + + def test_get_code(self): + i = imp.BuiltinImporter() + self.assertEqual(None, i.get_code('sys')) + self.assertEqual(None, i.get_code('_types')) + self.assertEqual(type(i.get_code('__hello__')), types.CodeType) + self.assertEqual(type(i.get_code('__phello__')), types.CodeType) + self.assertEqual(type(i.get_code('__phello__.spam')), types.CodeType) + self.assertRaises(ImportError, i.get_code, 'encodings') + + def test_get_source(self): + i = imp.BuiltinImporter() + self.assertEqual(None, i.get_source('sys')) + self.assertEqual(None, i.get_source('_types')) + self.assertEqual(None, i.get_source('__hello__')) + self.assertEqual(None, i.get_source('__phello__')) + self.assertEqual(None, i.get_source('__phello__.spam')) + self.assertRaises(ImportError, i.get_source, 'encodings') + + def test_imp_find_module_load_module(self): + def find_load_test(modname, path, expected_info): + del_loader(modname) + info = imp.find_module(modname, path) + self.assertEqual(info, expected_info, (modname, info)) + m = imp.load_module(modname, info[0], info[1], info[2]) + self.assertEqual(m.__name__, modname) + self.assertFalse(hasattr(m, '__loader__'), modname) + + find_load_test('sys', None, (None, 'sys', ('', '', imp.C_BUILTIN))) + self.assertRaises(ImportError, imp.find_module, + 'sys', ['path_is_not_ignored']) + find_load_test('_types', None, (None, '_types', ('', '', imp.C_BUILTIN))) + find_load_test('__hello__', None, + (None, '__hello__', ('', '', imp.PY_FROZEN))) + find_load_test('__phello__', None, + (None, '__phello__', ('', '', imp.PY_FROZEN))) + find_load_test('__phello__.spam', None, + (None, '__phello__.spam', ('', '', imp.PY_FROZEN))) + + self.assertRaises(ImportError, imp.find_module, 'no_module_by_this_name') + + def test_imp_reload(self): + def reload_test(loader, modname, attrname): + del_loader(modname) + m = loader.load_module(modname) + self.assertEqual(m.__name__, modname) + self.assertEqual(m.__loader__, loader) + attrval = getattr(m, attrname) + self.assert_(attrval) + delattr(m, attrname) + imp.reload(m) + self.assertEqual(getattr(m, attrname), attrval) + + i = imp.BuiltinImporter() + reload_test(i, 'sys', '__doc__') + reload_test(i, '_types', '__doc__') + reload_test(i, '__hello__', '__file__') + reload_test(i, '__phello__', '__file__') + reload_test(i, '__phello__.spam', '__file__') + + def test_imp_init_builtin(self): + def builtin_test(modname, is_builtin): + del_loader(modname) + m = imp.init_builtin(modname) + if is_builtin: + self.assertEqual(m.__name__, modname) + self.assertFalse(hasattr(m, '__loader__'), modname) + else: + self.assertEqual(m, None) + + builtin_test('sys', True) + builtin_test('_types', True) + builtin_test('__hello__', False) + builtin_test('__phello__', False) + builtin_test('__phello__.spam', False) + builtin_test('encodings', False) + + def test_imp_init_frozen(self): + def frozen_test(modname, is_frozen): + del_loader(modname) + m = imp.init_frozen(modname) + if is_frozen: + self.assertEqual(m.__name__, modname) + self.assertFalse(hasattr(m, '__loader__')) + else: + self.assertEqual(m, None) + + frozen_test('sys', False) + frozen_test('_types', False) + frozen_test('__hello__', True) + frozen_test('__phello__', True) + frozen_test('__phello__.spam', True) + frozen_test('encodings', False) + + def test_imp_is_builtin(self): + self.assertTrue(imp.is_builtin('sys')) + self.assertTrue(imp.is_builtin('_types')) + self.assertFalse(imp.is_builtin('__hello__')) + self.assertFalse(imp.is_builtin('__phello__')) + self.assertFalse(imp.is_builtin('__hello__.spam')) + self.assertFalse(imp.is_builtin('encodings')) + + def test_imp_is_frozen(self): + self.assertFalse(imp.is_frozen('sys')) + self.assertFalse(imp.is_frozen('_types')) + self.assertTrue(imp.is_frozen('__hello__')) + self.assertTrue(imp.is_frozen('__phello__')) + self.assertTrue(imp.is_frozen('__phello__.spam')) + self.assertFalse(imp.is_frozen('encodings')) + +def test_main(verbose=None): + test.test_support.run_unittest(TestCase) + +if __name__ == '__main__': + test_main() Property changes on: Lib/test/test_BuiltinImporter.py ___________________________________________________________________ Name: svn:executable + * Index: Lib/test/test_import.py =================================================================== --- Lib/test/test_import.py (revision 60883) +++ Lib/test/test_import.py (working copy) @@ -1,4 +1,3 @@ -from test.test_support import TESTFN, run_unittest, catch_warning import unittest import os @@ -8,7 +7,8 @@ import py_compile import warnings import imp -from test.test_support import unlink, TESTFN, unload +from test.test_support import unlink, TESTFN, run_unittest, unload, \ + catch_warning, verbose def remove_files(name): @@ -23,6 +23,14 @@ class ImportTest(unittest.TestCase): + def setUp(self): + if os.path.exists(TESTFN): + shutil.rmtree(TESTFN) + + def tearDown(self): + if os.path.exists(TESTFN): + shutil.rmtree(TESTFN) + def testCaseSensitivity(self): # Brief digression to test that import is case-sensitive: if we got this # far, we know for sure that "random" exists. @@ -234,17 +242,89 @@ class PathsTests(unittest.TestCase): SAMPLES = ('test', 'test\u00e4\u00f6\u00fc\u00df', 'test\u00e9\u00e8', - 'test\u00b0\u00b3\u00b2') + 'test\u00b0\u00b3\u00b2', + 'test\u03b1', # greek alpha + '\u0001', '\u00ff', # Just to be difficult + ) path = TESTFN def setUp(self): + if os.path.exists(self.path): + shutil.rmtree(self.path) os.mkdir(self.path) self.syspath = sys.path[:] def tearDown(self): - shutil.rmtree(self.path) + if os.path.exists(self.path): + shutil.rmtree(self.path) sys.path = self.syspath + def test_sys_path_with_unicode(self): + for i, sample in enumerate(self.SAMPLES): + # If we can't decode the encoded string, import won't work + roundtrip = None + try: + enc = sys.getfilesystemencoding() + roundtrip = sample.encode(enc).decode(enc) + except UnicodeEncodeError: + pass + if roundtrip != sample: + if verbose: + print("Skipping sample %d, can't roundtrip encode" % i) + continue + + # Try to create module and dirs + path = os.path.join(self.path, sample) + pkgname = 'pkg_%s' % sample + pkg_filename = os.path.join(path, pkgname) + subname = 'module_%s' % sample + fullname = '%s.%s' % (pkgname, subname) + module_filename = os.path.join(pkg_filename, subname + '.py') + try: + os.mkdir(path) + os.mkdir(pkg_filename) + f = open(os.path.join(pkg_filename, '__init__.py'), 'w') + f.write('') + f.close() + g = open(module_filename, 'w') + g.write("testdata = 'unicode path %i'\n" % i) + g.close() + except (UnicodeEncodeError, EnvironmentError) as e: + if (i == 0): + # Don't ignore errors in the first all-ascii sample + raise + else: + # If the os can't handle a particular filename, skip it + if verbose: + print("Skipping sample %d: %s" % (i, e)) + continue + # Import the module + try: + sys.path = self.syspath[:] + sys.path.append(path) + try: + pkg = __import__(fullname) + except ImportError: + if verbose: + print("Failed on sample %d (%s)" % (i, repr(sample))) + raise + self.assert_(hasattr(pkg, subname)) + mod = getattr(pkg, subname) + self.assertEqual(mod.testdata, 'unicode path %i' % i) + finally: + unload(fullname) + unload(pkgname) + + def test_null_bytes(self): + # TypeError: __import__() argument 1 must be string without + # null bytes, not str + self.assertRaises(TypeError, __import__, '\u0000') + self.assertRaises(TypeError, __import__, 'test\u0000foo') + # Shouldn't raise TypeError, \u0001 doesn't have null bytes in UTF-8 + self.assertRaises(ImportError, __import__, '\u0001') + # ValueError: Module name too long + self.assertRaises(ValueError, __import__, 't' * 1000000) + # http://bugs.python.org/issue1293 def test_trailing_slash(self): f = open(os.path.join(self.path, 'test_trailing_slash.py'), 'w') Index: Modules/zipimport.c =================================================================== --- Modules/zipimport.c (revision 60883) +++ Modules/zipimport.c (working copy) @@ -314,10 +314,6 @@ } dict = PyModule_GetDict(mod); - /* mod.__loader__ = self */ - if (PyDict_SetItemString(dict, "__loader__", (PyObject *)self) != 0) - goto error; - if (ispackage) { /* add __path__ to the module *before* the code gets executed */ @@ -343,7 +339,8 @@ if (err != 0) goto error; } - mod = PyImport_ExecCodeModuleEx(fullname, code, modpath); + mod = PyImport_ExecCodeModuleEx(fullname, code, modpath, + (PyObject *)self); Py_DECREF(code); if (Py_VerboseFlag) PySys_WriteStderr("import %s # loaded from Zip %s\n", @@ -509,13 +506,13 @@ "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."); +if 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\ +if 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[] = {