fix: add graceful error logging for BaseThread crashes#908
Conversation
S1DDHEY
left a comment
There was a problem hiding this comment.
ACK
ran the provided test on current implementation, observed that it raises a ValueError, but it is not handled by BaseThread. After applying the PR changes, the same test passes, confirming that exceptions are now properly logged and thread state is updated.
| except Exception as e: | ||
| logger.exception(f"Thread {self.__class__.__name__} crashed: {e}") | ||
| self.keep_running = False | ||
| self.run = wrapped_run |
There was a problem hiding this comment.
One thought/suggestion that I have is dynamic reassignment of self.run.
Wrapping run() at runtime via self.run = wrapped_run can be a bit fragile and harder to reason about. Would it make sense to instead define a stable run() in BaseThread and delegate subclass logic to something like _run_impl()?
There was a problem hiding this comment.
Yes, that is indeed what I was originally intending to do with this PR. However, I realised it would require refactoring across many files. Any class that uses Basethread would have to use _run_impl() instead of run(). Since my main goal was bringing light to this, I did not want to do a big refactor. However, if the maintainers think this a viable problem to work on, I can do those changes too. They would just require a rename of run() to maybe a simpler name like _run() rather than _run_imp() across few files, and a placeholder definition for it with the same logic I have here.
|
This is a blind spot. I can't recall if it has caused me trouble during development in the past, but it seems very likely that it would have. I had Claude take a look at our use of (Copied from Claude, but curated to be human-friendly) Alternative:
|
Description
Problem or Issue being addressed
Currently, any class that inherits from BaseThread (which handles nearly all hardware listeners, camera loops, and scrolling UI text) can silently crash without yielding any errors or stack traces to the output, refer to issue #907.
Because the underlying loop encapsulates its execution in an isolated boundary, if a developer writes buggy code or an edge-case logic error occurs inside
def run(self):, the thread terminates abruptly. It never halts the main UI/application event loop, but the targeted feature has zero trace output, which might make debugging harder.Solution
By wrapping the subclass's inherited
.run()method in a protective globaltry/except Exception:block at the instance level:logger.exception.self.keep_running = False.Additional Information
MicroPython Portability: Since it natively preserves the
threading.Threadinheritance hierarchy, it stays clean and is cross-compatible if there is MicroPython_threadport in the future.Screenshots
Tried adding an intentional bug in class
HorizontalTextScrollThread, it logs the exception properly. This will help in future dev workflows, rather than making it go unnoticed.This pull request is categorized as a:
Checklist
I ran
pytestlocallyI included screenshots of any new or modified screens
Should be part of the PR description above.
I added or updated tests
Any new or altered functionality should be covered in a unit test. Any new or updated sequences require FlowTests.
I tested this PR hands-on on the following platform(s):
I have reviewed these notes:
Thank you! Please join our Devs' Telegram group to get more involved.
Resolves #907
Part of Summer of Bitcoin