diff options
author | Eric V. Smith <eric@trueblade.com> | 2012-05-25 00:21:04 (GMT) |
---|---|---|
committer | Eric V. Smith <eric@trueblade.com> | 2012-05-25 00:21:04 (GMT) |
commit | 984b11f88fcace98e30decc19bbf9e281355e607 (patch) | |
tree | 613a0fb564da71c5fc84e9343813f87619591732 /Modules/zipimport.c | |
parent | fa52cbd5e6210f257de40aab12d55d84d64bdb91 (diff) | |
download | cpython-984b11f88fcace98e30decc19bbf9e281355e607.zip cpython-984b11f88fcace98e30decc19bbf9e281355e607.tar.gz cpython-984b11f88fcace98e30decc19bbf9e281355e607.tar.bz2 |
issue 14660: Implement PEP 420, namespace packages.
Diffstat (limited to 'Modules/zipimport.c')
-rw-r--r-- | Modules/zipimport.c | 148 |
1 files changed, 140 insertions, 8 deletions
diff --git a/Modules/zipimport.c b/Modules/zipimport.c index f822f92..4bb2863 100644 --- a/Modules/zipimport.c +++ b/Modules/zipimport.c @@ -259,6 +259,29 @@ enum zi_module_info { MI_PACKAGE }; +/* Does this path represent a directory? + on error, return < 0 + if not a dir, return 0 + if a dir, return 1 +*/ +static int +check_is_directory(ZipImporter *self, PyObject* prefix, PyObject *path) +{ + PyObject *dirpath; + PyObject *item; + + /* See if this is a "directory". If so, it's eligible to be part + of a namespace package. We test by seeing if the name, with an + appended path separator, exists. */ + dirpath = PyUnicode_FromFormat("%U%U%c", prefix, path, SEP); + if (dirpath == NULL) + return -1; + /* If dirpath is present in self->files, we have a directory. */ + item = PyDict_GetItem(self->files, dirpath); + Py_DECREF(dirpath); + return item != NULL; +} + /* Return some information about a module. */ static enum zi_module_info get_module_info(ZipImporter *self, PyObject *fullname) @@ -296,6 +319,46 @@ get_module_info(ZipImporter *self, PyObject *fullname) return MI_NOT_FOUND; } +/* The guts of "find_loader" and "find_module". Return values: + -1: error + 0: no loader or namespace portions found + 1: module/package found + 2: namespace portion found: *namespace_portion will point to the name +*/ +static int +find_loader(ZipImporter *self, PyObject *fullname, PyObject **namespace_portion) +{ + enum zi_module_info mi; + + *namespace_portion = NULL; + + mi = get_module_info(self, fullname); + if (mi == MI_ERROR) + return -1; + if (mi == MI_NOT_FOUND) { + /* Not a module or regular package. See if this is a directory, and + therefore possibly a portion of a namespace package. */ + int is_dir = check_is_directory(self, self->prefix, fullname); + if (is_dir < 0) + return -1; + if (is_dir) { + /* This is possibly a portion of a namespace + package. Return the string representing its path, + without a trailing separator. */ + *namespace_portion = PyUnicode_FromFormat("%U%c%U%U", + self->archive, SEP, + self->prefix, fullname); + if (*namespace_portion == NULL) + return -1; + return 2; + } + return 0; + } + /* This is a module or package. */ + return 1; +} + + /* Check whether we can satisfy the import of the module named by 'fullname'. Return self if we can, None if we can't. */ static PyObject * @@ -304,21 +367,78 @@ zipimporter_find_module(PyObject *obj, PyObject *args) ZipImporter *self = (ZipImporter *)obj; PyObject *path = NULL; PyObject *fullname; - enum zi_module_info mi; + PyObject* namespace_portion = NULL; if (!PyArg_ParseTuple(args, "U|O:zipimporter.find_module", &fullname, &path)) - return NULL; + goto error; - mi = get_module_info(self, fullname); - if (mi == MI_ERROR) - return NULL; - if (mi == MI_NOT_FOUND) { + switch (find_loader(self, fullname, &namespace_portion)) { + case -1: /* Error */ + goto error; + case 0: /* Not found, return None */ + Py_INCREF(Py_None); + return Py_None; + case 1: /* Return self */ + Py_INCREF(self); + return (PyObject *)self; + case 2: /* A namespace portion, but not allowed via + find_module, so return None */ + Py_DECREF(namespace_portion); Py_INCREF(Py_None); return Py_None; } - Py_INCREF(self); - return (PyObject *)self; + /* Can't get here. */ + assert(0); + return NULL; +error: + Py_XDECREF(namespace_portion); + return NULL; +} + + +/* Check whether we can satisfy the import of the module named by + 'fullname', or whether it could be a portion of a namespace + package. Return self if we can load it, a string containing the + full path if it's a possible namespace portion, None if we + can't load it. */ +static PyObject * +zipimporter_find_loader(PyObject *obj, PyObject *args) +{ + ZipImporter *self = (ZipImporter *)obj; + PyObject *path = NULL; + PyObject *fullname; + PyObject *result = NULL; + PyObject *namespace_portion = NULL; + + if (!PyArg_ParseTuple(args, "U|O:zipimporter.find_module", + &fullname, &path)) + goto error; + + switch (find_loader(self, fullname, &namespace_portion)) { + case -1: /* Error */ + goto error; + case 0: /* Not found, return (None, []) */ + if (!(result = Py_BuildValue("O[]", Py_None))) + goto error; + return result; + case 1: /* Return (self, []) */ + if (!(result = Py_BuildValue("O[]", self))) + goto error; + return result; + case 2: /* Return (None, [namespace_portion]) */ + if (!(result = Py_BuildValue("O[O]", Py_None, namespace_portion))) + goto error; + return result; + } + /* Can't get here. */ + assert(0); + return NULL; + +error: + Py_XDECREF(namespace_portion); + Py_XDECREF(result); + return NULL; } /* Load and return the module named by 'fullname'. */ @@ -558,6 +678,16 @@ instance itself if the module was found, or None if it wasn't.\n\ The optional 'path' argument is ignored -- it's there for compatibility\n\ with the importer protocol."); +PyDoc_STRVAR(doc_find_loader, +"find_loader(fullname, path=None) -> self, str or None.\n\ +\n\ +Search for a module specified by 'fullname'. 'fullname' must be the\n\ +fully qualified (dotted) module name. It returns the zipimporter\n\ +instance itself if the module was found, a string containing the\n\ +full path name if it's possibly a portion of a namespace package,\n\ +or None otherwise. The optional 'path' argument is ignored -- it's\n\ + there for compatibility with the importer protocol."); + PyDoc_STRVAR(doc_load_module, "load_module(fullname) -> module.\n\ \n\ @@ -599,6 +729,8 @@ Return the filename for the specified module."); static PyMethodDef zipimporter_methods[] = { {"find_module", zipimporter_find_module, METH_VARARGS, doc_find_module}, + {"find_loader", zipimporter_find_loader, METH_VARARGS, + doc_find_loader}, {"load_module", zipimporter_load_module, METH_VARARGS, doc_load_module}, {"get_data", zipimporter_get_data, METH_VARARGS, |