bdb: Add initial micropython support.

Requires micropython to be compiled with MICROPY_PY_SYS_SETTRACE.
Also requires https://github.com/micropython/micropython/pull/8767
pull/499/head
Andrew Leech 2022-06-21 10:08:39 +10:00
rodzic 8eced76f3e
commit 1f036b14c8
1 zmienionych plików z 23 dodań i 10 usunięć

Wyświetl plik

@ -1,13 +1,19 @@
"""Debugger basics""" """Debugger basics"""
# This is originally from cpython 3.10: https://raw.githubusercontent.com/python/cpython/3.10/Lib/bdb.py
# Patches for micropython have been commented as such.
import fnmatch import fnmatch
import sys import sys
import os import os
from inspect import CO_GENERATOR, CO_COROUTINE, CO_ASYNC_GENERATOR
## MPY: no inspect module avaialble
# from inspect import CO_GENERATOR, CO_COROUTINE, CO_ASYNC_GENERATOR
__all__ = ["BdbQuit", "Bdb", "Breakpoint"] __all__ = ["BdbQuit", "Bdb", "Breakpoint"]
GENERATOR_AND_COROUTINE_FLAGS = CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR ## MPY: These flags currently don't exist
# GENERATOR_AND_COROUTINE_FLAGS = CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR
class BdbQuit(Exception): class BdbQuit(Exception):
@ -115,6 +121,12 @@ class Bdb:
if self.quitting: raise BdbQuit if self.quitting: raise BdbQuit
return self.trace_dispatch return self.trace_dispatch
def is_coroutine(self, frame):
## MPY: co_flags attrib not available, compatible method of detecting coroutine TBD
# return frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS
return False
def dispatch_call(self, frame, arg): def dispatch_call(self, frame, arg):
"""Invoke user function and return trace function for call event. """Invoke user function and return trace function for call event.
@ -131,7 +143,7 @@ class Bdb:
# No need to trace this function # No need to trace this function
return # None return # None
# Ignore call events in generator except when stepping. # Ignore call events in generator except when stepping.
if self.stopframe and frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS: if self.stopframe and self.is_coroutine(frame):
return self.trace_dispatch return self.trace_dispatch
self.user_call(frame, arg) self.user_call(frame, arg)
if self.quitting: raise BdbQuit if self.quitting: raise BdbQuit
@ -146,7 +158,7 @@ class Bdb:
""" """
if self.stop_here(frame) or frame == self.returnframe: if self.stop_here(frame) or frame == self.returnframe:
# Ignore return events in generator except when stepping. # Ignore return events in generator except when stepping.
if self.stopframe and frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS: if self.stopframe and self.is_coroutine(frame):
return self.trace_dispatch return self.trace_dispatch
try: try:
self.frame_returning = frame self.frame_returning = frame
@ -170,7 +182,7 @@ class Bdb:
# When stepping with next/until/return in a generator frame, skip # When stepping with next/until/return in a generator frame, skip
# the internal StopIteration exception (with no traceback) # the internal StopIteration exception (with no traceback)
# triggered by a subiterator run with the 'yield from' statement. # triggered by a subiterator run with the 'yield from' statement.
if not (frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS if not (self.is_coroutine(frame)
and arg[0] is StopIteration and arg[2] is None): and arg[0] is StopIteration and arg[2] is None):
self.user_exception(frame, arg) self.user_exception(frame, arg)
if self.quitting: raise BdbQuit if self.quitting: raise BdbQuit
@ -179,7 +191,7 @@ class Bdb:
# next/until command at the last statement in the generator before the # next/until command at the last statement in the generator before the
# exception. # exception.
elif (self.stopframe and frame is not self.stopframe elif (self.stopframe and frame is not self.stopframe
and self.stopframe.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS and self.is_coroutine(self.stopframe)
and arg[0] in (StopIteration, GeneratorExit)): and arg[0] in (StopIteration, GeneratorExit)):
self.user_exception(frame, arg) self.user_exception(frame, arg)
if self.quitting: raise BdbQuit if self.quitting: raise BdbQuit
@ -315,7 +327,7 @@ class Bdb:
def set_return(self, frame): def set_return(self, frame):
"""Stop when returning from the given frame.""" """Stop when returning from the given frame."""
if frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS: if self.is_coroutine(frame):
self._set_stopinfo(frame, None, -1) self._set_stopinfo(frame, None, -1)
else: else:
self._set_stopinfo(frame.f_back, frame) self._set_stopinfo(frame.f_back, frame)
@ -557,7 +569,8 @@ class Bdb:
line of code (if it exists). line of code (if it exists).
""" """
import linecache, reprlib ## MPY: reprlib not yet available
import linecache #, reprlib
frame, lineno = frame_lineno frame, lineno = frame_lineno
filename = self.canonic(frame.f_code.co_filename) filename = self.canonic(frame.f_code.co_filename)
s = '%s(%r)' % (filename, lineno) s = '%s(%r)' % (filename, lineno)
@ -569,7 +582,7 @@ class Bdb:
if '__return__' in frame.f_locals: if '__return__' in frame.f_locals:
rv = frame.f_locals['__return__'] rv = frame.f_locals['__return__']
s += '->' s += '->'
s += reprlib.repr(rv) s += repr(rv)
line = linecache.getline(filename, lineno, frame.f_globals) line = linecache.getline(filename, lineno, frame.f_globals)
if line: if line:
s += lprefix + line.strip() s += lprefix + line.strip()
@ -628,7 +641,7 @@ class Bdb:
# This method is more useful to debug a single function call. # This method is more useful to debug a single function call.
def runcall(self, func, /, *args, **kwds): def runcall(self, func, *args, **kwds):
"""Debug a single function call. """Debug a single function call.
Return the result of the function call. Return the result of the function call.