# (C) Copyright 2018-2020 by Rocky Bernstein # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. """ Everything you ever wanted to know about Python versions and their magic numbers. And a little bit more... by_magic: in this dictionary, the key is a magic byte string like # b'\x03\xf3\r\n' and its value is a canonic version string, like # '2.7' by_version: in this dictionary, the key is a canonic version string like '2.7, and its value is a magic byte string like b'\x03\xf3\r\n' canonic name, like '2.7' magicint2version: in this dictionary, the key is an magic integer, e.g. 62211, and the value is its canonic versions string, e.g. '2.7' PYTHON_MAGIC_INT: The magic integer for the current running Python interpreter """ import re, struct, sys from xdis import IS_PYPY IS_PYPY3 = (48, 64, 112, 160, 192) def add_magic_from_int(magic_int, version): magicint2version[magic_int] = version versions[int2magic(magic_int)] = version def int2magic(magic_int): """Given a magic int like 62211, compute the corresponding magic byte string b'\x03\xf3\r\n' using the conversion method that does this. See also dictionary magic2nt2version which has precomputed these values for knonwn magic_int's. """ if magic_int in (39170, 39171): return struct.pack("= (3, 0): return struct.pack( "= (3, 4): import importlib MAGIC = importlib.util.MAGIC_NUMBER PYTHON_MAGIC_INT = magic2int(MAGIC) else: import imp MAGIC = imp.get_magic() PYTHON_MAGIC_INT = magic2int(MAGIC) # The magic word is used to reject .pyc files generated by other # Python versions. It should change for each incompatible change to # the bytecode. # # The value of CR and LF is incorporated so if you ever read or write # a .pyc file in text mode the magic number will be wrong; also, the # Apple MPW compiler swaps their values, botching string constants. # # The magic numbers must be spaced apart at least 2 values, as the # -U interpeter flag will cause MAGIC+1 being used. They have been # odd numbers for some time now. # # There were a variety of old schemes for setting the magic number. # The current working scheme is to increment the previous value by # 10. # # Starting with the adoption of PEP 3147 in Python 3.2, every bump in magic # number also includes a new "magic tag", i.e. a human readable string used # to represent the magic number in __pycache__ directories. When you change # the magic number, you must also set a new unique magic tag. Generally this # can be named after the Python major version of the magic number bump, but # it can really be anything, as long as it's different than anything else # that's come before. The tags are included in the following table, starting # with Python 3.2a0. # The below is taken from from Python/import.c, and # Lib/importlib/_bootstrap.py and other sources # magic, canonic version number add_magic_from_int(39170, "1.0") add_magic_from_int(39171, "1.1") # covers 1.2 as well add_magic_from_int(11913, "1.3") add_magic_from_int(5892, "1.4") # 1.5, 1.5.1, 1.5.2 add_magic_from_int(20121, "1.5") # 1.5.1, 1.5.2 add_magic_from_int(50428, "1.6") # 1.6 add_magic_from_int(50823, "2.0") # 2.0, 2.0.1 add_magic_from_int(60202, "2.1") # 2.1, 2.1.1, 2.1.2 add_magic_from_int(60717, "2.2") # 2.2 # Two magics one version! add_magic_from_int(62011, "2.3a0") add_magic_from_int(62021, "2.3a0") # two distinct magics for the same release add_magic_from_int(62041, "2.4a0") add_magic_from_int(62051, "2.4a3") add_magic_from_int(62061, "2.4b1") add_magic_from_int(62071, "2.5a0") add_magic_from_int(62081, "2.5a0") # ast-branch add_magic_from_int(62091, "2.5a0") # with add_magic_from_int(62092, "2.5a0") # changed WITH_CLEANUP opcode add_magic_from_int(62101, "2.5b3") # fix wrong code: for x, in ... add_magic_from_int(62111, "2.5b3") # fix wrong code: x += yield # Fix wrong lnotab with for loops and storing constants that should # have been removed add_magic_from_int(62121, "2.5c1") # Fix wrong code: "for x, in ..." in listcomp/genexp) add_magic_from_int(62131, "2.5c2") # Dropbox-modified Python 2.5 used in versions 1.1x and before of Dropbox add_magic_from_int(62135, "2.5dropbox") add_magic_from_int(62151, "2.6a0") # peephole optimizations & STORE_MAP add_magic_from_int(62161, "2.6a1") # WITH_CLEANUP optimization # Optimize list comprehensions/change LIST_APPEND add_magic_from_int(62171, "2.7a0") # Optimize conditional branches: introduce POP_JUMP_IF_FALSE and # POP_JUMP_IF_TRUE add_magic_from_int(62181, "2.7a0+1") add_magic_from_int(62191, "2.7a0+2") # introduce SETUP_WITH add_magic_from_int(62201, "2.7a0+3") # introduce BUILD_SET add_magic_from_int(62211, "2.7") # introduce MAP_ADD and SET_ADD add_magic_from_int(2657, "2.7pyston-0.6.1") # PyPy including pypy-2.6.1, pypy-5.0.1 PyPy adds 7 to the corresponding CPython nmber add_magic_from_int(62211 + 7, "2.7pypy") add_magic_from_int(3000, "3.000") add_magic_from_int(3010, "3.000+1") # removed UNARY_CONVERT add_magic_from_int(3020, "3.000+2") # added BUILD_SET add_magic_from_int(3030, "3.000+3") # added keyword-only parameters add_magic_from_int(3040, "3.000+4") # added signature annotations add_magic_from_int(3050, "3.000+5") # print becomes a function add_magic_from_int(3060, "3.000+6") # PEP 3115 metaclass syntax add_magic_from_int(3061, "3.000+7") # string literals become unicode add_magic_from_int(3071, "3.000+8") # PEP 3109 raise changes add_magic_from_int(3081, "3.000+9") # PEP 3137 make __file__ and __name__ unicode add_magic_from_int(3091, "3.000+10") # kill str8 interning add_magic_from_int(3101, "3.000+11") # merge from 2.6a0, see 62151 add_magic_from_int(3103, "3.000+12") # __file__ points to source file add_magic_from_int(3111, "3.0a4") # WITH_CLEANUP optimization add_magic_from_int(3131, "3.0a5") # lexical exception stacking, including POP_EXCEPT) add_magic_from_int(3141, "3.1a0") # optimize list, set and dict comprehensions add_magic_from_int(3151, "3.1a0+") # optimize conditional branches add_magic_from_int(3160, "3.2a0") # add SETUP_WITH add_magic_from_int(3170, "3.2a1") # add DUP_TOP_TWO, remove DUP_TOPX and ROT_FOUR add_magic_from_int(3180, "3.2a2") # 3.2a2 (add DELETE_DEREF) # Python 3.2.5 - PyPy 2.3.4 PyPy adds 7 to the corresponding CPython # number add_magic_from_int(3180 + 7, "3.2pypy") add_magic_from_int(3190, "3.3a0") # __class__ super closure changed add_magic_from_int(3200, "3.3a0+") # __qualname__ added add_magic_from_int(3220, "3.3a1") # changed PEP 380 implementation # Added size modulo 2**32 to the pyc header # NOTE: 3.3a2 is our name, other places call it 3.3 # but most 3.3 versions are 3.3a4 which comes next. # FIXME: figure out what the history is and # what the right thing to do if this isn't it. add_magic_from_int(3210, "3.3a2") add_magic_from_int(3230, "3.3a4") # revert changes to implicit __class__ closure # Evaluate positional default arg keyword-only defaults) add_magic_from_int(3250, "3.4a1") # Add LOAD_CLASSDEREF; add_magic_from_int locals, f class to override free vars add_magic_from_int(3260, "3.4a1+1") add_magic_from_int(3270, "3.4a1+2") # various tweaks to the __class__ closure add_magic_from_int(3280, "3.4a1+3") # remove implicit class argument add_magic_from_int(3290, "3.4a4") # changes to __qualname__ computation add_magic_from_int(3300, "3.4a4+") # more changes to __qualname__ computation add_magic_from_int(3310, "3.4rc2") # alter __qualname__ computation add_magic_from_int(3320, "3.5a0") # matrix multiplication operator add_magic_from_int(3330, "3.5b1") # pep 448: additional unpacking generalizations add_magic_from_int(3340, "3.5b2") # fix dictionary display evaluation order #11205 add_magic_from_int(3350, "3.5") # add GET_YIELD_FROM_ITER opcode #24400 (also 3.5b2) add_magic_from_int( 3351, "3.5.2" ) # fix BUILD_MAP_UNPACK_WITH_CALL opcode #27286; 3.5.3, 3.5.4, 3.5.5 add_magic_from_int(3360, "3.6a0") # add FORMAT_VALUE opcode #25483 add_magic_from_int(3361, "3.6a0+1") # lineno delta of code.co_lnotab becomes signed add_magic_from_int(3370, "3.6a1") # 16 bit wordcode add_magic_from_int(3371, "3.6a1+1") # add BUILD_CONST_KEY_MAP opcode #27140 add_magic_from_int( 3372, "3.6a1+2" ) # MAKE_FUNCTION simplification, remove MAKE_CLOSURE #27095 add_magic_from_int(3373, "3.6b1") # add BUILD_STRING opcode #27078 add_magic_from_int( 3375, "3.6b1+1" ) # add SETUP_ANNOTATIONS and STORE_ANNOTATION opcodes #27985 add_magic_from_int( 3376, "3.6b1+2" ) # simplify CALL_FUNCTIONs & BUILD_MAP_UNPACK_WITH_CALL add_magic_from_int(3377, "3.6b1+3") # set __class__ cell from type.__new__ #23722 add_magic_from_int(3378, "3.6b2") # add BUILD_TUPLE_UNPACK_WITH_CALL #28257 add_magic_from_int(3379, "3.6rc1") # more thorough __class__ validation #23722 add_magic_from_int(3390, "3.7.0alpha0") add_magic_from_int(3391, "3.7.0alpha3") # Initial PEP 552 - Deterministic pycs #31650 # Additional word in header and possibly no timestamp add_magic_from_int(3392, "3.7.0beta2") # Final PEP 552: timestamp + size field or no timestamp + SipHash # remove STORE_ANNOTATION opcode #3255 add_magic_from_int(3393, "3.7.0beta3") # restored docstring as the first stmt in the body; this might # affected the first line number #32911) add_magic_from_int(3394, "3.7.0") # move frame block handling to compiler #17611 add_magic_from_int(3400, "3.8.0a1") # add END_ASYNC_FOR #33041 add_magic_from_int(3401, "3.8.0a3+") # PEP570 Python Positional-Only Parameters #36540 add_magic_from_int(3410, "3.8.0a1+") # Reverse evaluation order of key: value in dict comprehensions # #35224 add_magic_from_int(3411, "3.8.0a1+") # Swap the position of positional args and positional only args in # ast.arguments #37593) add_magic_from_int(3412, "3.8.0beta2") # Fix "break" and "continue" in "finally" #37830 add_magic_from_int(3413, "3.8.0rc1+") add_magic_from_int(3422, "3.9.0alpha1") # Weird ones # WTF? Python 3.2.5 and PyPy have weird magic numbers add_magic_from_int(48, "3.2a2") add_magic_from_int(64, "3.3pypy") add_magic_from_int(112, "3.5pypy") # pypy3.5-c-jit-latest add_magic_from_int(160, "3.6.1pypy") # '3.6.1 ... PyPy 7.1.0-beta0' add_magic_from_int(192, "3.6pypy") # '3.6.9 ... PyPy 7.1.0-beta0' add_magic_from_int(1011, "2.7.1b3Jython") # jython add_magic_from_int(22138, "2.7.7Pyston") # 2.7.8pyston, pyston-0.6.0 magics = __by_version(versions) # From a Python version given in sys.info, e.g. 3.6.1, # what is the "canonic" version number, e.g. '3.6.0rc1' canonic_python_version = {} def add_canonic_versions(versions, canonic): for v in versions.split(): canonic_python_version[v] = canonic magics[v] = magics[canonic] try: magics[float(v)] = magics[canonic] except: pass pass return add_canonic_versions("1.5.1 1.5.2", "1.5") add_canonic_versions("2.0.1", "2.0") add_canonic_versions("2.1.1 2.1.2 2.1.3", "2.1") add_canonic_versions("2.2.3", "2.2") add_canonic_versions("2.3 2.3.7", "2.3a0") add_canonic_versions("2.4 2.4.0 2.4.1 2.4.2 2.4.3 2.4.5 2.4.6", "2.4b1") add_canonic_versions("2.5 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6", "2.5c2") add_canonic_versions("2.6 2.6.6 2.6.7 2.6.8 2.6.9", "2.6a1") add_canonic_versions( "2.7.0 2.7.1 2.7.2 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 " "2.7.8 2.7.9 2.7.10 2.7.11 2.7.12 2.7.13 2.7.14 2.7.15 " "2.7.15candidate1 " "2.7.16 " "2.7.17rc1 2.7.17candidate1 2.7.17 2.7.18", "2.7", ) add_canonic_versions("3.0 3.0.0 3.0.1", "3.0a5") add_canonic_versions("3.1 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5", "3.1a0+") add_canonic_versions("3.2 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6", "3.2a2") add_canonic_versions( "3.3 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7rc1 3.3.7", "3.3a4" ) add_canonic_versions( "3.4 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.6 3.4.7 3.4.8 3.4.9 3.4.10", "3.4rc2" ) add_canonic_versions("3.5 3.5.0 3.5.1", "3.5") add_canonic_versions("3.5.2 3.5.3 3.5.4 3.5.5 3.5.6 3.5.7 3.5.8 3.5.9", "3.5.2") add_canonic_versions( "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 3.6.9 3.6.10", "3.6rc1", ) add_canonic_versions("3.7b1", "3.7.0beta3") add_canonic_versions("3.8a1", "3.8.0beta2") add_canonic_versions("2.7.10pypy 2.7.13pypy", "2.7pypy") add_canonic_versions("2.7.3b0Jython", "2.7.1b3Jython") add_canonic_versions("3.2.5pypy", "3.2pypy") add_canonic_versions("3.3.5pypy", "3.3pypy") add_canonic_versions("3.5.3pypy", "3.5pypy") add_canonic_versions("3.6.9pypy", "3.6pypy") add_canonic_versions("2.7.8Pyston", "2.7.7Pyston") add_canonic_versions("3.7.0alpha3", "3.7.0alpha3") add_canonic_versions( "3.7 3.7.0beta5 3.7.1 3.7.2 3.7.3 3.7.4 3.7.5 3.7.6 3.7.7", "3.7.0" ) add_canonic_versions("3.8.0alpha0 3.8.0alpha3 3.8.0a0", "3.8.0a3+") add_canonic_versions("3.8b4 3.8.0candidate1 3.8 3.8.0 3.8.1 3.8.2 3.8.3", "3.8.0rc1+") add_canonic_versions( "3.9 3.9.0 3.9.0a1+ 3.9.0a2+ 3.9.0alpha1 3.9.0alpha2", "3.9.0alpha1" ) # The canonic version for a canonic version is itself for v in versions.values(): canonic_python_version[v] = v # A set of all Python versions we know about python_versions = set(canonic_python_version.keys()) def __show(text, magic): print(text, struct.unpack("BBBB", magic), struct.unpack("