Skip to content

Commit bd4a85d

Browse files
committed
pybricks.compile: add support for MPY v6.1
MicroPython v1.20 bumped the MPY version to v6.1 which only affects native modules. This add support for a new flag from the hub to indicate native module support and the new MPY version.
1 parent e283187 commit bd4a85d

File tree

4 files changed

+49
-10
lines changed

4 files changed

+49
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99
### Added
1010
- Added `PybricksHub.download_user_program()` method ([support#284]).
1111
- Added support for flashing EV3 firmware.
12+
- Added support for MPY ABI v6.1.
1213

1314
[support#284]: https://github.com/pybricks/support/issues/284
1415

pybricksdev/ble/pybricks.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# SPDX-License-Identifier: MIT
2-
# Copyright (c) 2021 The Pybricks Authors
2+
# Copyright (c) 2021-2023 The Pybricks Authors
33

44
"""
55
This module is used for Bluetooth Low Energy communications with devices
@@ -272,9 +272,19 @@ class HubCapabilityFlag(IntFlag):
272272
"""
273273
Hub supports user programs using a multi-file blob with MicroPython MPY (ABI V6) files.
274274
275+
Native module support is not implied by this flag but may be indicated by additional flags.
276+
275277
.. availability:: Since Pybricks protocol v1.2.0.
276278
"""
277279

280+
USER_PROG_MULTI_FILE_MPY6_1_NATIVE = 2 << 1
281+
"""
282+
Hub supports user programs using a multi-file blob with MicroPython MPY (ABI V6.1) files
283+
including native module support.
284+
285+
.. availability:: Since Pybricks protocol v1.3.0.
286+
"""
287+
278288

279289
def unpack_hub_capabilities(data: bytes) -> Tuple[int, HubCapabilityFlag, int]:
280290
"""

pybricksdev/compile.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# SPDX-License-Identifier: MIT
2-
# Copyright (c) 2019-2022 The Pybricks Authors
2+
# Copyright (c) 2019-2023 The Pybricks Authors
33

44
import asyncio
55
import logging
66
import os
77
from modulefinder import ModuleFinder
8-
from typing import List, Optional
8+
from typing import List, Optional, Tuple, Union
99

1010
import mpy_cross_v5
1111
import mpy_cross_v6
@@ -76,7 +76,7 @@ async def compile_file(path: str, abi: int, compile_args: Optional[List[str]] =
7676
return mpy
7777

7878

79-
async def compile_multi_file(path: str, abi: int):
79+
async def compile_multi_file(path: str, abi: Union[int, Tuple[int, int]]):
8080
"""Compiles a Python file and its dependencies with ``mpy-cross``.
8181
8282
On the hub, all dependencies behave as independent modules. Any (leading)
@@ -101,7 +101,8 @@ async def compile_multi_file(path: str, abi: int):
101101
path:
102102
Path to script that is to be compiled.
103103
abi:
104-
Expected MPY ABI version.
104+
Expected MPY ABI version. Can be major version (int) if no native
105+
.mpy modules or tuple of major, minor version.
105106
106107
Returns:
107108
Concatenation of all compiled files in the format given above.
@@ -123,10 +124,12 @@ async def compile_multi_file(path: str, abi: int):
123124
# Get a data blob with all scripts.
124125
parts: List[bytes] = []
125126

127+
abi_major, abi_minor = (abi, None) if isinstance(abi, int) else abi
128+
126129
for name, module in finder.modules.items():
127130
if not module.__file__:
128131
continue
129-
mpy = await compile_file(module.__file__, abi)
132+
mpy = await compile_file(module.__file__, abi_major)
130133

131134
parts.append(len(mpy).to_bytes(4, "little"))
132135
parts.append(name.encode() + b"\x00")
@@ -138,9 +141,19 @@ async def compile_multi_file(path: str, abi: int):
138141
try:
139142
with open(os.path.join(spath, f"{name}.mpy"), "rb") as f:
140143
mpy = f.read()
141-
if mpy[1] != abi:
144+
if mpy[1] != abi_major:
145+
raise ValueError(
146+
f"{name}.mpy has abi major version {mpy[1]} while {abi_major} is required"
147+
)
148+
if (
149+
abi_minor is not None # hub indicated a minor version
150+
and (mpy[2] >> 2) # mpy has native arch
151+
# TODO: How to validate native arch? For now just gets runtime error on hub.
152+
and (mpy[2] & 0x3)
153+
!= abi_minor # mpy minor version does not match hub minor version
154+
):
142155
raise ValueError(
143-
f"{name}.mpy has abi version {mpy[1]} while {abi} is required"
156+
f"{name}.mpy has abi minor version {mpy[2] & 0x3} while {abi_minor} is required"
144157
)
145158
parts.append(len(mpy).to_bytes(4, "little"))
146159
parts.append(name.encode() + b"\x00")

pybricksdev/connections/pybricks.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -548,13 +548,28 @@ async def run(
548548
return
549549

550550
# since Pybricks profile v1.2.0, the hub will tell us which file format(s) it supports
551-
if not (self._capability_flags & HubCapabilityFlag.USER_PROG_MULTI_FILE_MPY6):
551+
if not (
552+
self._capability_flags
553+
& (
554+
HubCapabilityFlag.USER_PROG_MULTI_FILE_MPY6
555+
| HubCapabilityFlag.USER_PROG_MULTI_FILE_MPY6_1_NATIVE
556+
)
557+
):
552558
raise RuntimeError(
553559
"Hub is not compatible with any of the supported file formats"
554560
)
555561

562+
# no support for native modules unless one of the flags below is set
563+
abi = 6
564+
565+
if (
566+
self._capability_flags
567+
& HubCapabilityFlag.USER_PROG_MULTI_FILE_MPY6_1_NATIVE
568+
):
569+
abi = (6, 1)
570+
556571
if py_path is not None:
557-
mpy = await compile_multi_file(py_path, 6)
572+
mpy = await compile_multi_file(py_path, abi)
558573
await self.download_user_program(mpy)
559574

560575
await self.start_user_program()

0 commit comments

Comments
 (0)