The header is 8 bytes: a magic number that identifies the bytecode, and the modification time of the source file. The code object follows straight after it. Both are 4-byte little-endian numbers. CPython writes the magic number, then a time of 0, then the code object, and fills in the real time last, so a file cut off while being written will not match its source.
Magic number
Offset 0, 4 bytes. The low two bytes are the number that names the bytecode, and the
high two are always 0D 0A, a carriage return and a line feed, so that a
file handled as text no longer matches. CPython compares the whole value and
recompiles if it differs.
The values are spaced at least two apart because the -U option adds 1,
giving byte-compiled code of its own; the explorer counts those numbers as the same
release. The table comes from the list of known values in CPython’s source. The last
number of each release is the one its final version writes.
| Magic number | Bytes in the file | Release | What the source says it changed |
|---|---|---|---|
| 62011 | 3B F2 0D 0A | 2.3a0, then 2.3 | None given. The source lists 62011, then 62021, then 62011 again with the note “(!)”. 62011 is what 2.3 writes |
| 62021 | 45 F2 0D 0A | 2.3a0 | None given |
| 62041 | 59 F2 0D 0A | 2.4a0 | None given |
| 62051 | 63 F2 0D 0A | 2.4a3 | None given |
| 62061 | 6D F2 0D 0A | 2.4b1; final 2.4 | None given |
| 62071 | 77 F2 0D 0A | 2.5a0 | None given |
| 62081 | 81 F2 0D 0A | 2.5a0 | ast-branch |
| 62091 | 8B F2 0D 0A | 2.5a0 | with |
| 62092 | 8C F2 0D 0A | 2.5a0 | changed WITH_CLEANUP opcode |
| 62101 | 95 F2 0D 0A | 2.5b3 | fix wrong code: for x, in ... |
| 62111 | 9F F2 0D 0A | 2.5b3 | fix wrong code: x += yield |
| 62121 | A9 F2 0D 0A | 2.5c1 | fix wrong lnotab with for loops and storing constants that should have been removed |
| 62131 | B3 F2 0D 0A | 2.5c2; final 2.5 | fix wrong code: for x, in ... in listcomp/genexp |
| 62151 | C7 F2 0D 0A | 2.6a0 | peephole optimizations and STORE_MAP opcode |
| 62161 | D1 F2 0D 0A | 2.6a1; final 2.6 | WITH_CLEANUP optimization |
| 62171 | DB F2 0D 0A | 2.7a0 | optimize list comprehensions/change LIST_APPEND |
| 62181 | E5 F2 0D 0A | 2.7a0 | optimize conditional branches: introduce POP_JUMP_IF_FALSE and POP_JUMP_IF_TRUE |
| 62191 | EF F2 0D 0A | 2.7a0 | introduce SETUP_WITH |
| 62201 | F9 F2 0D 0A | 2.7a0 | introduce BUILD_SET |
| 62211 | 03 F3 0D 0A | 2.7a0; final 2.7 | introduce MAP_ADD and SET_ADD |
Source modification time
Offset 4, 4 bytes: the modification time of the .py file that was
compiled, in seconds since 1970. When Python imports the module it compares this with
the source file’s time, and if they are not equal the source is compiled again. The
explorer shows the field as a date and time in UTC.
File names
The compiled file sits next to its source and takes its name with one letter added:
foo.py becomes foo.pyc. With the -O option
Python writes and reads foo.pyo instead. The two have the same layout.