mirror of
https://github.com/python/cpython.git
synced 2025-11-01 18:51:43 +00:00
issue 14660: Implement PEP 420, namespace packages.
This commit is contained in:
parent
fa52cbd5e6
commit
984b11f88f
25 changed files with 4455 additions and 3412 deletions
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue