diff -ur Python-3.5.1/Lib/test/test_zipimport.py Python-3.5.1.zip/Lib/test/test_zipimport.py --- Python-3.5.1/Lib/test/test_zipimport.py 2015-12-06 18:39:09.000000000 -0700 +++ Python-3.5.1.zip/Lib/test/test_zipimport.py 2016-01-04 10:24:37.019937720 -0700 @@ -5,6 +5,7 @@ import struct import time import unittest +import shutil from test import support @@ -48,6 +49,7 @@ TESTMOD = "ziptestmodule" TESTPACK = "ziptestpackage" TESTPACK2 = "ziptestpackage2" +TEMP_DIR = os.path.abspath("junk95142") TEMP_ZIP = os.path.abspath("junk95142.zip") pyc_file = importlib.util.cache_from_source(TESTMOD + '.py') @@ -82,23 +84,41 @@ zipimport._zip_directory_cache.clear() ImportHooksBaseTestCase.setUp(self) + def makeTree(self, files, dirName=TEMP_DIR): + if os.path.isdir(dirName): + shutil.rmtree(dirName) + for name, (mtime, data) in files.items(): + path = os.path.join(dirName, name) + if path[-1] == os.sep: + if not os.path.isdir(path): + os.makedirs(path) + else: + dname = os.path.dirname(path) + if not os.path.isdir(dname): + os.makedirs(dname) + with open(path, 'wb') as fp: + fp.write(data) + + def makeZip(self, files, zipName=TEMP_ZIP, **kw): + z = ZipFile(zipName, "w") + for name, (mtime, data) in files.items(): + zinfo = ZipInfo(name, time.localtime(mtime)) + zinfo.compress_type = self.compression + z.writestr(zinfo, data) + z.close() + + stuff = kw.get("stuff", None) + if stuff is not None: + # Prepend 'stuff' to the start of the zipfile + with open(TEMP_ZIP, "rb") as f: + data = f.read() + with open(zipName, "wb") as f: + f.write(stuff) + f.write(data) + def doTest(self, expected_ext, files, *modules, **kw): - z = ZipFile(TEMP_ZIP, "w") try: - for name, (mtime, data) in files.items(): - zinfo = ZipInfo(name, time.localtime(mtime)) - zinfo.compress_type = self.compression - z.writestr(zinfo, data) - z.close() - - stuff = kw.get("stuff", None) - if stuff is not None: - # Prepend 'stuff' to the start of the zipfile - with open(TEMP_ZIP, "rb") as f: - data = f.read() - with open(TEMP_ZIP, "wb") as f: - f.write(stuff) - f.write(data) + self.makeZip(files, **kw) sys.path.insert(0, TEMP_ZIP) @@ -114,7 +134,6 @@ self.assertEqual(file, os.path.join(TEMP_ZIP, *modules) + expected_ext) finally: - z.close() os.remove(TEMP_ZIP) def testAFakeZlib(self): @@ -209,6 +228,188 @@ packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc)} self.doTest(pyc_ext, files, TESTPACK, TESTPACK2, TESTMOD) + def testDeepNamespacePackage(self): + packdir = TESTPACK + os.sep + packdir2 = packdir + TESTPACK2 + os.sep + # The first two files are just directory entries (so have no data). + files = {packdir: (NOW, ""), + packdir2: (NOW, ""), + packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc)} + self.doTest(pyc_ext, files, TESTPACK, TESTPACK2, TESTMOD) + + def testMixedNamespacePackage(self): + packdir = TESTPACK + os.sep + packdir2 = packdir + TESTPACK2 + os.sep + packdir3 = packdir2 + TESTPACK + '3' + os.sep + files1 = {packdir: (NOW, ""), + packdir + TESTMOD + pyc_ext: (NOW, test_pyc), + packdir2: (NOW, ""), + packdir3: (NOW, ""), + packdir3 + TESTMOD + pyc_ext: (NOW, test_pyc), + packdir2 + TESTMOD + '3' + pyc_ext: (NOW, test_pyc), + packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc)} + files2 = {packdir: (NOW, ""), + packdir + TESTMOD + '2' + pyc_ext: (NOW, test_pyc), + packdir2: (NOW, ""), + packdir2 + TESTMOD + '2' + pyc_ext: (NOW, test_pyc), + packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc)} + + try: + zip1 = os.path.abspath("path1.zip") + self.makeZip(files1, zip1) + #os.system("unzip -l " + zip1) + zip2 = TEMP_DIR + self.makeTree(files2, zip2) + #os.system("find " + zip2) + + # zip2 should override zip1 + sys.path.insert(0, zip1) + sys.path.insert(0, zip2) + + mod = __import__(TESTPACK, globals(), locals(), + ["__dummy__"]) + + # if TESTPACK is functioning as a namespace pkg then + # there should be two entries in the __path__ + # First should be path2 and second path1 + self.assertEqual(2, len(mod.__path__)) + p1, p2 = mod.__path__ + self.assertEqual(os.path.basename(TEMP_DIR), p1.split(os.sep)[-2]) + self.assertEqual("path1.zip", p2.split(os.sep)[-2]) + + # packdir3 should import as a namespace package + # it's __path__ is an iterable of 1 element from zip1 + mod = __import__(packdir3.replace(os.sep, '.')[:-1], + globals(), locals(), ["__dummy__"]) + self.assertEqual(1, len(mod.__path__)) + mpath = list(mod.__path__)[0].split('path1.zip' + os.sep)[1] + self.assertEqual(packdir3[:-1], mpath) + + # TESTPACK/TESTMOD only exists in path1 + mod = __import__('.'.join((TESTPACK, TESTMOD)), globals(), locals(), + ["__dummy__"]) + self.assertEqual("path1.zip", mod.__file__.split(os.sep)[-3]) + + # And TESTPACK/(TESTMOD + '2') only exists in path2 + mod = __import__('.'.join((TESTPACK, TESTMOD + '2')), + globals(), locals(), ["__dummy__"]) + self.assertEqual(os.path.basename(TEMP_DIR), + mod.__file__.split(os.sep)[-3]) + + # One level deeper... + subpkg = '.'.join((TESTPACK, TESTPACK2)) + mod = __import__(subpkg, globals(), locals(), ["__dummy__"]) + self.assertEqual(2, len(mod.__path__)) + p1, p2 = mod.__path__ + self.assertEqual(os.path.basename(TEMP_DIR), p1.split(os.sep)[-3]) + self.assertEqual("path1.zip", p2.split(os.sep)[-3]) + + # subpkg.TESTMOD exists in both zips should load from zip2 + mod = __import__('.'.join((subpkg, TESTMOD)), + globals(), locals(), ["__dummy__"]) + self.assertEqual(os.path.basename(TEMP_DIR), + mod.__file__.split(os.sep)[-4]) + + # subpkg.TESTMOD + '2' only exists in zip2 + mod = __import__('.'.join((subpkg, TESTMOD + '2')), + globals(), locals(), ["__dummy__"]) + self.assertEqual(os.path.basename(TEMP_DIR), + mod.__file__.split(os.sep)[-4]) + + # Finally subpkg.TESTMOD + '3' only exists in zip1 + mod = __import__('.'.join((subpkg, TESTMOD + '3')), + globals(), locals(), ["__dummy__"]) + self.assertEqual('path1.zip', mod.__file__.split(os.sep)[-4]) + finally: + os.remove(zip1) + if os.path.isdir(TEMP_DIR): + shutil.rmtree(TEMP_DIR) + + def testNamespacePackage(self): + packdir = TESTPACK + os.sep + packdir2 = packdir + TESTPACK2 + os.sep + packdir3 = packdir2 + TESTPACK + '3' + os.sep + files1 = {packdir: (NOW, ""), + packdir + TESTMOD + pyc_ext: (NOW, test_pyc), + packdir2: (NOW, ""), + packdir3: (NOW, ""), + packdir3 + TESTMOD + pyc_ext: (NOW, test_pyc), + packdir2 + TESTMOD + '3' + pyc_ext: (NOW, test_pyc), + packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc)} + zip1 = os.path.abspath("path1.zip") + self.makeZip(files1, zip1) + #os.system("unzip -l " + zip1) + + files2 = {packdir: (NOW, ""), + packdir + TESTMOD + '2' + pyc_ext: (NOW, test_pyc), + packdir2: (NOW, ""), + packdir2 + TESTMOD + '2' + pyc_ext: (NOW, test_pyc), + packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc)} + zip2 = os.path.abspath("path2.zip") + self.makeZip(files2, zip2) + #os.system("unzip -l " + zip2) + + try: + # zip2 should override zip1 + sys.path.insert(0, zip1) + sys.path.insert(0, zip2) + + mod = __import__(TESTPACK, globals(), locals(), + ["__dummy__"]) + + # if TESTPACK is functioning as a namespace pkg then + # there should be two entries in the __path__ + # First should be path2 and second path1 + self.assertEqual(2, len(mod.__path__)) + p1, p2 = mod.__path__ + self.assertEqual("path2.zip", p1.split(os.sep)[-2]) + self.assertEqual("path1.zip", p2.split(os.sep)[-2]) + + # packdir3 should import as a namespace package + # it's __path__ is an iterable of 1 element from zip1 + mod = __import__(packdir3.replace(os.sep, '.')[:-1], + globals(), locals(), ["__dummy__"]) + self.assertEqual(1, len(mod.__path__)) + mpath = list(mod.__path__)[0].split('path1.zip' + os.sep)[1] + self.assertEqual(packdir3[:-1], mpath) + + # TESTPACK/TESTMOD only exists in path1 + mod = __import__('.'.join((TESTPACK, TESTMOD)), globals(), locals(), + ["__dummy__"]) + self.assertEqual("path1.zip", mod.__file__.split(os.sep)[-3]) + + # And TESTPACK/(TESTMOD + '2') only exists in path2 + mod = __import__('.'.join((TESTPACK, TESTMOD + '2')), + globals(), locals(), ["__dummy__"]) + self.assertEqual("path2.zip", mod.__file__.split(os.sep)[-3]) + + # One level deeper... + subpkg = '.'.join((TESTPACK, TESTPACK2)) + mod = __import__(subpkg, globals(), locals(), ["__dummy__"]) + self.assertEqual(2, len(mod.__path__)) + p1, p2 = mod.__path__ + self.assertEqual("path2.zip", p1.split(os.sep)[-3]) + self.assertEqual("path1.zip", p2.split(os.sep)[-3]) + + # subpkg.TESTMOD exists in both zips should load from zip2 + mod = __import__('.'.join((subpkg, TESTMOD)), + globals(), locals(), ["__dummy__"]) + self.assertEqual('path2.zip', mod.__file__.split(os.sep)[-4]) + + # subpkg.TESTMOD + '2' only exists in zip2 + mod = __import__('.'.join((subpkg, TESTMOD + '2')), + globals(), locals(), ["__dummy__"]) + self.assertEqual('path2.zip', mod.__file__.split(os.sep)[-4]) + + # Finally subpkg.TESTMOD + '3' only exists in zip1 + mod = __import__('.'.join((subpkg, TESTMOD + '3')), + globals(), locals(), ["__dummy__"]) + self.assertEqual('path1.zip', mod.__file__.split(os.sep)[-4]) + + finally: + os.remove(zip1) + os.remove(zip2) + def testZipImporterMethods(self): packdir = TESTPACK + os.sep packdir2 = packdir + TESTPACK2 + os.sep diff -ur Python-3.5.1/Modules/zipimport.c Python-3.5.1.zip/Modules/zipimport.c --- Python-3.5.1/Modules/zipimport.c 2015-12-06 18:39:10.000000000 -0700 +++ Python-3.5.1.zip/Modules/zipimport.c 2016-01-04 10:23:51.427188124 -0700 @@ -325,17 +325,14 @@ } typedef enum { - FL_ERROR, - FL_NOT_FOUND, - FL_MODULE_FOUND, - FL_NS_FOUND + FL_ERROR = -1, /* error */ + FL_NOT_FOUND, /* no loader or namespace portions found */ + FL_MODULE_FOUND, /* module/package found */ + FL_NS_FOUND /* namespace portion found: */ + /* *namespace_portion will point to the name */ } find_loader_result; -/* 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 +/* The guts of "find_loader" and "find_module". */ static find_loader_result find_loader(ZipImporter *self, PyObject *fullname, PyObject **namespace_portion) @@ -350,21 +347,34 @@ 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); + find_loader_result result = FL_NOT_FOUND; + PyObject *subname; + int is_dir; + + /* We're only interested in the last path component of fullname; + earlier components are recorded in self->prefix. */ + subname = get_subname(fullname); + if (subname == NULL) { + return FL_ERROR; + } + + is_dir = check_is_directory(self, self->prefix, subname); if (is_dir < 0) - return -1; - if (is_dir) { + result = FL_ERROR; + else 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); + self->prefix, subname); if (*namespace_portion == NULL) - return FL_ERROR; - return FL_NS_FOUND; + result = FL_ERROR; + else + result = FL_NS_FOUND; } - return FL_NOT_FOUND; + Py_DECREF(subname); + return result; } /* This is a module or package. */ return FL_MODULE_FOUND; @@ -398,6 +408,9 @@ case FL_MODULE_FOUND: result = (PyObject *)self; break; + default: + PyErr_BadInternalCall(); + return NULL; } Py_INCREF(result); return result;