Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

MicroPython for Microcontrollers  Module 14 – Versions: what changed and MicroPython 2.0

What changed in 1.24 to 1.29, and MicroPython 2.0

The MicroPython 1.24 to 1.29 changes that break existing code, how to find the ones for your board, and how to make the vfs move of MicroPython 2.0 today.

  • Intermediate
  • 15 minutes
  • Examples run with Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free lesson, Pro answers: 15 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Find the release notes and the versioned documentation that matter for your code and board
  • Update code for the seven breaking changes between MicroPython 1.24 and 1.29
  • Choose between a feature probe and a version check for a change between releases
  • Prepare code for MicroPython 2.0 by importing mount and the Vfs classes from vfs

Before you start

What you’ll learn · Before you start

What you will learn

  • Find the release notes and the versioned documentation that matter for your code and board
  • Update code for the seven breaking changes between MicroPython 1.24 and 1.29
  • Choose between a feature probe and a version check for a change between releases
  • Prepare code for MicroPython 2.0 by importing mount and the Vfs classes from vfs

Before you start

On this page

Six MicroPython releases, 1.24 to 1.29, brought hundreds of changes, and seven of them can break code that worked the day before: a sys.exit() that now restarts the board, an encoding name that now raises LookupError, an SD card call whose return value turned its meaning around. Behind them MicroPython 2.0 is taking shape, with one API move announced so far.

This lesson shows where a release’s notes put the changes for your board, walks through the seven breaking changes with the fix for each, separates the features you probe for from the behaviour you check by version, and makes the vfs move of 2.0 in code that runs on today’s releases.

Release notes put the breaking changes first, then every change by port

Each release has its notes on GitHub, under the tag of its version, such as v1.29.0. They open with an overview: the new features, then a paragraph for each port that changed. Release 1.29 adds a section of its own for breaking changes; earlier releases name theirs inside the overview, which makes them easy to miss. A table of firmware sizes follows, and then the full list of changes, sorted by component: the core under “py core” and “extmod”, then documentation, tests and tools, and at the end one block per port, “rp2 port”, “esp32 port” and so on. For code on one board, read the overview, the breaking changes, and the blocks of the core and of your port.

Walk through the six releases of this lesson and the version after them:

Timeline · 7 events in order Six releases, and the version after them Walk through the releases in order; each card names what changed for application code and, where there is one, the change that can break it.
  1. 1.24

    RP2350 and ESP32-C6, and a soft reset on sys.exit() breaking change

    Support for the RP2350 and the ESP32-C6, a RISC-V native emitter, network.ipconfig(), UART interrupts that behave alike across ports, and mpremote sha256sum with copies that skip unchanged files. Breaking: sys.exit() now soft-resets the board instead of dropping to the REPL.

  2. 1.25

    ROMFS and the RISC-V inline assembler

    The ROMFS filesystem with mpremote romfs, @micropython.asm_rv32, DTLS in the tls module, mpremote rm -r, and sys.implementation._build. Bus numbers became optional for I2C and SPI on rp2 and for I2C on esp32, and on stm32 buses are now released at a soft reset.

  3. 1.26

    machine.I2CTarget and better floats

    Python code can act as an I2C device, floats print in a form that reads back to the same value far more often, native code got faster, and assigning to a bytearray or memoryview slice no longer allocates. On ESP32 the vfs area is sized at start-up and PCNT, Counter and Encoder arrived.

  4. 1.27

    Support tiers, ESP32-C5 and ESP32-P4

    Every port now has a support tier. The ESP32-C5 and ESP32-P4 joined, the unix and windows ports gained the raw REPL, and most ports except esp32 gained hard callbacks for machine.Timer.

  5. 1.28

    machine.CAN, PWM everywhere and t-strings

    The machine.CAN class arrived with an stm32 implementation, PWM now works on every Tier 1 and Tier 2 microcontroller port, t-strings arrived at the full feature level, and weakref on the webassembly PyScript variant.

  6. 1.29

    mem_backup(), c_module() and stricter text breaking change

    machine.mem_backup(), c_module() in manifests, decoding errors "ignore" and "replace", TLS-PSK, a codeberg: alias in mpremote, and const() with type hints. Breaking: only utf-8 and ascii encodings; SDCard readblocks() and writeblocks() return 0 or a negative errno on esp32 and stm32; sys.platform is "nrf" on every nrf board; stm32 stm names drop _S and _NS.

  7. 2.0

    In development, with no builds to download planned

    The migration guide lists one change so far, the move of os.mount, os.umount and the Vfs classes into the vfs module, and goals for consistent machine APIs and for running .mpy files in place from the filesystem.

The timeline has 7 events, from 1.24 to 2.0.

The documentation is versioned too. docs.micropython.org/en/v1.29.0/ describes exactly that release, while /en/latest/ follows the development branch, so link and read the version your boards run. On the board, sys.implementation.version says which release it is, (1, 29, 0, '') for 1.29.0, a tuple that compares correctly.

Comparing versions as strings

os.uname().release >= "1.24" is true on 1.9.4, because strings compare letter by letter and “9” comes after “2”. Compare tuples: sys.implementation.version >= (1, 24).

Checkpoint

Check: Reading release notes and versioned docs

  1. Question 1 of 2 Your code runs on ESP32 boards. Which parts of a release's notes must you read?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: The overview, the breaking changes, and the detailed blocks of the core and of the esp32 port

    Breaking changes may sit in the overview in releases before 1.29, so read it whole, then the blocks that apply to your build.

  2. Question 2 of 2 Which check is right for "this board runs 1.24 or later"?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: sys.implementation.version >= (1, 24)

    sys.implementation.version is a tuple made for comparisons; strings and floats are not.

Seven changes from 1.24 to 1.29 break code that worked before

These are the changes the release notes themselves call breaking change: A change in a new release after which code that worked before fails or behaves differently, without being changed itself. Release notes call such changes out so that they can be fixed before an upgrade. that reach Python code, with what each asks of it:

Release What changed What to do
1.24 sys.exit() and raise SystemExit soft-reset the board, where they used to return to the REPL Stop a program by returning from its main function, never with sys.exit() in main.py
1.25 stm32 releases I2C and SPI buses at a soft reset Create and initialise every bus when the program starts
1.26 nrf: enable_irq(state) takes the value disable_irq() returned Pass the state, as on every other port
1.29 Only “utf-8”, “utf8” and “ascii” are accepted as encodings; others raise LookupError instead of decoding as UTF-8 Fix the name, or decode Latin-1 by hand
1.29 esp32 and stm32: SDCard.readblocks() and writeblocks() return 0 or a negative number, no longer True on success Check results with a helper that works with both
1.29 sys.platform is “nrf” on every nrf board Test sys.platform == "nrf", and name the board with sys.implementation._machine
1.29 stm32 boards with Cortex-M33 or M55 cores: stm register names lose _S and _NS, so stm.RTC_S becomes stm.RTC Use the name without the ending; the firmware picks the secure or non-secure address

The 1.29 section lists three more, for people who build firmware or write C rather than Python: the unix port’s MICROPY_FORCE_32BIT option is gone, the powerpc port is merged into qemu, and stm32’s C function rng_get() is renamed.

The 1.24 change is the one that turns a program into a reboot loop: a main.py that calls sys.exit() when a sensor is missing now restarts, finds the sensor missing again and restarts again. The SD card change is the subtle one. Code that checked if card.writeblocks(n, buf): now reads success, 0, as failure. And the first fix that comes to mind is wrong in a way the trace shows:

Interactive · line by line A check that accepts both SDCard results, and its bug The loop feeds the check four results a write can return. Watch what it says about False, an old firmware's failure.

Program · compat/sdcard_bug.py

for r in (True, False, 0, -5):
    ok = r == 0 or r is True
    print(r, ok)

Variables

After line 1: r is -5; ok is False; the loop's own list from (True, False, 0, -5) is (True, False, 0, -5) (the loop stopped at position 4).r-5okFalsethe loop reads (True, False, 0, -5)True0False102-534end
  • just taken by the loop
  • changed in this step
  • passed by the loop
  • read next
  • where the loop stopped

Printed so far

True True
False True
0 True
-5 False

Every step was recorded from a run with Python 3.14.8 on macOS 26 arm64.

The drawing shows the variables when the program ends, and the program's 14 steps are listed below.

Line run 1
Next line end
Loop pass 4
Every step as text
  1. Start. No line has run yet, so there are no variables. Python starts with line 1.
  2. Line 1. for r in (True, False, 0, -5): takes the item at position 0 of (True, False, 0, -5): r is True.
  3. Line 2. ok = r == 0 or r is True sets ok to True. True, a success before 1.29, is not 0, so the check goes on to r is True and accepts it.
  4. Line 3. print(r, ok) prints True True.
  5. Line 1. for r in (True, False, 0, -5): takes the item at position 1 of (True, False, 0, -5): r is False.
  6. Line 2. ok = r == 0 or r is True ok keeps its value, True. The bug False is a failure before 1.29, but False == 0 is true in Python, because bool is a kind of int: the failed write counts as a success.
  7. Line 3. print(r, ok) prints False True.
  8. Line 1. for r in (True, False, 0, -5): takes the item at position 2 of (True, False, 0, -5): r is 0.
  9. Line 2. ok = r == 0 or r is True ok keeps its value, True.
  10. Line 3. print(r, ok) prints 0 True.
  11. Line 1. for r in (True, False, 0, -5): takes the item at position 3 of (True, False, 0, -5): r is -5.
  12. Line 2. ok = r == 0 or r is True sets ok to False (it was True). -5, a negative errno from 1.29, is neither 0 nor True, so it is reported as the failure it is.
  13. Line 3. print(r, ok) prints -5 False.
  14. Line 1. for r in (True, False, 0, -5): finds no item at position 4 of (True, False, 0, -5) (it has 4 items), so the loop ends. That was the last line, so the program ends. Three of the four answers are right and the one that matters most, a failed write, is wrong. Test is True and is False before comparing with 0.

r == 0 or r is True called a failed write of an old firmware, False, a success, because in Python False == 0. The working check tests the two booleans first, then the number. The program below runs that check, the Latin-1 decoder and the vfs choice of the next sections, from one small module:

A compat module for the changes of 1.24 to 1.29

compat/what_changed.py

"""The compat module beside this file on three builds, imitated with their version tuples and their vfs module."""
import sys
import types

import compat

builds = {"1.22.2": (1, 22, 2, ""), "1.26.0": (1, 26, 0, ""), "1.29.0": (1, 29, 0, "")}
for name, ver in builds.items():
    impl = types.SimpleNamespace(name="micropython", version=ver)
    sys.modules["vfs"] = types.ModuleType("vfs") if ver >= (1, 23) else None  # None: import vfs fails
    print(name, "mount from", compat.load_vfs().__name__, end="; ")
    try:
        compat.requires((1, 26), "a slice assignment in a hard IRQ", impl=impl)
        print("the IRQ handler may run")
    except RuntimeError as e:
        print(e)

for r in (True, False, None, 0, -5):
    print("SDCard returned", repr(r), "-> ok:", compat.blocks_ok(r))
print(compat.latin1(b"Caf\xe9 at 21\xb0C"), "from Latin-1 bytes")

Output

1.22.2 mount from os; a slice assignment in a hard IRQ needs MicroPython 1.26 or later, this is 1.22.2
1.26.0 mount from vfs; the IRQ handler may run
1.29.0 mount from vfs; the IRQ handler may run
SDCard returned True -> ok: True
SDCard returned False -> ok: False
SDCard returned None -> ok: True
SDCard returned 0 -> ok: True
SDCard returned -5 -> ok: False
Café at 21°C from Latin-1 bytes

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 what_changed.py

compat/compat.py

"""Calls that changed between MicroPython 1.24 and 1.29, and the 2.0 move of mount, made to work on every build."""
import sys


def load_vfs():
    """The module that holds mount(): vfs on 1.23 and later, the only place in 2.0; os on older builds."""
    try:
        import vfs

        return vfs
    except ImportError:
        import os

        return os


vfs = load_vfs()


def version(impl=None):
    return tuple((impl or sys.implementation).version[:3])


def requires(minimum, name="this code", impl=None):
    """Raise a clear error on a build older than minimum, a tuple such as (1, 26)."""
    have = version(impl)
    if have < tuple(minimum):
        raise RuntimeError("%s needs MicroPython %s or later, this is %s"
                           % (name, ".".join(map(str, minimum)), ".".join(map(str, have))))


def blocks_ok(result):
    """True when a block device read or write succeeded: True (esp32 and stm32 SDCard before 1.29), None or 0."""
    if result is True or result is None:  # test the bools first: False == 0 in Python
        return True
    if result is False:
        return False
    return result == 0  # from 1.29: 0, or a negative errno


def latin1(data):
    """Decode Latin-1 bytes by hand: 1.29 accepts only utf-8 and ascii, and raises LookupError for the rest."""
    return "".join(chr(b) for b in data)  # every Latin-1 byte is the code point of the same number

blocks_ok() accepts True, None and 0 as success and False and negative numbers as failure, so the same logger runs on 1.28 and on 1.29. latin1() works because every Latin-1 byte is the Unicode code point of the same number, so chr() of each byte decodes it, and b"Caf\xe9" becomes “Café” on a release that refuses the encoding name.

Testing a result that may be a bool against 0

if result == 0: is also true for False. Whenever a call may return a bool on one version and a number on another, test is True and is False first.

Checkpoint

Check: The breaking changes of 1.24 to 1.29

  1. Question 1 of 2 After an upgrade from 1.23 to 1.24, a board whose sensor is unplugged reboots again and again. Its main.py calls sys.exit() when the sensor is missing. Why?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Since 1.24, sys.exit() soft-resets the board, which runs main.py again, which exits again

    Report the missing sensor and return from the main function, or wait and retry, instead of calling sys.exit().

  2. Question 2 of 2 Which check of an SDCard write result is right both before and after the 1.29 change?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: True if r is True or r is None; False if r is False; otherwise r == 0

    From 1.29 the esp32 and stm32 SDCard methods return 0 or a negative number, as vfs.AbstractBlockDev describes; before, True or False.

Probe for new features; check the version only for changed behaviour

The feature-levels lesson’s rule holds across releases: ask the build for a feature with a feature probe: A short test a program runs to find out whether the Python under it has a feature: it compiles a snippet of new syntax, tries an import or looks up an attribute, inside try and except, and uses the answer to choose a code path. More in “Which Python is this? Feature levels and probes” instead of guessing from its version, because a release may leave a feature out of a smaller build. Most of the additions of 1.24 to 1.29 can be probed in one line:

Added in Feature Probe
1.24 network.ipconfig() hasattr(network, "ipconfig")
1.26 machine.I2CTarget hasattr(machine, "I2CTarget")
1.26 machine.Counter, machine.Encoder on ESP32 hasattr(machine, "Counter")
1.28 machine.CAN, first on stm32 hasattr(machine, "CAN")
1.28 t-strings, at the full feature level compile('t"x"', "probe", "eval") in a try
1.29 machine.mem_backup() hasattr(machine, "mem_backup")
1.29 rp2.DMATimer hasattr(rp2, "DMATimer")

Some changes have nothing to probe: the code is the same and only its behaviour differs. Since 1.26, assigning to a slice of a bytearray or memoryview no longer allocates, so it works in a hard interrupt handler with the heap locked; before 1.26 the same line raised MemoryError there. For such a change, compare the version once at start-up and fail clearly, as requires((1, 26), ...) in the compat module does: on the 1.22.2 build it raised an error that names both versions.

Key idea

A feature that may be missing is a question for the build, answered by a probe; a behaviour that changed is a question of the release, answered by sys.implementation.version.

Checkpoint

Check: Probes against version checks

  1. One question Code uses machine.mem_backup() if it is there, and relies on slice assignment working in a hard interrupt handler. Which checks fit?

    Choose one answer.

    Show the answer: One question

    Answer: hasattr(machine, "mem_backup") for the first, a version check for 1.26 or later for the second

    The lesson's rule: probe features, check the version only for behaviour that changed.

MicroPython 2.0 moves mount into vfs, and you can move today

MicroPython 2.0 is the next major release, still in development, with no firmware to download; the migration guide says it can be built by enabling MICROPY_PREVIEW_VERSION_2. Its goals are consistent machine APIs across ports, running .mpy files straight from the filesystem without copying them into RAM, more flexible partitions, and code that works on CPython and MicroPython alike. One change is listed so far: six names leave os for the vfs module. The table shows where each of them lives, before, during and after the move:

Names Before 1.23 1.23 to 1.29 2.0
mount(), umount() os vfs, and os for old code vfs only
VfsFat, VfsLfs1, VfsLfs2 os vfs, and os for old code vfs only
VfsPosix os vfs, and os for old code vfs only

The column for 1.23 to 1.29 is why the move can happen today: both modules answer there, and the os documentation of 1.29 keeps the names in os only for backwards compatibility. So import them from vfs now. compat.load_vfs() tries import vfs and falls back to os on builds older than 1.23, and the program above showed it: mount came from os on the 1.22.2 build and from vfs on 1.26.0 and 1.29.0. Code written this way runs unchanged on every 1.x release since 1.23 and on 2.0.

Watch the guide, not only the notes

The migration guide is not final: new items will appear there before 2.0 ships. Read it in the documentation of each new release, and move code as items appear, while both forms still work.

Checkpoint

Check: MicroPython 2.0 and the vfs move

  1. Question 1 of 2 Code calls os.mount(). What change keeps it working on every release from 1.23 and on MicroPython 2.0?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Import mount from vfs, falling back to os on builds that have no vfs module

    compat.load_vfs() did exactly that: os on the 1.22.2 build, vfs on 1.26.0 and 1.29.0.

  2. Question 2 of 2 Can you download MicroPython 2.0 for a Pico now?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: No; it is in development, with no builds to download, though it can be built with MICROPY_PREVIEW_VERSION_2

    The guide promises builds for both 1.x.y and 2.0.0-preview as 2.0 gets closer.

Practise: a compat module for MicroPython 1.20 to 1.29

Write the three functions a fleet of boards on mixed releases needs: a version requirement with a clear error, a choice between vfs and os, and a check of block-device results that works on both sides of 1.29. The sample tests pass fake modules and version tuples instead of a board:

Exercise · Easy · Python

A compat module for MicroPython 1.20 to 1.29

Write the three functions of compat.py, which let one codebase run on boards from MicroPython 1.20 to 1.29.

requires(minimum, version, name="this code") takes two version tuples, such as (1, 26) and (1, 24, 1, ""), and raises RuntimeError when the version is older than the minimum; the message names both, as in "I2CTarget needs MicroPython 1.26 or later, this is 1.24.1". Compare only the first three numbers of version.

pick_vfs(found) takes a dict of what import found on the board, {"vfs": module or None, "os": module}, and returns the module to call mount() on: found["vfs"] when it is not None, otherwise found["os"] if it has a mount attribute, otherwise it raises ImportError.

blocks_ok(result) says whether a block device read or write succeeded on either side of 1.29: True, None and 0 mean success; False and negative numbers mean failure.

Starter code · compat.py

def requires(minimum, version, name="this code"):
    """Raise RuntimeError when version is older than minimum."""


def pick_vfs(found):
    """The module that holds mount(): vfs if present, else os if it has mount."""
    return found["os"]


def blocks_ok(result):
    """True when a block device read or write succeeded, before or after 1.29."""
    return result == 0
The sample tests · test_compat.py
import types

from compat import blocks_ok, pick_vfs, requires


def test_requires_passes():
    """allows the minimum and later versions"""
    requires((1, 26), (1, 26, 0, ""))
    requires((1, 26), (1, 29, 0, ""))


def test_requires_refuses():
    """refuses an older version with both versions in the message"""
    try:
        requires((1, 26), (1, 24, 1, ""), "I2CTarget")
    except RuntimeError as e:
        assert str(e) == "I2CTarget needs MicroPython 1.26 or later, this is 1.24.1"
    else:
        raise AssertionError("no RuntimeError")


def test_requires_not_strings():
    """compares numbers, so 1.9 is older than 1.24"""
    try:
        requires((1, 24), (1, 9, 4, ""))
    except RuntimeError:
        return
    raise AssertionError("1.9.4 passed as 1.24 or later")


def test_pick_vfs():
    """prefers vfs, falls back to an os that has mount, and refuses otherwise"""
    vfs = types.SimpleNamespace(mount=lambda *a: None)
    old_os = types.SimpleNamespace(mount=lambda *a: None)
    cpython_os = types.SimpleNamespace()
    assert pick_vfs({"vfs": vfs, "os": old_os}) is vfs
    assert pick_vfs({"vfs": None, "os": old_os}) is old_os
    try:
        pick_vfs({"vfs": None, "os": cpython_os})
    except ImportError:
        return
    raise AssertionError("no ImportError")


def test_blocks_ok():
    """accepts both conventions and never mistakes False for 0"""
    assert [blocks_ok(r) for r in (True, None, 0)] == [True, True, True]
    assert [blocks_ok(r) for r in (False, -5, -110)] == [False, False, False]
A hint

Tuples compare item by item, so tuple(version[:3]) < tuple(minimum) is the whole test; join the numbers with ".".join(map(str, ...)) for the message. In blocks_ok(), check result is True, result is None and result is False before you compare with 0.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Then check yourself on release notes, breaking changes, probes and 2.0:

Practice that adapts to you

Practise this lesson

8 questions about 4 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 8 Which documentation describes exactly the release your boards run, 1.29.0?

    Choose one answer.

    Show the answer: Question 1 of 8

    Answer: docs.micropython.org/en/v1.29.0/

    Read and link the documentation of the version you run, and move the link when you upgrade.

  2. Question 2 of 8 Where do the notes of 1.24 mention that sys.exit() changed?

    Choose one answer.

    Show the answer: Question 2 of 8

    Answer: In the overview, in a paragraph that calls it a breaking change

    Read the whole overview of each release you skip over when upgrading.

  3. Question 3 of 8 What does b"Caf\xe9".decode("latin-1") do on MicroPython 1.29?

    Choose one answer.

    Show the answer: Question 3 of 8

    Answer: Raises LookupError, because only utf-8, utf8 and ascii are accepted

    Decode Latin-1 by hand with chr() of each byte, as compat.latin1() does, or fix the data's encoding.

  4. Question 4 of 8 Code chooses pins with if sys.platform == "nrf52":. What happens on an nrf52 board with 1.29?

    Choose one answer.

    Show the answer: Question 4 of 8

    Answer: The branch is skipped, because sys.platform is now "nrf" on every nrf board

    Test sys.platform for the port and sys.implementation._machine for the board, or better, the features you need.

  5. Question 5 of 8 How do you check whether a build accepts t-strings?

    Choose one answer.

    Show the answer: Question 5 of 8

    Answer: Compile a tiny t-string with compile() inside a try, and take SyntaxError, or NameError on a build without compile(), as no

    The feature-levels lesson's probe: compile('t"x"', "probe", "eval") in a try.

  6. Question 6 of 8 Why is a probe better than a version check for a feature such as machine.I2CTarget?

    Choose one answer.

    Show the answer: Question 6 of 8

    Answer: A build of a new release can still leave the feature out, so only asking the build is sure

    I2CTarget exists on alif, esp32, mimxrt, rp2, samd, stm32 and zephyr; hasattr(machine, "I2CTarget") answers for the build in hand.

  7. Question 7 of 8 Which names does the MicroPython 2.0 migration guide move from os to vfs?

    Choose every answer that is right.

    Show the answer: Question 7 of 8

    Answer:

    • VfsFat, VfsLfs1, VfsLfs2 and VfsPosix
    • mount and umount

    The guide's one change so far removes MicroPython's filesystem extensions from os, which brings os closer to CPython's.

  8. Question 8 of 8 Why does moving mount out of os serve the 2.0 goal of CPython compatibility?

    Choose one answer.

    Show the answer: Question 8 of 8

    Answer: CPython's os has no mount, so code that uses os then means the same on both and MicroPython's extensions live in their own module

    Moving extensions into MicroPython-only modules is how 2.0 makes shared modules mean the same thing on both implementations.

Interview questions

Warm-up (fresher): Where do you look to see what changed in a MicroPython release for your board?

Open the release’s notes on GitHub, under the tag of the version, such as v1.29.0. Read the overview, the breaking changes, which 1.29 lists in a section of its own and earlier releases inside the overview, and in the detailed list the blocks for the core, “py core” and “extmod”, and for your port, such as “rp2 port”. Then read the documentation of that exact version at docs.micropython.org/en/v1.29.0/ rather than the latest, and check on the board with sys.implementation.version which release it really runs.

Warm-up (fresher): What does MicroPython 2.0 change so far, and how can code prepare for it?

MicroPython 2.0 is still in development and has no builds to download. Its migration guide lists one change so far: os.mount, os.umount and the Vfs classes move from os to the vfs module. Code prepares by importing them from vfs today, which every release since 1.23 has, with a fallback to os for older builds. The guide is not final, so reading it with each release shows the next moves early.

Warm-up (fresher): How do you check in code that a board runs MicroPython 1.24 or later?

Compare sys.implementation.version with a tuple: sys.implementation.version >= (1, 24). The version is a tuple of the major, minor and micro numbers and a release level, such as (1, 29, 0, '') for 1.29.0, and tuples compare number by number, so (1, 9, 4, '') counts as older than (1, 24). Comparing the strings of os.uname().release gets it wrong, because “1.9.4” sorts after “1.24” letter by letter. Keep such a check for behaviour that changed between releases; a feature that a build may leave out is probed instead, with hasattr() or an import in a try.

Two longer questions about upgrades follow; each row leads to what it tests and a first step:

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Mid to senior

    A fleet of ESP32 loggers moves from MicroPython 1.28 to 1.29. Afterwards every SD card write is reported as failed although the files are fine, and a parser of a vendor's text files raises LookupError. Diagnose both, and make the code correct on both releases while the fleet is half upgraded.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Fresher to mid

    A product's main.py calls sys.exit() when its sensor does not answer. After an upgrade from 1.23 to 1.24, boards with a broken sensor reboot in a loop and never reach the REPL. Explain what changed and redesign the failure path.

    Model answer, Pro. First step and follow-ups

Flashcards · 8 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. Where are a MicroPython release's breaking changes listed?

    In its notes on GitHub under the version's tag, such as v1.29.0: 1.29 gives them a section of their own, earlier releases name them in the overview. Then read the py core, extmod and port blocks.

  2. How do you check that a board runs 1.24 or later?

    sys.implementation.version >= (1, 24): tuples compare number by number. Comparing the strings of os.uname().release makes 1.9.4 look newer than 1.24.

  3. Why can sys.exit() in main.py cause a reboot loop since 1.24?

    It soft-resets the board instead of returning to the REPL, so a main.py that exits when a sensor is missing restarts and exits again. Return from the main function instead.

  4. What changed in SDCard.readblocks() and writeblocks() on esp32 and stm32 in 1.29?

    They return 0 or a negative number, no longer True on success, so code that tests if card.writeblocks(n, buf): now reads success as failure.

  5. Why can a test for 0 or True misjudge a block device's result across versions?

    In Python False == 0, so a failed write on an older firmware counts as a success. Test is True and is False first, then the number.

  6. When does code probe for a feature, and when does it check the version?

    A feature a build may lack is probed, with hasattr(), an import or compile() in a try. Behaviour that changed between releases is checked with sys.implementation.version.

  7. What is the one change listed so far for MicroPython 2.0?

    mount(), umount() and the Vfs classes leave os for the vfs module. Import them from vfs today, which every release since 1.23 has.

  8. How do you decode Latin-1 on 1.29, which refuses the encoding name?

    A Latin-1 byte's value is also its Unicode code point, so chr() of each byte, joined, gives the text: b"Caf\xe9" becomes "Café".

Key takeaways

  • A release’s notes give the overview and the breaking changes first, then every change by component and port; read the blocks of the core and of your port, in the documentation of the version you run.
  • Seven changes from 1.24 to 1.29 break old code: sys.exit() soft-resets, stm32 releases buses at a soft reset, nrf’s enable_irq(state), only utf-8 and ascii, SDCard results of 0 or a negative number, sys.platform “nrf”, and stm32’s stm names without _S or _NS.
  • Check results that may be a bool on one version and a number on another with is True and is False first, because False == 0.
  • Probe for features with hasattr(), import or compile(), and compare sys.implementation.version as a tuple only for behaviour that changed.
  • MicroPython 2.0 is in development; its one listed change moves mount, umount and the Vfs classes to vfs, which code can import today on any release since 1.23.

Terms in this lesson

breaking change
A change in a new release after which code that worked before fails or behaves differently, without being changed itself. Release notes call such changes out so that they can be fixed before an upgrade.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.