diff --git a/Lib/test/test_namespace_pkgs.py b/Lib/test/test_namespace_pkgs.py --- a/Lib/test/test_namespace_pkgs.py +++ b/Lib/test/test_namespace_pkgs.py @@ -254,9 +254,8 @@ class DynamicPathCalculation(NamespacePa class ZipWithMissingDirectory(NamespacePackageTest): paths = ['missing_directory.zip'] - @unittest.expectedFailure def test_missing_directory(self): - # This will fail because missing_directory.zip contains: + # missing_directory.zip contains: # Length Date Time Name # --------- ---------- ----- ---- # 29 2012-05-03 18:13 foo/one.py @@ -265,8 +264,8 @@ class ZipWithMissingDirectory(NamespaceP # --------- ------- # 67 3 files - # Because there is no 'foo/', the zipimporter currently doesn't - # know that foo is a namespace package + # foo/ should be synthesised and added to zipimporter's internal state, + # allowing import foo.one to work. import foo.one diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -311,6 +311,9 @@ Extension Modules - Issue #15194: Update libffi to the 3.0.11 release. +- Issue #14905: zipimport.c now does not require directory entries to exist in + the zipfile manifest in order to import modules within namespaces. + Tools/Demos ----------- diff --git a/Modules/zipimport.c b/Modules/zipimport.c --- a/Modules/zipimport.c +++ b/Modules/zipimport.c @@ -481,7 +481,7 @@ zipimporter_load_module(PyObject *obj, P goto error; err = PyDict_SetItemString(dict, "__path__", pkgpath); Py_DECREF(pkgpath); - if (err != 0) + if (err == -1) goto error; } mod = PyImport_ExecCodeModuleObject(fullname, code, modpath, NULL); @@ -897,8 +897,10 @@ read_directory(PyObject *archive) /* Start of Central Directory */ count = 0; for (;;) { - PyObject *t; + PyObject *toc_entry; + PyObject *dirnameobj = NULL; int err; + ssize_t nameobj_length; fseek(fp, header_offset, 0); /* Start of file header */ l = PyMarshal_ReadLongFromFile(fp); @@ -960,15 +962,54 @@ read_directory(PyObject *archive) path = PyUnicode_FromFormat("%U%c%U", archive, SEP, nameobj); if (path == NULL) goto error; - t = Py_BuildValue("Nhllnhhl", path, compress, data_size, + toc_entry = Py_BuildValue("Nhllnhhl", path, compress, data_size, file_size, file_offset, time, date, crc); - if (t == NULL) + if (toc_entry == NULL) goto error; - err = PyDict_SetItem(files, nameobj, t); - Py_CLEAR(nameobj); - Py_DECREF(t); + err = PyDict_SetItem(files, nameobj, toc_entry); + Py_DECREF(toc_entry); if (err != 0) goto error; + + nameobj_length = PyUnicode_GetLength(nameobj); + if (nameobj_length == -1) + goto error; + + for (i = 0; i < nameobj_length - 1; i++) { + Py_UCS4 c = PyUnicode_READ_CHAR(nameobj, i); + if (c != SEP) + continue; + + dirnameobj = PyUnicode_Substring(nameobj, 0, i + 1); + if (dirnameobj == NULL) { + goto error; + } + if (PyDict_Contains(files, dirnameobj)) { + Py_DECREF(dirnameobj); + continue; + } + + path = PyUnicode_FromFormat("%U%c%U", archive, SEP, dirnameobj); + if (path == NULL) { + Py_DECREF(dirnameobj); + goto error; + } + + toc_entry = Py_BuildValue("Ohllnhhl", path, 0, 0, 0, 0, 0, 0, 0); + Py_DECREF(path); + if (toc_entry == NULL) { + Py_DECREF(dirnameobj); + goto error; + } + err = PyDict_SetItem(files, dirnameobj, toc_entry); + Py_DECREF(dirnameobj); + Py_DECREF(toc_entry); + if (err != 0) { + goto error; + } + count++; + } + Py_DECREF(nameobj); count++; } fclose(fp);