Skip to content

Commit 51835c7

Browse files
committed
bpo-29514: Check magic number for micro release
Add a dict importlib.util.EXPECTED_MAGIC_NUMBERS which details the initial and expected pyc magic number for each minor release. This gives a mechanism for users to check if the magic number has changed within a release and for a test to ensure procedure is followed if a change is necessary. Add a test to check the current MAGIC_NUMBER against the expected number for the release if the current release is at candidate or final level. On test failure, describe to the developer the procedure for changing the magic number.
1 parent af88e7e commit 51835c7

File tree

4 files changed

+2277
-2196
lines changed

4 files changed

+2277
-2196
lines changed

Lib/importlib/_bootstrap_external.py

+15
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,21 @@ def _write_atomic(path, data, mode=0o666):
248248
#
249249
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
250250
# in PC/launcher.c must also be updated.
251+
#
252+
# In general, the MAGIC_NUMBER should not change for maintenance releases
253+
# although this may be required under exceptional circumstances. When
254+
# each release reaches candidate status, an entry in EXPECTED_MAGIC_NUMBERS
255+
# should be added for this release. This value is tested against the actual
256+
# MAGIC_NUMBER to ensure that if a change is required within a minor
257+
# release, the exception is first discussed with python-dev and relevant
258+
# community stakeholders such as OS distribution package maintainers
259+
# are properly informed of the change.
260+
261+
EXPECTED_MAGIC_NUMBERS = {
262+
(2, 7): 62211,
263+
(3, 5): 3350,
264+
(3, 6): 3379
265+
}
251266

252267
MAGIC_NUMBER = (3390).to_bytes(2, 'little') + b'\r\n'
253268
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c

Lib/importlib/util.py

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from ._bootstrap import _resolve_name
55
from ._bootstrap import spec_from_loader
66
from ._bootstrap import _find_spec
7+
from ._bootstrap_external import EXPECTED_MAGIC_NUMBERS
78
from ._bootstrap_external import MAGIC_NUMBER
89
from ._bootstrap_external import cache_from_source
910
from ._bootstrap_external import decode_source
+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import importlib.util as util
2+
import sys
3+
import unittest
4+
5+
6+
class ImportModuleReleaseTests(unittest.TestCase):
7+
"""
8+
Test release compatibility issues relating to importlib
9+
"""
10+
def test_magic_number(self):
11+
"""
12+
Each python minor release should generally have a MAGIC_NUMBER
13+
that does not change once the release reaches candidate status.
14+
15+
Once a release reaches candidate status, an entry should be
16+
added to EXPECTED_MAGIC_NUMBERS. This test will then check that
17+
the actual MAGIC_NUMBER matches the expected value for the
18+
release.
19+
20+
In exceptional cases, it may be required to change the MAGIC_NUMBER
21+
for a maintenance release. In this case the change should be
22+
discussed in dev-python. If a change is required, community
23+
stakeholders such as OS package maintainers must be notified
24+
in advance. Such exceptional releases will then require an
25+
adjustment to this test case.
26+
"""
27+
if sys.version_info.releaselevel not in ('final', 'candidate'):
28+
return
29+
30+
release = (sys.version_info.major, sys.version_info.minor)
31+
32+
msg = (
33+
"Candidate and final releases require an entry in "
34+
"importlib.util.EXPECTED_MAGIC_NUMBERS. Set the expected "
35+
"magic number to the current MAGIC_NUMBER to continue "
36+
"with the release."
37+
)
38+
self.assertIn(release, util.EXPECTED_MAGIC_NUMBERS, msg=msg)
39+
expected = util.EXPECTED_MAGIC_NUMBERS[release]
40+
actual = int.from_bytes(util.MAGIC_NUMBER[:2], 'little')
41+
42+
# Adjust for exceptional releases
43+
if release == (3, 5):
44+
expected = 3351 # Changed in 3.5.3 issue 27286
45+
46+
msg = (
47+
"The MAGIC_NUMBER {0} does not match the expected value "
48+
"{1} for release {2}. Changing the MAGIC_NUMBER for a "
49+
"maintenance release requires discussion in dev-python and "
50+
"notification of community stakeholders."
51+
.format(actual, expected, release)
52+
)
53+
self.assertEqual(expected, actual, msg)
54+
55+
56+
if __name__ == '__main__':
57+
unittest.main()

0 commit comments

Comments
 (0)