-
-
Notifications
You must be signed in to change notification settings - Fork 8.4k
mpremote: Refactor error handling to apply generically to any errno. #17090
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
mpremote: Refactor error handling to apply generically to any errno. #17090
Conversation
7231295
to
ca86679
Compare
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #17090 +/- ##
=======================================
Coverage 98.54% 98.54%
=======================================
Files 169 169
Lines 21890 21890
=======================================
Hits 21572 21572
Misses 318 318 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Code size report:
|
2069827
to
790d153
Compare
I've added the TransportError handler back in, and a test script to verify it and everything else. |
This looks good now, thank you. I have some feedback on the test, which is just a suggestion, not necessary to get this merged: it's possible, and simpler in the case at hand, to define an actual filesystem in Python, rather than a block device. And then make that filesystem raise the relevant exception. Eg something like this (untested!): class FS:
def setup_error(self, er):
self._cur_error = er
def mount(self, readonly, mkfs):
pass
def chdir(self, path):
pass
def open(self, path, mode):
raise self._cur_error
fs = FS()
vfs.mount(fs, "/fs")
setup_error = fs.setup_error |
Good call on that, that'd definitely make the test shorter and less clumsy -- makes for better documentation of what it's trying to do, even if it's not a technically necessary change. I'll implement this and retest; also, I should probably test under Windows first too, just to be sure, now that I have an environment for that set up. |
790d153
to
9ab2347
Compare
I've updated the test with the simplification suggested -- which also let me replace the I've re-tested the full mpremote test suite with a WSL2 Linux mpremote host and RPi Pico2 device; I've confirned that the test cases in f54195c discriminate regressions on all the aforementioned issues in this thread. |
Running the test from a Windows 11 mpremote host (using an MSYS2 bash shell) against the same RPi Pico2 device has revealed an interesting deviation: --- test_errno.sh.exp 2025-04-24 10:02:53.218824300 -0400
+++ test_errno.sh.out 2025-04-24 10:02:57.736379400 -0400
@@ -20,6 +20,6 @@
mpremote: cat: EPERM.py: Operation not permitted.
expect error
-----
-mpremote: cat: EOPNOTSUPP.py: Operation not supported.
+mpremote: cat: EOPNOTSUPP.py: Unknown error.
expect error
----- It seems that The fact there's a platform difference in what set of errnos are available leads me to think it might be better to rethink searching the error text for e.g. the symbol name strings |
After a couple more hours working through other possible parsing strategies, I'm forced to conclude that trying to parse all the different ways micropython might output an OSError trace would go well outside the scope of this PR. As proposed, this operates correctly on the subset of errors that the commands it's used for can produce; let's just let that be sufficient, even if it does technically break when you artificially inject more pathological error conditions. |
Signed-off-by: Anson Mansfield <amansfield@mantaro.com>
This rewrites the code that previously manually emitted and caught various OSError subclasses with equivalent code that uses the errno name dictionary to do this generically, and updates the exception handler in do_filesystem to catch them in a similarly-generic fashion using os.strerror to retrieve an appropriate error message text equivalent to the current messages. Note that in the CPython environments where mpremote runs, the call to the OSError constructor already returns an instance of the corresponding mapped exception subtype. Signed-off-by: Anson Mansfield <amansfield@mantaro.com>
Signed-off-by: Anson Mansfield <amansfield@mantaro.com>
9ab2347
to
dc46cf1
Compare
Sounds good! |
Yes, good to be pragmatic. Now merged. |
Summary
This rewrites the code that previously manually emitted and caught various
OSError
subclasses with equivalent code that uses the errno name dictionary to do this generically, and updates the exception handler indo_filesystem
to catch them in a similarly-generic fashion usingos.strerror
to retrieve an appropriate error message text equivalent to the current messages.Note that in the CPython environments where mpremote runs, the call to the
OSError
constructor already returns an instance of the corresponding mapped exception subtype.That is, this code:
is exactly equivalent to:
i.e. they both raise a
PermissionError(1, '')
error.(See also: #17088, documenting this difference with micropython.)
Testing
This PR adds an automated test script,
test_errno.sh
. It includes test cases for all of the errnos that were previously special-cased and for raw TransportErrors not wrapping any OSError.I've tested running the full mpremote test suite, including this new script against rp2040 and rp2350 targets, and have performed additional manual tests of all of the existing exception cases to make sure they were covered.
Manual testing also led to the discovery of the modules_errno_enotsup.py cpydiff submitted in #17088. I've also added a test to ensure mpremote correctly handles this corner case.
Trade-offs and Alternatives
This PR removes the conversion from
errno.ENODEV
intoFileNotFoundError
; see #17090 (comment) for a discussion of the rationale.This PR subtly changes the spelling of some of the error messages that get reported when filesystem operations fail. This may break external tools that depend on those specific spellings may break; such usages arguably deserve to break, but this change also inadvertently makes mpremote compatible with any other pre-existing jank that expects the CPython error spellings.
Many of the errors this code scans for in the mpremote output are ones that micropython could never generate. This is a mild performance pessimization. I previously suspected that this might also broaden a correctness issue around injecting errno symbol names into file paths, however I've tested and verified that no such vulnerability exists.