Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5d57959

Browse files
authored
gh-91279: ZipFile.writestr now respect SOURCE_DATE_EPOCH (#124435)
1 parent dda02eb commit 5d57959

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
@@ -730,6 +730,12 @@ zipinfo
730730

731731
(Contributed by Bénédikt Tran in :gh:`123424`.)
732732

733+
* :meth:`zipfile.ZipFile.writestr` now respect ``SOURCE_DATE_EPOCH`` that
734+
distributions can set centrally and have build tools consume this in order
735+
to produce reproducible output.
736+
737+
(Contributed by Jiahao Li in :gh:`91279`.)
738+
733739
.. Add improved modules above alphabetically, not here at the end.
734740
735741
Optimizations

Lib/test/test_zipfile/test_core.py

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

2222
from test import archiver_tests
23-
from test.support import script_helper
23+
from test.support import script_helper, os_helper
2424
from test.support import (
2525
findfile, requires_zlib, requires_bz2, requires_lzma,
2626
captured_stdout, captured_stderr, requires_subprocess,
@@ -1784,6 +1784,35 @@ def test_writestr_extended_local_header_issue1202(self):
17841784
zinfo.flag_bits |= zipfile._MASK_USE_DATA_DESCRIPTOR # Include an extended local header.
17851785
orig_zip.writestr(zinfo, data)
17861786

1787+
def test_write_with_source_date_epoch(self):
1788+
with os_helper.EnvironmentVarGuard() as env:
1789+
# Set the SOURCE_DATE_EPOCH environment variable to a specific timestamp
1790+
env['SOURCE_DATE_EPOCH'] = "1735715999"
1791+
1792+
with zipfile.ZipFile(TESTFN, "w") as zf:
1793+
zf.writestr("test_source_date_epoch.txt", "Testing SOURCE_DATE_EPOCH")
1794+
1795+
with zipfile.ZipFile(TESTFN, "r") as zf:
1796+
zip_info = zf.getinfo("test_source_date_epoch.txt")
1797+
get_time = time.localtime(int(os.environ['SOURCE_DATE_EPOCH']))[:6]
1798+
# Compare each element of the date_time tuple
1799+
# Allow for a 1-second difference
1800+
for z_time, g_time in zip(zip_info.date_time, get_time):
1801+
self.assertAlmostEqual(z_time, g_time, delta=1)
1802+
1803+
def test_write_without_source_date_epoch(self):
1804+
if 'SOURCE_DATE_EPOCH' in os.environ:
1805+
del os.environ['SOURCE_DATE_EPOCH']
1806+
1807+
with zipfile.ZipFile(TESTFN, "w") as zf:
1808+
zf.writestr("test_no_source_date_epoch.txt", "Testing without SOURCE_DATE_EPOCH")
1809+
1810+
with zipfile.ZipFile(TESTFN, "r") as zf:
1811+
zip_info = zf.getinfo("test_no_source_date_epoch.txt")
1812+
current_time = time.localtime()[:6]
1813+
for z_time, c_time in zip(zip_info.date_time, current_time):
1814+
self.assertAlmostEqual(z_time, c_time, delta=1)
1815+
17871816
def test_close(self):
17881817
"""Check that the zipfile is closed after the 'with' block."""
17891818
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+
epoch = os.environ.get('SOURCE_DATE_EPOCH')
619+
get_time = int(epoch) if epoch else time.time()
620+
self.date_time = time.localtime(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)