Skip to content

Commit e58c7ce

Browse files
committed
docs/reference: Add documentation describing use of .mpy files.
Including information about .mpy versioning and how to debug failed imports of .mpy files.
1 parent a3df152 commit e58c7ce

File tree

2 files changed

+179
-0
lines changed

2 files changed

+179
-0
lines changed

docs/reference/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ implementation and the best practices to use them.
2121

2222
glossary.rst
2323
repl.rst
24+
mpyfiles.rst
2425
isr_rules.rst
2526
speed_python.rst
2627
constrained.rst

docs/reference/mpyfiles.rst

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
.. _mpy_files:
2+
3+
MicroPython .mpy files
4+
======================
5+
6+
MicroPython defines the concept of an .mpy file which is a binary container
7+
file format that holds precompiled code, and which can be imported like a
8+
normal .py module. The file ``foo.mpy`` can be imported via ``import foo``,
9+
as long as ``foo.mpy`` can be found in the usual way by the import machinery.
10+
Usually, each directory listed in ``sys.path`` is searched in order. When
11+
searching a particular directory ``foo.py`` is looked for first and if that
12+
is not found then ``foo.mpy`` is looked for, then the search continues in the
13+
next directory if neither is found. As such, ``foo.py`` will take precedence
14+
over ``foo.mpy``.
15+
16+
These .mpy files can contain bytecode which is usually generated from Python
17+
source files (.py files) via the ``mpy-cross`` program. For some architectures
18+
an .mpy file can also contain native machine code, which can be generated in
19+
a variety of ways, most notably from C source code.
20+
21+
Versioning and compatibility of .mpy files
22+
------------------------------------------
23+
24+
A given .mpy file may or may not be compatible with a given MicroPython system.
25+
Compatibility is based on the following:
26+
27+
* Version of the .mpy file: the version of the file must match the version
28+
supported by the system loading it.
29+
30+
* Bytecode features used in the .mpy file: there are two bytecode features
31+
which must match between the file and the system: unicode support and
32+
inline caching of map lookups in the bytecode.
33+
34+
* Small integer bits: the .mpy file will require a minimum number of bits in
35+
a small integer and the system loading it must support at least this many
36+
bits.
37+
38+
* Qstr compression window size: the .mpy file will require a minimum window
39+
size for qstr decompression and the system loading it must have a window
40+
greater or equal to this size.
41+
42+
* Native architecture: if the .mpy file contains native machine code then
43+
it will specify the architecture of that machine code and the system
44+
loading it must support execution of that architecture's code.
45+
46+
If a MicroPython system supports importing .mpy files then the
47+
``sys.implementation.mpy`` field will exist and return an integer which
48+
encodes the version (lower 8 bits), features and native architecture.
49+
50+
Trying to import an .mpy file that fails one of the first four tests will
51+
raise ``ValueError('incompatible .mpy file')``. Trying to import an .mpy
52+
file that fails the native architecture test (if it contains native machine
53+
code) will raise ``ValueError('incompatible .mpy arch')``.
54+
55+
If importing an .mpy file fails then try the following:
56+
57+
* Determine the .mpy version and flags supported by your MicroPython system
58+
by executing::
59+
60+
import sys
61+
sys_mpy = sys.implementation.mpy
62+
arch = [None, 'x86', 'x64',
63+
'armv6', 'armv6m', 'armv7m', 'armv7em', 'armv7emsp', 'armv7emdp',
64+
'xtensa', 'xtensawin'][sys_mpy >> 10]
65+
print('mpy version:', sys_mpy & 0xff)
66+
print('mpy flags:', end='')
67+
if arch:
68+
print(' -march=' + arch, end='')
69+
if sys_mpy & 0x100:
70+
print(' -mcache-lookup-bc', end='')
71+
if not sys_mpy & 0x200:
72+
print(' -mno-unicode', end='')
73+
print()
74+
75+
* Check the validity of the .mpy file by inspecting the first two bytes of
76+
the file. The first byte should be an uppercase 'M' and the second byte
77+
will be the version number, which should match the system version from above.
78+
If it doesn't match then rebuild the .mpy file.
79+
80+
* Check if the system .mpy version matches the version emitted by ``mpy-cross``
81+
that was used to build the .mpy file, found by ``mpy-cross --version``.
82+
If it doesn't match then recompile ``mpy-cross`` from the Git repository
83+
checked out at the tag (or hash) reported by ``mpy-cross --version``.
84+
85+
* Make sure you are using the correct ``mpy-cross`` flags, found by the code
86+
above, or by inspecting the ``MPY_CROSS_FLAGS`` Makefile variable for the
87+
port that you are using.
88+
89+
The following table shows the correspondence between MicroPython release
90+
and .mpy version.
91+
92+
=================== ============
93+
MicroPython release .mpy version
94+
=================== ============
95+
v1.12 and up 5
96+
v1.11 4
97+
v1.9.3 - v1.10 3
98+
v1.9 - v1.9.2 2
99+
v1.5.1 - v1.8.7 0
100+
=================== ============
101+
102+
For completeness, the next table shows the Git commit of the main
103+
MicroPython repository at which the .mpy version was changed.
104+
105+
=================== ========================================
106+
.mpy version change Git commit
107+
=================== ========================================
108+
4 to 5 5716c5cf65e9b2cb46c2906f40302401bdd27517
109+
3 to 4 9a5f92ea72754c01cc03e5efcdfe94021120531e
110+
2 to 3 ff93fd4f50321c6190e1659b19e64fef3045a484
111+
1 to 2 dd11af209d226b7d18d5148b239662e30ed60bad
112+
0 to 1 6a11048af1d01c78bdacddadd1b72dc7ba7c6478
113+
initial version 0 d8c834c95d506db979ec871417de90b7951edc30
114+
=================== ========================================
115+
116+
Binary encoding of .mpy files
117+
-----------------------------
118+
119+
MicroPython .mpy files are a binary container format with code objects
120+
stored internally in a nested hierarchy. To keep files small while still
121+
providing a large range of possible values it uses the concept of a
122+
variably-encoded-unsigned-integer (vuint) in many places. Similar to utf-8
123+
encoding, this encoding stores 7 bits per byte with the 8th bit (MSB) set
124+
if one or more bytes follow. The bits of the unsigned integer are stored
125+
in the vuint in LSB form.
126+
127+
The top-level of an .mpy file consists of two parts:
128+
129+
* The header.
130+
131+
* The raw-code for the outer scope of the module.
132+
This outer scope is executed when the .mpy file is imported.
133+
134+
The header
135+
~~~~~~~~~~
136+
137+
The .mpy header is:
138+
139+
====== ================================
140+
size field
141+
====== ================================
142+
byte value 0x4d (ASCII 'M')
143+
byte .mpy version number
144+
byte feature flags
145+
byte number of bits in a small int
146+
vuint size of qstr window
147+
====== ================================
148+
149+
Raw code elements
150+
~~~~~~~~~~~~~~~~~
151+
152+
A raw-code element contains code, either bytecode or native machine code. Its
153+
contents are:
154+
155+
====== ================================
156+
size field
157+
====== ================================
158+
vuint type and size
159+
... code (bytecode or machine code)
160+
vuint number of constant objects
161+
vuint number of sub-raw-code elements
162+
... constant objects
163+
... sub-raw-code elements
164+
====== ================================
165+
166+
The first vuint in a raw-code element encodes the type of code stored in this
167+
element (the two least-significant bits), and the decompressed length of the code
168+
(the amount of RAM to allocate for it).
169+
170+
Following the vuint comes the code itself. In the case of bytecode it also contains
171+
compressed qstr values.
172+
173+
Following the code comes a vuint counting the number of constant objects, and
174+
another vuint counting the number of sub-raw-code elements.
175+
176+
The constant objects are then stored next.
177+
178+
Finally any sub-raw-code elements are stored, recursively.

0 commit comments

Comments
 (0)