Skip to content

Commit ed25c02

Browse files
author
rocky
committed
Word on magics numbers and routiens
set xdis.magic.minor_release_names list release names. Generally these will be supported. dict xdis.magic.version2magicint[] gives a way to go from version name to magic number. frozenset xdis.INTERIM_MAGIC_INTS gives a (partial) list of used magic numbers that are interim.
1 parent 82fd00f commit ed25c02

File tree

3 files changed

+108
-43
lines changed

3 files changed

+108
-43
lines changed

pytest/test_magic.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
"""
22
Unit test for xdis.magics
33
"""
4+
45
import pytest
5-
from xdis.magics import MAGIC, sysinfo2magic
6+
from xdis.magics import (
7+
INTERIM_MAGIC_INTS,
8+
MAGIC,
9+
magicint2version,
10+
minor_release_names,
11+
sysinfo2magic,
12+
version2magicint,
13+
)
614
from xdis.version_info import PYTHON_VERSION_TRIPLE
715

816

@@ -12,3 +20,15 @@
1220
)
1321
def test_magic():
1422
assert sysinfo2magic() == MAGIC, (sysinfo2magic(), MAGIC)
23+
24+
# Check that our interim version numbers are not used as release numbers.
25+
interim_version_names = {
26+
magicint2version[magic_int] for magic_int in INTERIM_MAGIC_INTS
27+
}
28+
incorrect_interim_names = interim_version_names.intersection(minor_release_names)
29+
if interim_version_names:
30+
for incorrect_name in incorrect_interim_names:
31+
print(f"Remove {incorrect_name} {version2magicint[incorrect_name]}")
32+
assert (
33+
not incorrect_interim_names
34+
), f"Remove magic numbers for {incorrect_interim_names}"

xdis/load.py

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from xdis.dropbox.decrypt25 import fix_dropbox_pyc
3030
from xdis.magics import (
3131
GRAAL3_MAGICS,
32+
INTERIM_MAGIC_INTS,
3233
JYTHON_MAGICS,
3334
PYPY3_MAGICS,
3435
PYTHON_MAGIC_INT,
@@ -251,34 +252,7 @@ def load_module_from_file_object(
251252
version = magicint2version.get(magic_int, "")
252253
raise ImportError(f"Magic int {magic_int} ({version}) is not supported.")
253254

254-
if magic_int in (
255-
3010,
256-
3020,
257-
3030,
258-
3040,
259-
3050,
260-
3060,
261-
3061,
262-
3071,
263-
3361,
264-
3091,
265-
3101,
266-
3103,
267-
3141,
268-
3270,
269-
3280,
270-
3290,
271-
3300,
272-
3320,
273-
3330,
274-
3371,
275-
62071,
276-
62071,
277-
62081,
278-
62091,
279-
62092,
280-
62111,
281-
):
255+
if magic_int in INTERIM_MAGIC_INTS:
282256
raise ImportError(
283257
"%s is interim Python %s (%d) bytecode which is "
284258
"not supported.\nFinal released versions are "

xdis/magics.py

Lines changed: 85 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import re
3636
import struct
3737
import sys
38+
from collections import defaultdict
3839
from importlib.util import MAGIC_NUMBER as MAGIC
3940
from typing import Dict, Set, Tuple
4041

@@ -54,11 +55,44 @@
5455
24881, # RustPython 3.13
5556
)
5657

58+
# A list of interim Python version magic numbers used, but were not
59+
# the *final* major/minor for that release. For example, number 3430
60+
# to 3435 (3.10a1 to 3.10a7) are in this list, but 3438 (the *last* of
61+
# the 3.10b magic number changes), is not. Magic number 3438 used in
62+
# 3.10b was the final release before major/minor release 3.10; it
63+
# is the magic number used in 3.10 releases 3.10.0 to 3.10.19.
64+
65+
# FIXME: complete the below table.
66+
# fmt: off
67+
68+
# The following should be interim and this list.
69+
# The they all seems to be used in Python up to 3.6.15!
70+
# 3378 3.6b Used up to 3.6.15
71+
# 3372 is also 3.6?
72+
# 3379 is 3.7?
73+
# 3230 3.3a4
74+
# 3131 3.0a5 is 3.0.1?
75+
# 3393 3.7?
76+
# 3401 3.8.0a3+?
77+
INTERIM_MAGIC_INTS = frozenset([
78+
3010, 3020, 3030, 3040, 3050, 3060, 3061, 3071, 3081, 3091,
79+
3101, 3103, 3111, 3141, 3160, 3170, 3190, 3200, 3210, 3220,
80+
3250, 3260, 3270, 3280, 3290, 3300, 3320, 3340,
81+
3361, 3371, 3373, 3375, 3376, 3377, 3390, 3391,
82+
3392, 3400, 3410, 3411, 3412, 3420, 3421, 3422, 3423,
83+
3424, 3430, 3431, 3432, 3433, 3435, 3436, 3437, 3438, 3450, 3451,
84+
# ...
85+
62041, 62051, 62071, 62081, 62071, 62091, 62081, 62091, 62092, 62111,
86+
62121, 62121, 62151, 62171, 62181, 62191, 62201,
87+
])
88+
# fmt: ofn
5789

5890
def add_magic_from_int(magic_int: int, version: str) -> None:
5991
magicint2version[magic_int] = version
6092
versions[int2magic(magic_int)] = version
93+
version2magicint[version].append(magic_int)
6194

95+
version2magicint = defaultdict(list)
6296

6397
def int2magic(magic_int: int) -> bytes:
6498
"""Given a magic int like 62211, compute the corresponding magic byte string
@@ -154,11 +188,9 @@ def __by_version(magic_versions: Dict[bytes, str]) -> dict:
154188
add_magic_from_int(62041, "2.4a0")
155189
add_magic_from_int(62051, "2.4a3")
156190
add_magic_from_int(62061, "2.4b1")
157-
add_magic_from_int(62071, "2.5a0")
158-
159-
# ast-branch
160-
add_magic_from_int(62081, "2.5a0")
161191

192+
add_magic_from_int(62071, "2.5a0")
193+
add_magic_from_int(62081, "2.5a0") # ast-branch
162194
add_magic_from_int(62091, "2.5a0") # with
163195
add_magic_from_int(62092, "2.5a0") # changed WITH_CLEANUP opcode
164196
add_magic_from_int(62101, "2.5b3") # fix wrong code: for x, in ...
@@ -416,7 +448,11 @@ def __by_version(magic_versions: Dict[bytes, str]) -> dict:
416448

417449
# Change CALL_xxx opcodes
418450
add_magic_from_int(3466, "3.11a4c")
451+
452+
# Add SEND opcode
419453
add_magic_from_int(3467, "3.11a4d")
454+
455+
# bpo-45711: remove type, traceback from exc_info
420456
add_magic_from_int(3468, "3.11a4e")
421457
add_magic_from_int(3469, "3.11a4f")
422458
add_magic_from_int(3470, "3.11a4g")
@@ -591,19 +627,19 @@ def __by_version(magic_versions: Dict[bytes, str]) -> dict:
591627
# Add oparg to END_ASYNC_FOR
592628
add_magic_from_int(3618, "3.14a6b")
593629

594-
# Renumber RESUME opcode from 149 to 128
630+
# Renumber RESUME opcode from 149 to 128.
595631
add_magic_from_int(3619, "3.14a6c")
596632

597-
# Optimize bytecode for all/any/tuple called on a genexp
633+
# Optimize bytecode for all/any/tuple called on a genexp.
598634
add_magic_from_int(3620, "3.14a7a")
599635

600-
# Optimize LOAD_FAST opcodes into LOAD_FAST_BORROW
636+
# Optimize LOAD_FAST opcodes into LOAD_FAST_BORROW.
601637
add_magic_from_int(3621, "3.14a7b")
602638

603-
# Store annotations in different class dict keys
639+
# Store annotations in different class dict keys.
604640
add_magic_from_int(3622, "3.14a7c")
605641

606-
# Add BUILD_INTERPOLATION & BUILD_TEMPLATE opcodes
642+
# Add BUILD_INTERPOLATION & BUILD_TEMPLATE opcodes.
607643
add_magic_from_int(3623, "3.14a7d")
608644

609645
# Don't optimize LOAD_FAST when local is killed by DELETE_FAST
@@ -612,11 +648,34 @@ def __by_version(magic_versions: Dict[bytes, str]) -> dict:
612648
# Fix handling of opcodes that may leave operands on the stack when optimizing LOAD_FAST
613649
add_magic_from_int(3625, "3.14b3")
614650

651+
# Fix miscompilation of some module-level annotations.
652+
add_magic_from_int(3626, "3.14rc2")
653+
654+
# Fix miscompilation of some module-level annotations.
615655
add_magic_from_int(3627, "3.14rc3")
616656

617-
# add_magic_from_int(3655, "3.15.0a0")
657+
# Initial 3.15 version
658+
add_magic_from_int(3650, "3.15a0")
659+
660+
# Simplify LOAD_CONST.
661+
add_magic_from_int(3651, "3.15a1a")
662+
663+
# Add Virtual iterators.
664+
add_magic_from_int(3652, "3.15a1b")
665+
666+
# Fix handling of opcodes that may leave operands on the stack when
667+
# optimizing LOAD_FAST.
668+
add_magic_from_int(3653, "3.15a1c")
669+
670+
# Fix missing exception handlers in logical expression.
671+
add_magic_from_int(3654, "3.15a1d")
672+
673+
# Fix miscompilation of some module-level annotations.
674+
# add_magic_from_int(3655, "3.15a1e")
618675
# NOTE: this will change on release!
619-
add_magic_from_int(3655, "3.15.0")
676+
add_magic_from_int(3655, "3.15-dev")
677+
678+
# Python 3.16 will start with 3700
620679

621680
# Weird ones
622681
# WTF? Python 3.2.5 and PyPy have weird magic numbers
@@ -703,7 +762,7 @@ def add_canonic_versions(release_versions: str, canonic: str) -> None:
703762
add_canonic_versions(
704763
"3.6b2 3.6 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.6.8 "
705764
"3.6.9 3.6.10 3.6.11 3.6.12 3.6.13 3.6.14 3.6.15",
706-
"3.6rc1",
765+
"3.6rc1", # ?? 3.6b2 is not 3.6rc1!
707766
)
708767

709768
add_canonic_versions("3.7b1", "3.7.0beta3")
@@ -774,8 +833,8 @@ def add_canonic_versions(release_versions: str, canonic: str) -> None:
774833
add_canonic_versions("3.14 3.14.0", "3.14rc3")
775834

776835
add_canonic_versions(
777-
"3.15 3.15.0a1 3.15-dev 3.15.0a0",
778-
"3.15.0",
836+
"3.15 3.15.0 3.15.0a1 3.15.0a0",
837+
"3.15-dev",
779838
)
780839

781840
# The canonic version for a canonic version is itself
@@ -784,6 +843,12 @@ def add_canonic_versions(release_versions: str, canonic: str) -> None:
784843
# A set of all Python versions we know about
785844
python_versions = set(canonic_python_version.keys())
786845

846+
# Python major, minor version names, e.g. 3.6, 3.11pypy, etc.
847+
# These are not considered interim version number.
848+
minor_release_names = {
849+
python_version for python_version in python_versions if re.match("^[1-3][.][0-9]+(?:pypy|Graal)?$", python_version)
850+
}
851+
787852

788853
def __show(text, magic) -> None:
789854
print(text, struct.unpack("BBBB", magic), struct.unpack("<HBB", magic))
@@ -869,6 +934,12 @@ def test() -> None:
869934
print(type(magic_20), len(magic_20), repr(magic_20))
870935
assert sysinfo2magic() == MAGIC, (sysinfo2magic(), MAGIC)
871936

937+
# Check that our interim version numbers are not used as release numbers.
938+
interim_version_names = {magicint2version[magic_int] for magic_int in INTERIM_MAGIC_INTS}
939+
incorrect_interim_names = interim_version_names.intersection(minor_release_names)
940+
if interim_version_names:
941+
for incorrect_name in incorrect_interim_names:
942+
print(f"Remove {incorrect_name} {version2magicint[incorrect_name]}")
872943

873944
if __name__ == "__main__":
874945
test()

0 commit comments

Comments
 (0)