Is there any way to know by which Python version the .pyc
file was compiled?

- 6,661
- 7
- 48
- 63

- 38,793
- 23
- 126
- 164
6 Answers
The first two bytes of the .pyc
file are the magic number that tells the version of the bytecodes. The word is stored in little-endian format, and the known values are:
Python version | Decimal | Hexadecimal | Comment |
---|---|---|---|
Python 1.5 | 20121 | 0x994e | |
Python 1.5.1 | 20121 | 0x994e | |
Python 1.5.2 | 20121 | 0x994e | |
Python 1.6 | 50428 | 0x4cc4 | |
Python 2.0 | 50823 | 0x87c6 | |
Python 2.0.1 | 50823 | 0x87c6 | |
Python 2.1 | 60202 | 0x2aeb | |
Python 2.1.1 | 60202 | 0x2aeb | |
Python 2.1.2 | 60202 | 0x2aeb | |
Python 2.2 | 60717 | 0x2ded | |
Python 2.3a0 | 62011 | 0x3bf2 | |
Python 2.3a0 | 62021 | 0x45f2 | |
Python 2.3a0 | 62011 | 0x3bf2 | ! |
Python 2.4a0 | 62041 | 0x59f2 | |
Python 2.4a3 | 62051 | 0x63f2 | |
Python 2.4b1 | 62061 | 0x6df2 | |
Python 2.5a0 | 62071 | 0x77f2 | |
Python 2.5a0 | 62081 | 0x81f2 | ast-branch |
Python 2.5a0 | 62091 | 0x8bf2 | with |
Python 2.5a0 | 62092 | 0x8cf2 | changed WITH_CLEANUP opcode |
Python 2.5b3 | 62101 | 0x95f2 | fix wrong code: for x, in ... |
Python 2.5b3 | 62111 | 0x9ff2 | fix wrong code: x += yield |
Python 2.5c1 | 62121 | 0xa9f2 | fix wrong lnotab with for loops and storing constants that should have been removed |
Python 2.5c2 | 62131 | 0xb3f2 | fix wrong code: for x, in ... in listcomp/genexp |
Python 2.6a0 | 62151 | 0xc7f2 | peephole optimizations and STORE_MAP opcode |
Python 2.6a1 | 62161 | 0xd1f2 | WITH_CLEANUP optimization |
Python 2.7a0 | 62171 | 0xdbf2 | optimize list comprehensions/change LIST_APPEND |
Python 2.7a0 | 62181 | 0xe5f2 | optimize conditional branches: introduce POP_JUMP_IF_FALSE and POP_JUMP_IF_TRUE |
Python 2.7a0 | 62191 | 0xeff2 | introduce SETUP_WITH |
Python 2.7a0 | 62201 | 0xf9f2 | introduce BUILD_SET |
Python 2.7a0 | 62211 | 0x03f3 | introduce MAP_ADD and SET_ADD |
Python 3000 | 3000 | 0xb80b | |
3010 | 0xc20b | removed UNARY_CONVERT |
|
3020 | 0xcc0b | added BUILD_SET |
|
3030 | 0xd60b | added keyword-only parameters | |
3040 | 0xe00b | added signature annotations | |
3050 | 0xea0b | print becomes a function |
|
3060 | 0xf40b | PEP 3115 metaclass syntax | |
3061 | 0xf50b | string literals become unicode | |
3071 | 0xff0b | PEP 3109 raise changes | |
3081 | 0x090c | PEP 3137 make __file__ and __name__ unicode |
|
3091 | 0x130c | kill str8 interning | |
3101 | 0x1d0c | merge from 2.6a0, see 62151 | |
3103 | 0x1f0c | __file__ points to source file |
|
Python 3.0a4 | 3111 | 0x270c | WITH_CLEANUP optimization |
Python 3.0a5 | 3131 | 0x3b0c | lexical exception stacking, including POP_EXCEPT |
Python 3.1a0 | 3141 | 0x450c | optimize list, set and dict comprehensions: change LIST_APPEND and SET_ADD , add MAP_ADD |
Python 3.1a0 | 3151 | 0x4f0c | optimize conditional branches: introduce POP_JUMP_IF_FALSE and POP_JUMP_IF_TRUE |
Python 3.2a0 | 3160 | 0x580c | add SETUP_WITH , tag: cpython-32 |
Python 3.2a1 | 3170 | 0x620c | add DUP_TOP_TWO , remove DUP_TOPX and ROT_FOUR , tag: cpython-32 |
Python 3.2a2 | 3180 | 0x6c0c | add DELETE_DEREF |
Sources:
- Python/import.c - merged by aix from Python 2.7.2 and Python 3.2.2
- Little endian hex values for comparison first two bytes of Igor Popov's method added by jimbob
-
2`@aix` also a nice answer, but usually a programmatic solution is easy to understand as `@Popov` gave. Thanks indeed. – Aamir Rind Oct 18 '11 at 13:31
-
5@AamirAdnan: But what if you don't happen to have the python version that created the .pyc file? – Cascabel Oct 18 '11 at 16:32
-
No problem. Your answer was great and now its a simple table lookup using `with open('test51.pyc') as f: magic=f.read(2); magic.encode('hex')`. – dr jimbob Oct 18 '11 at 16:41
-
1`@Jefromi` Oh yeah what a point man, in that case `@Popov` solution `Fails` then. As it will never matched with the `python version hex code` to the `hex code of first two bytes` of `.pyc` file. And i will never know what was the version. And eventually i have to go to `@aix` solution for Python Version lookup. Am i right?:) – Aamir Rind Oct 18 '11 at 16:47
-
@AamirAdnan: Right, the other solution tackles a slightly different problem, namely: "is this `.pyc` file compatible with the Python version that I am running?" Whether this what you want, I don't know. – NPE Oct 18 '11 at 16:59
-
2updated magic number list @ https://raw.githubusercontent.com/google/pytype/master/pytype/pyc/magic.py – evandrix Jan 03 '19 at 07:08
You can get the magic number of your Python as follows:
$ python -V
Python 2.6.2
# python
>>> import imp
>>> imp.get_magic().encode('hex')
'd1f20d0a'
To get the magic number for a pyc file you can do the following:
>>> f = open('test25.pyc')
>>> magic = f.read(4)
>>> magic.encode('hex')
'b3f20d0a'
>>> f = open('test26.pyc')
>>> magic = f.read(4)
>>> magic.encode('hex')
'd1f20d0a'
By comparing the magic numbers you'll know the python version that generated the pyc file.

- 9,795
- 7
- 55
- 68
-
3to compare the magic word written as an integer in [Python source](https://github.com/python/cpython/blob/3460198f6ba40a839f105c381f07179aba1e8c61/Lib/importlib/_bootstrap_external.py#L131) with `imp.get_magic()` bytes (Python 3): `int.from_bytes(imp.get_magic()[:2], 'little')` (ignore `b'\r\n'`) e.g., `imp.get_magic() == b'3\r\r\n'` corresponds to `3379` (Python 3.6). – jfs Jan 07 '18 at 16:41
Or, if you have a GNU/Linux system you can use the command "file" in a terminal:
$ file code.pyc
> code.pyc: python 3.5.2 byte-compiled

- 196
- 2
- 11
-
1
-
Thanks. But I got `test2.cpython-38.pyc: data` under Windows WSL Ubuntu with python 3.8.5 I don't know why. – Rick Nov 04 '20 at 16:38
-
@Rick it depends on the release of the "file" program itself, my file command is from BSD general commands and my version is 5.39. The source code can be found here: https://github.com/freebsd/freebsd/tree/master/contrib/file and the file where the rules for .pyc files are written is here: https://github.com/freebsd/freebsd/blob/master/contrib/file/magic/Magdir/python – Tom Nov 29 '20 at 12:12
Take a look at my script in Python that detects and returns the version of Python by which the file (*.pyc or *.pyo) was compiled.
It detects versions of Python from Python 1.5 up to last Python 3 build.

- 2,987
- 4
- 30
- 39
-
2I couldn't find my magic number in the using the other answers, thanks for the script! – s3v3n Dec 19 '15 at 17:41
The official Python github repository no longer appears to keep the list in import.c.
When searching for a more current list than I could find elsewhere, I encountered what appears to be an up-to-date list from Google as of May 2017.
https://github.com/google/pytype/blob/master/pytype/pyc/magic.py

- 775
- 11
- 26
-
3the original data is [from CPython repo](https://github.com/python/cpython/blob/3460198f6ba40a839f105c381f07179aba1e8c61/Lib/importlib/_bootstrap_external.py#L131) – jfs Jan 07 '18 at 16:31
Adding to @Igor Popov's answer.
To check the version of the compiled script from a newer version of Python:
# written in python3
filename = "outfit.cpython-39.pyc"
with open(filename,'rb') as f:
magic = f.read(4)
print(int.from_bytes(magic[:2], 'little'))
You can lookup the output number here: https://github.com/google/pytype/blob/master/pytype/pyc/magic.py