Skip to content

Commit 0a6335e

Browse files
gh-120932: Check the pyc file mtime at import
1 parent e6264b4 commit 0a6335e

1 file changed

Lines changed: 21 additions & 5 deletions

File tree

Lib/importlib/_bootstrap_external.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import _imp
2727
import _io
2828
import sys
29+
import time
2930
import _warnings
3031
import marshal
3132

@@ -725,8 +726,8 @@ def _classify_pyc(data, name, exc_details):
725726
return flags
726727

727728

728-
def _validate_timestamp_pyc(data, source_mtime, source_size, name,
729-
exc_details):
729+
def _validate_timestamp_pyc(self, data, source_mtime, source_size, name,
730+
bytecode_path, exc_details):
730731
"""Validate a pyc against the source last-modified time.
731732
732733
*data* is the contents of the pyc file. (Only the first 16 bytes are
@@ -738,19 +739,32 @@ def _validate_timestamp_pyc(data, source_mtime, source_size, name,
738739
739740
*name* is the name of the module being imported. It is used for logging.
740741
742+
*bytecode_path* is the path of the pyc file.
743+
741744
*exc_details* is a dictionary passed to ImportError if it raised for
742745
improved debugging.
743746
744747
An ImportError is raised if the bytecode is stale.
745748
746749
"""
747-
if _unpack_uint32(data[8:12]) != (source_mtime & 0xFFFFFFFF):
750+
timestamp = _unpack_uint32(data[8:12])
751+
if timestamp != (int(source_mtime) & 0xFFFFFFFF):
748752
message = f'bytecode is stale for {name!r}'
749753
_bootstrap._verbose_message('{}', message)
750754
raise ImportError(message, **exc_details)
751755
if (source_size is not None and
752756
_unpack_uint32(data[12:16]) != (source_size & 0xFFFFFFFF)):
753757
raise ImportError(f'bytecode is stale for {name!r}', **exc_details)
758+
if time.time() - source_mtime < 2:
759+
try:
760+
bytecode_mtime = self.path_mtime(bytecode_path)
761+
except OSError:
762+
pass
763+
else:
764+
if bytecode_mtime < source_mtime:
765+
message = f'bytecode may be stale for {name!r}'
766+
_bootstrap._verbose_message('{}', message)
767+
raise ImportError(message, **exc_details)
754768

755769

756770
def _validate_hash_pyc(data, source_hash, name, exc_details):
@@ -794,7 +808,7 @@ def _code_to_timestamp_pyc(code, mtime=0, source_size=0):
794808
"Produce the data for a timestamp-based pyc."
795809
data = bytearray(MAGIC_NUMBER)
796810
data.extend(_pack_uint32(0))
797-
data.extend(_pack_uint32(mtime))
811+
data.extend(_pack_uint32(int(mtime)))
798812
data.extend(_pack_uint32(source_size))
799813
data.extend(marshal.dumps(code))
800814
return data
@@ -1111,7 +1125,7 @@ def get_code(self, fullname):
11111125
except OSError:
11121126
pass
11131127
else:
1114-
source_mtime = int(st['mtime'])
1128+
source_mtime = st['mtime']
11151129
try:
11161130
data = self.get_data(bytecode_path)
11171131
except OSError:
@@ -1139,10 +1153,12 @@ def get_code(self, fullname):
11391153
exc_details)
11401154
else:
11411155
_validate_timestamp_pyc(
1156+
self,
11421157
data,
11431158
source_mtime,
11441159
st['size'],
11451160
fullname,
1161+
bytecode_path,
11461162
exc_details,
11471163
)
11481164
except (ImportError, EOFError):

0 commit comments

Comments
 (0)