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.
This commit is contained in:
Bernhard M. Wiedemann 2018-01-24 22:26:18 +01:00 committed by Brett Cannon
parent 6f6eb35f9b
commit ccbe5818af
4 changed files with 22 additions and 1 deletions

View File

@ -55,7 +55,9 @@ byte-code cache files in the directory containing the source code.
*invalidation_mode* should be a member of the :class:`PycInvalidationMode` *invalidation_mode* should be a member of the :class:`PycInvalidationMode`
enum and controls how the generated ``.pyc`` files are invalidated at enum and controls how the generated ``.pyc`` files are invalidated at
runtime. runtime. If the :envvar:`SOURCE_DATE_EPOCH` environment variable is set,
*invalidation_mode* will be forced to
:attr:`PycInvalidationMode.CHECKED_HASH`.
.. versionchanged:: 3.2 .. versionchanged:: 3.2
Changed default value of *cfile* to be :PEP:`3147`-compliant. Previous 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.
.. versionchanged:: 3.7 .. versionchanged:: 3.7
The *invalidation_mode* parameter was added as specified in :pep:`552`. The *invalidation_mode* parameter was added as specified in :pep:`552`.
If the :envvar:`SOURCE_DATE_EPOCH` environment variable is set,
*invalidation_mode* will be forced to
:attr:`PycInvalidationMode.CHECKED_HASH`.
.. class:: PycInvalidationMode .. class:: PycInvalidationMode

View File

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

View File

@ -98,6 +98,18 @@ class PyCompileTests(unittest.TestCase):
self.assertFalse(os.path.exists( self.assertFalse(os.path.exists(
importlib.util.cache_from_source(bad_coding))) importlib.util.cache_from_source(bad_coding)))
def test_source_date_epoch(self):
testtime = 123456789
with support.EnvironmentVarGuard() as env:
env["SOURCE_DATE_EPOCH"] = str(testtime)
py_compile.compile(self.source_path, self.pyc_path)
self.assertTrue(os.path.exists(self.pyc_path))
self.assertFalse(os.path.exists(self.cache_path))
with open(self.pyc_path, 'rb') as fp:
flags = importlib._bootstrap_external._classify_pyc(
fp.read(), 'test', {})
self.assertEqual(flags, 0b11)
@unittest.skipIf(sys.flags.optimize > 0, 'test does not work with -O') @unittest.skipIf(sys.flags.optimize > 0, 'test does not work with -O')
def test_double_dot_no_clobber(self): def test_double_dot_no_clobber(self):
# http://bugs.python.org/issue22966 # http://bugs.python.org/issue22966

View File

@ -0,0 +1,2 @@
If the :envvar:`SOURCE_DATE_EPOCH` environment variable is set,
:mod:`py_compile` will always create hash-based ``.pyc`` files.