Skip to content

Commit 3911731

Browse files
ammaraskarmatrixise
andcommitted
bpo-34990: Treat the pyc header's mtime in compileall as an unsigned int
Co-authored-by: Stéphane Wirtel <[email protected]>
1 parent 12446e6 commit 3911731

File tree

4 files changed

+33
-5
lines changed

4 files changed

+33
-5
lines changed

Lib/compileall.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,8 @@ def compile_file(fullname, ddir=None, force=False, rx=None, quiet=0,
206206
if not force:
207207
try:
208208
mtime = int(os.stat(fullname).st_mtime)
209-
expect = struct.pack('<4sll', importlib.util.MAGIC_NUMBER,
210-
0, mtime)
209+
expect = struct.pack('<4sLL', importlib.util.MAGIC_NUMBER,
210+
0, mtime & 0xFFFF_FFFF)
211211
for cfile in opt_cfiles.values():
212212
with open(cfile, 'rb') as chandle:
213213
actual = chandle.read(12)

Lib/test/test_compileall.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,21 @@ def timestamp_metadata(self):
5555
with open(self.bc_path, 'rb') as file:
5656
data = file.read(12)
5757
mtime = int(os.stat(self.source_path).st_mtime)
58-
compare = struct.pack('<4sll', importlib.util.MAGIC_NUMBER, 0, mtime)
58+
compare = struct.pack('<4sLL', importlib.util.MAGIC_NUMBER, 0,
59+
mtime & 0xFFFF_FFFF)
5960
return data, compare
6061

62+
def test_year_2038_mtime_compilation(self):
63+
# Test to make sure we can handle mtimes larger than what a 32-bit
64+
# signed number can hold as part of bpo-34990
65+
with open(self.source_path, 'r') as f:
66+
os.utime(f.name, (2**32 - 1, 2**32 - 1))
67+
self.assertTrue(compileall.compile_file(self.source_path))
68+
69+
with open(self.source_path, 'r') as f:
70+
os.utime(f.name, (2**35, 2**35))
71+
self.assertTrue(compileall.compile_file(self.source_path))
72+
6173
def recreation_check(self, metadata):
6274
"""Check that compileall recreates bytecode when the new metadata is
6375
used."""
@@ -76,7 +88,7 @@ def recreation_check(self, metadata):
7688

7789
def test_mtime(self):
7890
# Test a change in mtime leads to a new .pyc.
79-
self.recreation_check(struct.pack('<4sll', importlib.util.MAGIC_NUMBER,
91+
self.recreation_check(struct.pack('<4sLL', importlib.util.MAGIC_NUMBER,
8092
0, 1))
8193

8294
def test_magic_number(self):

Lib/test/test_zipimport.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ def make_pyc(co, mtime, size):
4141
else:
4242
mtime = int(-0x100000000 + int(mtime))
4343
pyc = (importlib.util.MAGIC_NUMBER +
44-
struct.pack("<iii", 0, int(mtime), size & 0xFFFFFFFF) + data)
44+
struct.pack("<iLL", 0,
45+
int(mtime) & 0xFFFF_FFFF, size & 0xFFFF_FFFF) + data)
4546
return pyc
4647

4748
def module_path_to_dotted_name(path):
@@ -253,6 +254,19 @@ def testBadMTime(self):
253254
TESTMOD + pyc_ext: (NOW, badtime_pyc)}
254255
self.doTest(".py", files, TESTMOD)
255256

257+
def test2038MTime(self):
258+
# Make sure we can handle mtimes larger than what a 32-bit signed number
259+
# can hold.
260+
twenty_thirty_eight_pyc = make_pyc(test_co, 2**32 - 1, len(test_src))
261+
files = {TESTMOD + ".py": (NOW, test_src),
262+
TESTMOD + pyc_ext: (NOW, twenty_thirty_eight_pyc)}
263+
self.doTest(".py", files, TESTMOD)
264+
265+
larger_than_32_bit = make_pyc(test_co, 2**35, len(test_src))
266+
files = {TESTMOD + ".py": (NOW, test_src),
267+
TESTMOD + pyc_ext: (NOW, twenty_thirty_eight_pyc)}
268+
self.doTest(".py", files, TESTMOD)
269+
256270
def testPackage(self):
257271
packdir = TESTPACK + os.sep
258272
files = {packdir + "__init__" + pyc_ext: (NOW, test_pyc),
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fixed a Y2k38 bug in the compileall module where it would fail to compile
2+
files created after 2038.

0 commit comments

Comments
 (0)