Skip to content

Commit 333d2d9

Browse files
committed
bdb: Add initial micropython support. Requires MICROPY_PY_SYS_SETTRACE.
1 parent 2d2abf2 commit 333d2d9

File tree

1 file changed

+71
-47
lines changed

1 file changed

+71
-47
lines changed

python-stdlib/bdb/bdb.py

Lines changed: 71 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
"""Debugger basics"""
22

3+
# This is originally from cpython 3.10: https://raw.githubusercontent.com/python/cpython/3.10/Lib/bdb.py
4+
# Patches for micropython have been commented as such.
5+
36
import fnmatch
47
import sys
58
import os
6-
from inspect import CO_GENERATOR, CO_COROUTINE, CO_ASYNC_GENERATOR
9+
10+
## MPY: no inspect module avaialble
11+
# from inspect import CO_GENERATOR, CO_COROUTINE, CO_ASYNC_GENERATOR
712

813
__all__ = ["BdbQuit", "Bdb", "Breakpoint"]
914

10-
GENERATOR_AND_COROUTINE_FLAGS = CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR
15+
## MPY: These flags currently don't exist
16+
# GENERATOR_AND_COROUTINE_FLAGS = CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR
1117

1218

1319
class BdbQuit(Exception):
@@ -46,17 +52,21 @@ def canonic(self, filename):
4652
"""
4753
if filename == "<" + filename[1:-1] + ">":
4854
return filename
49-
canonic = self.fncache.get(filename)
50-
if not canonic:
51-
canonic = os.path.abspath(filename)
52-
canonic = os.path.normcase(canonic)
53-
self.fncache[filename] = canonic
54-
return canonic
55+
56+
## MPY: os.path module difficult to add as dependency
57+
# canonic = self.fncache.get(filename)
58+
# if not canonic:
59+
# canonic = os.path.abspath(filename)
60+
# canonic = os.path.normcase(canonic)
61+
# self.fncache[filename] = canonic
62+
# return canonic
63+
return filename
5564

5665
def reset(self):
5766
"""Set values of attributes as ready to start debugging."""
58-
import linecache
59-
linecache.checkcache()
67+
## MPY: linecache not yet available
68+
# import linecache
69+
# linecache.checkcache()
6070
self.botframe = None
6171
self._set_stopinfo(None, None)
6272

@@ -115,6 +125,12 @@ def dispatch_line(self, frame):
115125
if self.quitting: raise BdbQuit
116126
return self.trace_dispatch
117127

128+
def is_coroutine(self, frame):
129+
## MPY: co_flags attrib not available, compatible method of detecting coroutine TBD
130+
# return frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS
131+
return False
132+
133+
118134
def dispatch_call(self, frame, arg):
119135
"""Invoke user function and return trace function for call event.
120136
@@ -131,7 +147,7 @@ def dispatch_call(self, frame, arg):
131147
# No need to trace this function
132148
return # None
133149
# Ignore call events in generator except when stepping.
134-
if self.stopframe and frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS:
150+
if self.stopframe and self.is_coroutine(frame):
135151
return self.trace_dispatch
136152
self.user_call(frame, arg)
137153
if self.quitting: raise BdbQuit
@@ -146,7 +162,7 @@ def dispatch_return(self, frame, arg):
146162
"""
147163
if self.stop_here(frame) or frame == self.returnframe:
148164
# Ignore return events in generator except when stepping.
149-
if self.stopframe and frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS:
165+
if self.stopframe and self.is_coroutine(frame):
150166
return self.trace_dispatch
151167
try:
152168
self.frame_returning = frame
@@ -170,7 +186,7 @@ def dispatch_exception(self, frame, arg):
170186
# When stepping with next/until/return in a generator frame, skip
171187
# the internal StopIteration exception (with no traceback)
172188
# triggered by a subiterator run with the 'yield from' statement.
173-
if not (frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS
189+
if not (self.is_coroutine(frame)
174190
and arg[0] is StopIteration and arg[2] is None):
175191
self.user_exception(frame, arg)
176192
if self.quitting: raise BdbQuit
@@ -179,7 +195,7 @@ def dispatch_exception(self, frame, arg):
179195
# next/until command at the last statement in the generator before the
180196
# exception.
181197
elif (self.stopframe and frame is not self.stopframe
182-
and self.stopframe.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS
198+
and self.is_coroutine(self.stopframe)
183199
and arg[0] in (StopIteration, GeneratorExit)):
184200
self.user_exception(frame, arg)
185201
if self.quitting: raise BdbQuit
@@ -305,8 +321,9 @@ def set_step(self):
305321
# for performance reasons) when returning from the current frame.
306322
if self.frame_returning:
307323
caller_frame = self.frame_returning.f_back
308-
if caller_frame and not caller_frame.f_trace:
309-
caller_frame.f_trace = self.trace_dispatch
324+
## MPY: f_trace attrib not yet available
325+
# if caller_frame and not caller_frame.f_trace:
326+
# caller_frame.f_trace = self.trace_dispatch
310327
self._set_stopinfo(None, None)
311328

312329
def set_next(self, frame):
@@ -315,7 +332,7 @@ def set_next(self, frame):
315332

316333
def set_return(self, frame):
317334
"""Stop when returning from the given frame."""
318-
if frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS:
335+
if self.is_coroutine(frame):
319336
self._set_stopinfo(frame, None, -1)
320337
else:
321338
self._set_stopinfo(frame.f_back, frame)
@@ -326,12 +343,13 @@ def set_trace(self, frame=None):
326343
If frame is not specified, debugging starts from caller's frame.
327344
"""
328345
if frame is None:
329-
frame = sys._getframe().f_back
346+
frame = sys._getframe(1)
330347
self.reset()
331-
while frame:
332-
frame.f_trace = self.trace_dispatch
333-
self.botframe = frame
334-
frame = frame.f_back
348+
## MPY: f_trace attrib not yet available
349+
# while frame:
350+
# frame.f_trace = self.trace_dispatch
351+
# self.botframe = frame
352+
# frame = frame.f_back
335353
self.set_step()
336354
sys.settrace(self.trace_dispatch)
337355

@@ -345,10 +363,12 @@ def set_continue(self):
345363
if not self.breaks:
346364
# no breakpoints; run without debugger overhead
347365
sys.settrace(None)
348-
frame = sys._getframe().f_back
349-
while frame and frame is not self.botframe:
350-
del frame.f_trace
351-
frame = frame.f_back
366+
## MPY: was sys._getframe().f_back but f_back missing when inside trace dispatch functions
367+
frame = sys._getframe(1)
368+
## MPY: f_trace attrib not yet available
369+
# while frame and frame is not self.botframe:
370+
# del frame.f_trace
371+
# frame = frame.f_back
352372

353373
def set_quit(self):
354374
"""Set quitting attribute to True.
@@ -381,10 +401,11 @@ def set_break(self, filename, lineno, temporary=False, cond=None,
381401
The filename should be in canonical form.
382402
"""
383403
filename = self.canonic(filename)
384-
import linecache # Import as late as possible
385-
line = linecache.getline(filename, lineno)
386-
if not line:
387-
return 'Line %s:%d does not exist' % (filename, lineno)
404+
## MPY: linecache not yet available
405+
# import linecache # Import as late as possible
406+
# line = linecache.getline(filename, lineno)
407+
# if not line:
408+
# return 'Line %s:%d does not exist' % (filename, lineno)
388409
self._add_to_breaks(filename, lineno)
389410
bp = Breakpoint(filename, lineno, temporary, cond, funcname)
390411
return None
@@ -557,23 +578,26 @@ def format_stack_entry(self, frame_lineno, lprefix=': '):
557578
line of code (if it exists).
558579
559580
"""
560-
import linecache, reprlib
561581
frame, lineno = frame_lineno
562-
filename = self.canonic(frame.f_code.co_filename)
563-
s = '%s(%r)' % (filename, lineno)
564-
if frame.f_code.co_name:
565-
s += frame.f_code.co_name
566-
else:
567-
s += "<lambda>"
568-
s += '()'
569-
if '__return__' in frame.f_locals:
570-
rv = frame.f_locals['__return__']
571-
s += '->'
572-
s += reprlib.repr(rv)
573-
line = linecache.getline(filename, lineno, frame.f_globals)
574-
if line:
575-
s += lprefix + line.strip()
576-
return s
582+
return repr(frame.f_code)
583+
## MPY: linecache, reprlib, f_locals not yet available
584+
# import linecache, reprlib
585+
# frame, lineno = frame_lineno
586+
# filename = self.canonic(frame.f_code.co_filename)
587+
# s = '%s(%r)' % (filename, lineno)
588+
# if frame.f_code.co_name:
589+
# s += frame.f_code.co_name
590+
# else:
591+
# s += "<lambda>"
592+
# s += '()'
593+
# if '__return__' in frame.f_locals:
594+
# rv = frame.f_locals['__return__']
595+
# s += '->'
596+
# s += reprlib.repr(rv)
597+
# line = linecache.getline(filename, lineno, frame.f_globals)
598+
# if line:
599+
# s += lprefix + line.strip()
600+
# return s
577601

578602
# The following methods can be called by clients to use
579603
# a debugger to debug a statement or an expression.
@@ -628,7 +652,7 @@ def runctx(self, cmd, globals, locals):
628652

629653
# This method is more useful to debug a single function call.
630654

631-
def runcall(self, func, /, *args, **kwds):
655+
def runcall(self, func, *args, **kwds):
632656
"""Debug a single function call.
633657
634658
Return the result of the function call.

0 commit comments

Comments
 (0)