Skip to content

Commit ccbe581

Browse files
bmwiedemannbrettcannon
authored andcommitted
bpo-29708: Setting SOURCE_DATE_EPOCH forces hash-based .pyc files (GH-5200)
To support reproducible builds, the setting of of SOURCE_DATE_EPOCH triggers the py_compile module -- and by extension, compileall -- to forcibly compile with hash-based .pyc files. This eliminates the possibility of timestamp-based .pyc files which vary between builds.
1 parent 6f6eb35 commit ccbe581

File tree

4 files changed

+22
-1
lines changed

4 files changed

+22
-1
lines changed

Doc/library/py_compile.rst

+6-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ byte-code cache files in the directory containing the source code.
5555

5656
*invalidation_mode* should be a member of the :class:`PycInvalidationMode`
5757
enum and controls how the generated ``.pyc`` files are invalidated at
58-
runtime.
58+
runtime. If the :envvar:`SOURCE_DATE_EPOCH` environment variable is set,
59+
*invalidation_mode* will be forced to
60+
:attr:`PycInvalidationMode.CHECKED_HASH`.
5961

6062
.. versionchanged:: 3.2
6163
Changed default value of *cfile* to be :PEP:`3147`-compliant. Previous
@@ -71,6 +73,9 @@ byte-code cache files in the directory containing the source code.
7173

7274
.. versionchanged:: 3.7
7375
The *invalidation_mode* parameter was added as specified in :pep:`552`.
76+
If the :envvar:`SOURCE_DATE_EPOCH` environment variable is set,
77+
*invalidation_mode* will be forced to
78+
:attr:`PycInvalidationMode.CHECKED_HASH`.
7479

7580

7681
.. class:: PycInvalidationMode

Lib/py_compile.py

+2
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ def compile(file, cfile=None, dfile=None, doraise=False, optimize=-1,
112112
the resulting file would be regular and thus not the same type of file as
113113
it was previously.
114114
"""
115+
if os.environ.get('SOURCE_DATE_EPOCH'):
116+
invalidation_mode = PycInvalidationMode.CHECKED_HASH
115117
if cfile is None:
116118
if optimize >= 0:
117119
optimization = optimize if optimize >= 1 else ''

Lib/test/test_py_compile.py

+12
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,18 @@ def test_bad_coding(self):
9898
self.assertFalse(os.path.exists(
9999
importlib.util.cache_from_source(bad_coding)))
100100

101+
def test_source_date_epoch(self):
102+
testtime = 123456789
103+
with support.EnvironmentVarGuard() as env:
104+
env["SOURCE_DATE_EPOCH"] = str(testtime)
105+
py_compile.compile(self.source_path, self.pyc_path)
106+
self.assertTrue(os.path.exists(self.pyc_path))
107+
self.assertFalse(os.path.exists(self.cache_path))
108+
with open(self.pyc_path, 'rb') as fp:
109+
flags = importlib._bootstrap_external._classify_pyc(
110+
fp.read(), 'test', {})
111+
self.assertEqual(flags, 0b11)
112+
101113
@unittest.skipIf(sys.flags.optimize > 0, 'test does not work with -O')
102114
def test_double_dot_no_clobber(self):
103115
# http://bugs.python.org/issue22966
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
If the :envvar:`SOURCE_DATE_EPOCH` environment variable is set,
2+
:mod:`py_compile` will always create hash-based ``.pyc`` files.

0 commit comments

Comments
 (0)