summaryrefslogtreecommitdiffstats
path: root/Modules/zipimport.c
diff options
context:
space:
mode:
authorEric V. Smith <eric@trueblade.com>2012-05-25 00:21:04 (GMT)
committerEric V. Smith <eric@trueblade.com>2012-05-25 00:21:04 (GMT)
commit984b11f88fcace98e30decc19bbf9e281355e607 (patch)
tree613a0fb564da71c5fc84e9343813f87619591732 /Modules/zipimport.c
parentfa52cbd5e6210f257de40aab12d55d84d64bdb91 (diff)
downloadcpython-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.c148
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,