Skip to content

Commit 693e66f

Browse files
committed
finish
1 parent 81376fe commit 693e66f

File tree

4 files changed

+44
-2
lines changed

4 files changed

+44
-2
lines changed

Doc/whatsnew/3.14.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,12 @@ zipinfo
670670

671671
(Contributed by Bénédikt Tran in :gh:`123424`.)
672672

673+
* :meth:`zipfile.ZipFile.writestr` now respect ``SOURCE_DATE_EPOCH`` that
674+
distributions can set centrally and have build tools consume this in order
675+
to produce reproducible output.
676+
677+
(Contributed by Jiahao Li in :gh:`91279`.)
678+
673679
.. Add improved modules above alphabetically, not here at the end.
674680
675681
Optimizations

Lib/test/test_zipfile/test_core.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from random import randint, random, randbytes
2020

2121
from test import archiver_tests
22-
from test.support import script_helper
22+
from test.support import script_helper, os_helper
2323
from test.support import (
2424
findfile, requires_zlib, requires_bz2, requires_lzma,
2525
captured_stdout, captured_stderr, requires_subprocess
@@ -1781,6 +1781,35 @@ def test_writestr_extended_local_header_issue1202(self):
17811781
zinfo.flag_bits |= zipfile._MASK_USE_DATA_DESCRIPTOR # Include an extended local header.
17821782
orig_zip.writestr(zinfo, data)
17831783

1784+
def test_write_with_source_date_epoch(self):
1785+
with os_helper.EnvironmentVarGuard() as env:
1786+
# Set the SOURCE_DATE_EPOCH environment variable to a specific timestamp
1787+
env['SOURCE_DATE_EPOCH'] = "1727440508"
1788+
1789+
with zipfile.ZipFile(TESTFN, "w") as zf:
1790+
zf.writestr("test_source_date_epoch.txt", "Testing SOURCE_DATE_EPOCH")
1791+
1792+
with zipfile.ZipFile(TESTFN, "r") as zf:
1793+
zip_info = zf.getinfo("test_source_date_epoch.txt")
1794+
get_time = time.gmtime(int(os.environ['SOURCE_DATE_EPOCH']))[:6]
1795+
# Compare each element of the date_time tuple
1796+
# Allow for a 1-second difference
1797+
for z_time, g_time in zip(zip_info.date_time, get_time):
1798+
self.assertAlmostEqual(z_time, g_time, delta=1)
1799+
1800+
def test_write_without_source_date_epoch(self):
1801+
if 'SOURCE_DATE_EPOCH' in os.environ:
1802+
del os.environ['SOURCE_DATE_EPOCH']
1803+
1804+
with zipfile.ZipFile(TESTFN, "w") as zf:
1805+
zf.writestr("test_no_source_date_epoch.txt", "Testing without SOURCE_DATE_EPOCH")
1806+
1807+
with zipfile.ZipFile(TESTFN, "r") as zf:
1808+
zip_info = zf.getinfo("test_no_source_date_epoch.txt")
1809+
current_time = time.gmtime()[:6]
1810+
for z_time, c_time in zip(zip_info.date_time, current_time):
1811+
self.assertAlmostEqual(z_time, c_time, delta=1)
1812+
17841813
def test_close(self):
17851814
"""Check that the zipfile is closed after the 'with' block."""
17861815
with zipfile.ZipFile(TESTFN2, "w") as zipfp:

Lib/zipfile/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,11 @@ def _for_archive(self, archive: ZipFile) -> Self:
614614
615615
Return self.
616616
"""
617-
self.date_time = time.localtime(time.time())[:6]
617+
# gh-91279: Set the SOURCE_DATE_EPOCH to a specific timestamp
618+
self.epoch = os.environ.get('SOURCE_DATE_EPOCH')
619+
self.get_time = int(self.epoch) if self.epoch else time.time()
620+
self.date_time = time.gmtime(self.get_time)[:6]
621+
618622
self.compress_type = archive.compression
619623
self.compress_level = archive.compresslevel
620624
if self.filename.endswith('/'): # pragma: no cover
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:meth:`zipfile.ZipFile.writestr` now respect ``SOURCE_DATE_EPOCH`` that
2+
distributions can set centrally and have build tools consume this in order
3+
to produce reproducible output.

0 commit comments

Comments
 (0)