scripts/mpos_controller: give the serial port to mpremote - #226
Conversation
MPOSController accepted --serial-port for its own REPL connection. But it started mpremote with no `connect` argument. mpremote then connects to the first USB serial device that it finds. It sorts the devices by name and selects the first device in that list. If the computer has one device, that device is always the correct device. The problem stays hidden. If the computer has two devices, mpremote sends the files to the incorrect device. mpremote selects the same incorrect device each time, because it sorts the devices. The user sees no error, and sees no change on the correct device. The user then examines the application code, and does not examine the connection. Four functions had this problem: the installapp action, _read_remote_file, write_remote_file and get_widget_tree. Because of this, a host with two devices also made screenshots of the incorrect device. All four functions now build the command with _mpremote_cmd(). That function puts `connect <port>` in the command. The installapp action also stops and gives an error message if the user gives no --serial-port and the computer has more than one device. It is not possible to select the correct device in that condition. The test for app management had the same problem. It received a serial_port argument, but it did not use that argument. The new mpremoteport test shows that the port goes into the command. This test does not use a device. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012s21uqAdYgdHeyB4jnpiG4
0e922b2 to
5aba5ef
Compare
|
Oh, this is cool, and makes total sense, thanks! |
|
Note that it's possible some tests fail if you're not on the latest commit because I just reworked the CI/release workflows. |
ThomasFarstrike
left a comment
There was a problem hiding this comment.
Nice fix — the bug is real and the consolidation into _mpremote_cmd() is clean. Two concerns:
-
run_test_file()left inconsistent. It correctly passesconnect self.portbut still inlines its own mpremote path resolution instead of using_mpremote_cmd(). The helper exists to centralize mpremote command construction — skipping one caller is divergence someone will copy-paste from and forget theconnectargument again. Not blocking, but worth unifying while the change is fresh. -
_count_usb_serial_devices()returns 0 onImportError. If pyserial is missing or has a brokenserial.tools.list_ports, the multi-device guard silently bypasses. Practically: no pyserial = no serial = no problem. But a comment noting the assumption would help the next person debugging why the warning didn't fire.
|
Note that these are minor concerns, of course, but if you wouldn't mind giving your view on them or applying a minor (probably one-line) fix, all the better! |
…guish unknown device count Review follow-up for MicroPythonOS#226: - run_test_file() now builds its three mpremote commands with _mpremote_cmd(), like the other callers. No behaviour change: self.port always has a value in SerialBackend. - _count_usb_serial_devices() gives None instead of 0 when pyserial is missing. mpremote runs with python3, which can be a different interpreter that does have pyserial. A 0 would make the installapp guard read 'no devices' and skip the multi-device check. The guard now warns on None and still errors on more than one device. - New check in the mpremoteport test section: a missing pyserial gives None, not 0. Verified red with the old behaviour. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Hi, thanks for the review. I made two changes:
_count_usb_serial_devices() now returns None for "unknown". The installapp guard warns on None (and proceeds), still errors on >1. A new check in the mpremoteport test section pins this: with the old return 0 it fails, with return None it passes. |
|
Awesome, thanks for the replies, let's merge and see how it goes! |
PR MicroPythonOS#226 moved mpremote command construction into _mpremote_cmd() and added a test section for it. This PR adds a test section for read_until sentinel matching. Both touched the same three places in tests/cpython_mpos_controller.py, so all three conflicts are additive: each side appended a new item where the other appended one. Resolution keeps both sides in every case: - the import list gains AIOREPLClient and END_MARKER next to _count_usb_serial_devices and _mpremote_cmd - the sections registry gains both "readuntil" and "mpremoteport" - both test functions are kept, ordered to match the registry scripts/mpos_controller.py merged without conflict. The two changes do not overlap: MicroPythonOS#226 rewrote the mpremote helpers, this PR changed read_until. Verified that the merged file differs from upstream/main by the CRLF fix and nothing else. Also adds "mpremoteport" to the --only help string. MicroPythonOS#226 added the section but not the help text, and the merge kept this branch's version of that line, so the omission would otherwise have been invisible. Both device-free sections pass in the merged tree (13 checks). Confirmed test_read_until still fails against the pre-fix read_until: the CRLF case waits out the full timeout, so the elapsed-time check catches it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pqs3t4Xx6zh1aHNsDSK31A
The problem
MPOSControlleraccepts--serial-portfor its own REPL connection. But itstarted
mpremotewith noconnectargument.mpremotethen connects to thefirst USB serial device that it finds:
The REPL connection used the correct port, because it uses pyserial directly.
Only the file transfers used the incorrect port.
Why this is difficult to find
If the host has one device, that device is always the correct device. The
problem stays hidden. If the host has two devices,
mpremotesends the filesto the incorrect device.
mpremotesorts the devices by name. Because of this, it selects the sameincorrect device each time. A random selection is more safe: it fails only some
of the time, and the user then examines the connection quickly. This selection
fails all of the time. The user sees no error, and sees no change on the
correct device. The user then examines the application code, and does not
examine the connection.
How to see the problem
python3 scripts/mpos_controller.py --serial-port <the second device> installapp <path>The change
Four functions started
mpremotewithout the port:main(), installapp actionmkdir,fs cp -rSerialBackend._read_remote_filecp :path tmpscreenshot()SerialBackend.write_remote_filecp tmp :pathwrite_file()SerialBackend.get_widget_treecp :/_mpos_tree.json tmpBecause
screenshot()andget_widget_tree()also used the incorrect device,a host with two devices showed the old screen after a correct installation.
This gave more incorrect data to the user.
All four functions now build the command with a new
_mpremote_cmd()helper.The helper puts
connect <port>in the command when the port is known.SerialBackend.run_test_file()is newer code, and it already givesconnect self.portto mpremote. This change makes the four older functions dothe same thing. I did not change
run_test_file(), because it is correct.The installapp action also stops and gives an error message if the user gives
no
--serial-portand the host has more than one device. It is not possible toselect the correct device in that condition.
tests/cpython_mpos_controller.pyhad the same problem. The app managementtest received a
serial_portargument, but it did not use that argument.Tests
A new
mpremoteportsection tests_mpremote_cmd(). It does not use a device,so it runs with the desktop backend:
To show that the test is sufficient, I replaced
_mpremote_cmd()with the oldbehaviour. Two of the five checks then failed.
I also tested the change with one device. The files went to that device.
I did not test with two devices, because I have only one device.
Parts that this change does not correct
scripts/install.shalso startsmpremotewith noconnectargument. Thatscript has no port option, so it does not ignore a port that the user gave. It
always uses the first device. To correct it, it must get a new port option.
This is a change to its interface, so it is not in this pull request.
AGENTS.mdalso shows anmpremotecommand with no port. It has the samecondition.
Checklist
Tools:item is in the "Future release" section.make lint: all checks pass.ruff formaton these files, because the files were not formatted with itbefore this change.
make lintand the CI workflow useruff checkonly.not change an app or a board.