[Python-checkins] bpo-28494: Test existing zipfile working behavior. (GH-15853)

Miss Islington (bot) webhook-mailer at python.org
Tue Sep 10 18:58:00 EDT 2019


https://github.com/python/cpython/commit/74b0291b03db60dd244d31e9c97407cccb8d30dd
commit: 74b0291b03db60dd244d31e9c97407cccb8d30dd
branch: 3.8
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: GitHub <noreply at github.com>
date: 2019-09-10T15:57:54-07:00
summary:

bpo-28494: Test existing zipfile working behavior. (GH-15853)


Add unittests for executables with a zipfile appended to test_zipfile, as zipfile.is_zipfile and zipfile.ZipFile work properly on these today.
(cherry picked from commit 3f4db4a0bab073b768fae958e93288bd5d24eadd)

Co-authored-by: Gregory P. Smith <greg at krypto.org>

files:
A Lib/test/ziptestdata/README.md
A Lib/test/ziptestdata/exe_with_z64
A Lib/test/ziptestdata/exe_with_zip
A Lib/test/ziptestdata/header.sh
A Lib/test/ziptestdata/testdata_module_inside_zip.py
M Lib/test/test_zipfile.py

diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py
index 8e437e5cb2b9..955e540f96e6 100644
--- a/Lib/test/test_zipfile.py
+++ b/Lib/test/test_zipfile.py
@@ -5,6 +5,8 @@
 import pathlib
 import posixpath
 import struct
+import subprocess
+import sys
 import time
 import unittest
 import zipfile
@@ -2443,6 +2445,44 @@ def build_alpharep_fixture():
     return zf
 
 
+class TestExecutablePrependedZip(unittest.TestCase):
+    """Test our ability to open zip files with an executable prepended."""
+
+    def setUp(self):
+        self.exe_zip = findfile('exe_with_zip', subdir='ziptestdata')
+        self.exe_zip64 = findfile('exe_with_z64', subdir='ziptestdata')
+
+    def _test_zip_works(self, name):
+        # bpo-28494 sanity check: ensure is_zipfile works on these.
+        self.assertTrue(zipfile.is_zipfile(name),
+                        f'is_zipfile failed on {name}')
+        # Ensure we can operate on these via ZipFile.
+        with zipfile.ZipFile(name) as zipfp:
+            for n in zipfp.namelist():
+                data = zipfp.read(n)
+                self.assertIn(b'FAVORITE_NUMBER', data)
+
+    def test_read_zip_with_exe_prepended(self):
+        self._test_zip_works(self.exe_zip)
+
+    def test_read_zip64_with_exe_prepended(self):
+        self._test_zip_works(self.exe_zip64)
+
+    @unittest.skipUnless(sys.executable, 'sys.executable required.')
+    @unittest.skipUnless(os.access('/bin/bash', os.X_OK),
+                         'Test relies on #!/bin/bash working.')
+    def test_execute_zip2(self):
+        output = subprocess.check_output([self.exe_zip, sys.executable])
+        self.assertIn(b'number in executable: 5', output)
+
+    @unittest.skipUnless(sys.executable, 'sys.executable required.')
+    @unittest.skipUnless(os.access('/bin/bash', os.X_OK),
+                         'Test relies on #!/bin/bash working.')
+    def test_execute_zip64(self):
+        output = subprocess.check_output([self.exe_zip64, sys.executable])
+        self.assertIn(b'number in executable: 5', output)
+
+
 class TestPath(unittest.TestCase):
     def setUp(self):
         self.fixtures = contextlib.ExitStack()
diff --git a/Lib/test/ziptestdata/README.md b/Lib/test/ziptestdata/README.md
new file mode 100644
index 000000000000..6b9147db76e1
--- /dev/null
+++ b/Lib/test/ziptestdata/README.md
@@ -0,0 +1,35 @@
+# Test data for `test_zipfile`
+
+The test executables in this directory are created manually from header.sh and
+the `testdata_module_inside_zip.py` file.  You must have infozip's zip utility
+installed (`apt install zip` on Debian).
+
+## Purpose
+
+These are used to test executable files with an appended zipfile, in a scenario
+where the executable is _not_ a Python interpreter itself so our automatic
+zipimport machinery (that'd look for `__main__.py`) is not being used.
+
+## Updating the test executables
+
+If you update header.sh or the testdata_module_inside_zip.py file, rerun the
+commands below.  These are expected to be rarely changed, if ever.
+
+### Standard old format (2.0) zip file
+
+```
+zip -0 zip2.zip testdata_module_inside_zip.py
+cat header.sh zip2.zip >exe_with_zip
+rm zip2.zip
+```
+
+### Modern format (4.5) zip64 file
+
+Redirecting from stdin forces infozip's zip tool to create a zip64.
+
+```
+zip -0 <testdata_module_inside_zip.py >zip64.zip
+cat header.sh zip64.zip >exe_with_z64
+rm zip64.zip
+```
+
diff --git a/Lib/test/ziptestdata/exe_with_z64 b/Lib/test/ziptestdata/exe_with_z64
new file mode 100755
index 000000000000..82b03cf39d91
Binary files /dev/null and b/Lib/test/ziptestdata/exe_with_z64 differ
diff --git a/Lib/test/ziptestdata/exe_with_zip b/Lib/test/ziptestdata/exe_with_zip
new file mode 100755
index 000000000000..c833cdf9f934
Binary files /dev/null and b/Lib/test/ziptestdata/exe_with_zip differ
diff --git a/Lib/test/ziptestdata/header.sh b/Lib/test/ziptestdata/header.sh
new file mode 100755
index 000000000000..52dc91acf740
--- /dev/null
+++ b/Lib/test/ziptestdata/header.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+INTERPRETER_UNDER_TEST="$1"
+if [[ ! -x "${INTERPRETER_UNDER_TEST}" ]]; then
+    echo "Interpreter must be the command line argument."
+    exit 4
+fi
+EXECUTABLE="$0" exec "${INTERPRETER_UNDER_TEST}" -E - <<END_OF_PYTHON
+import os
+import zipfile
+
+namespace = {}
+
+filename = os.environ['EXECUTABLE']
+print(f'Opening {filename} as a zipfile.')
+with zipfile.ZipFile(filename, mode='r') as exe_zip:
+  for file_info in exe_zip.infolist():
+    data = exe_zip.read(file_info)
+    exec(data, namespace, namespace)
+    break  # Only use the first file in the archive.
+
+print('Favorite number in executable:', namespace["FAVORITE_NUMBER"])
+
+### Archive contents will be appended after this file. ###
+END_OF_PYTHON
diff --git a/Lib/test/ziptestdata/testdata_module_inside_zip.py b/Lib/test/ziptestdata/testdata_module_inside_zip.py
new file mode 100644
index 000000000000..6f8dcd6c0e84
--- /dev/null
+++ b/Lib/test/ziptestdata/testdata_module_inside_zip.py
@@ -0,0 +1,2 @@
+# Test data file to be stored within a zip file.
+FAVORITE_NUMBER = 5



More information about the Python-checkins mailing list