From: Wulian <1055917385@qq.com> Date: Mon, 20 Jan 2025 18:12:29 +0000 (+0800) Subject: gh-91279: ZipFile.writestr now respect SOURCE_DATE_EPOCH (#124435) X-Git-Tag: v3.14.0a5~336 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=5d57959d7da8cfcef8df072a6ad53795c60546aa;p=thirdparty%2FPython%2Fcpython.git gh-91279: ZipFile.writestr now respect SOURCE_DATE_EPOCH (#124435) --- diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 7f149d5c03df..0dcecd4944f2 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -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 diff --git a/Lib/test/test_zipfile/test_core.py b/Lib/test/test_zipfile/test_core.py index 02e28d4004c0..ab5fb6500843 100644 --- a/Lib/test/test_zipfile/test_core.py +++ b/Lib/test/test_zipfile/test_core.py @@ -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: diff --git a/Lib/zipfile/__init__.py b/Lib/zipfile/__init__.py index 24531c1c2b78..49c40032d848 100644 --- a/Lib/zipfile/__init__.py +++ b/Lib/zipfile/__init__.py @@ -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 index 000000000000..30ee2ea5efd0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-30-19-53-14.gh-issue-91279.EeOJk1.rst @@ -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.