]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-91279: ZipFile.writestr now respect SOURCE_DATE_EPOCH (#124435)
authorWulian <1055917385@qq.com>
Mon, 20 Jan 2025 18:12:29 +0000 (02:12 +0800)
committerGitHub <noreply@github.com>
Mon, 20 Jan 2025 18:12:29 +0000 (13:12 -0500)
Doc/whatsnew/3.14.rst
Lib/test/test_zipfile/test_core.py
Lib/zipfile/__init__.py
Misc/NEWS.d/next/Library/2024-12-30-19-53-14.gh-issue-91279.EeOJk1.rst [new file with mode: 0644]

index 7f149d5c03dfbbe368074873b1e8bf33a6c9e720..0dcecd4944f2f662f9df736278bd40b7f17f51e5 100644 (file)
@@ -730,6 +730,12 @@ zipinfo
 
   (Contributed by Bénédikt Tran in :gh:`123424`.)
 
+* :meth:`zipfile.ZipFile.writestr` now respect ``SOURCE_DATE_EPOCH`` that
+  distributions can set centrally and have build tools consume this in order
+  to produce reproducible output.
+
+  (Contributed by Jiahao Li in :gh:`91279`.)
+
 .. Add improved modules above alphabetically, not here at the end.
 
 Optimizations
index 02e28d4004c01af5cfc192bcf2733f2c4013cb2f..ab5fb650084327d7fd4426b76871f651f5a8b62d 100644 (file)
@@ -20,7 +20,7 @@ from tempfile import TemporaryFile
 from random import randint, random, randbytes
 
 from test import archiver_tests
-from test.support import script_helper
+from test.support import script_helper, os_helper
 from test.support import (
     findfile, requires_zlib, requires_bz2, requires_lzma,
     captured_stdout, captured_stderr, requires_subprocess,
@@ -1784,6 +1784,35 @@ class OtherTests(unittest.TestCase):
                 zinfo.flag_bits |= zipfile._MASK_USE_DATA_DESCRIPTOR  # Include an extended local header.
                 orig_zip.writestr(zinfo, data)
 
+    def test_write_with_source_date_epoch(self):
+        with os_helper.EnvironmentVarGuard() as env:
+            # Set the SOURCE_DATE_EPOCH environment variable to a specific timestamp
+            env['SOURCE_DATE_EPOCH'] = "1735715999"
+
+            with zipfile.ZipFile(TESTFN, "w") as zf:
+                zf.writestr("test_source_date_epoch.txt", "Testing SOURCE_DATE_EPOCH")
+
+            with zipfile.ZipFile(TESTFN, "r") as zf:
+                zip_info = zf.getinfo("test_source_date_epoch.txt")
+                get_time = time.localtime(int(os.environ['SOURCE_DATE_EPOCH']))[:6]
+                # Compare each element of the date_time tuple
+                # Allow for a 1-second difference
+                for z_time, g_time in zip(zip_info.date_time, get_time):
+                    self.assertAlmostEqual(z_time, g_time, delta=1)
+
+    def test_write_without_source_date_epoch(self):
+        if 'SOURCE_DATE_EPOCH' in os.environ:
+            del os.environ['SOURCE_DATE_EPOCH']
+
+        with zipfile.ZipFile(TESTFN, "w") as zf:
+            zf.writestr("test_no_source_date_epoch.txt", "Testing without SOURCE_DATE_EPOCH")
+
+        with zipfile.ZipFile(TESTFN, "r") as zf:
+            zip_info = zf.getinfo("test_no_source_date_epoch.txt")
+            current_time = time.localtime()[:6]
+            for z_time, c_time in zip(zip_info.date_time, current_time):
+                self.assertAlmostEqual(z_time, c_time, delta=1)
+
     def test_close(self):
         """Check that the zipfile is closed after the 'with' block."""
         with zipfile.ZipFile(TESTFN2, "w") as zipfp:
index 24531c1c2b78045fa751b4cd535d09a3f15f0ab5..49c40032d848f1a085728daff467ccc9786f9ba9 100644 (file)
@@ -614,7 +614,11 @@ class ZipInfo:
 
         Return self.
         """
-        self.date_time = time.localtime(time.time())[:6]
+        # gh-91279: Set the SOURCE_DATE_EPOCH to a specific timestamp
+        epoch = os.environ.get('SOURCE_DATE_EPOCH')
+        get_time = int(epoch) if epoch else time.time()
+        self.date_time = time.localtime(get_time)[:6]
+
         self.compress_type = archive.compression
         self.compress_level = archive.compresslevel
         if self.filename.endswith('/'):  # pragma: no cover
diff --git a/Misc/NEWS.d/next/Library/2024-12-30-19-53-14.gh-issue-91279.EeOJk1.rst b/Misc/NEWS.d/next/Library/2024-12-30-19-53-14.gh-issue-91279.EeOJk1.rst
new file mode 100644 (file)
index 0000000..30ee2ea
--- /dev/null
@@ -0,0 +1,3 @@
+:meth:`zipfile.ZipFile.writestr` now respect ``SOURCE_DATE_EPOCH`` that
+distributions can set centrally and have build tools consume this in order
+to produce reproducible output.