ยปCore Development>Code coverage>Lib/bdb.py

Python code coverage for Lib/bdb.py

#countcontent
1n/a"""Debugger basics"""
2n/a
3n/aimport fnmatch
4n/aimport sys
5n/aimport os
6n/afrom inspect import CO_GENERATOR
7n/a
8n/a__all__ = ["BdbQuit", "Bdb", "Breakpoint"]
9n/a
10n/aclass BdbQuit(Exception):
11n/a """Exception to give up completely."""
12n/a
13n/a
14n/aclass Bdb:
15n/a """Generic Python debugger base class.
16n/a
17n/a This class takes care of details of the trace facility;
18n/a a derived class should implement user interaction.
19n/a The standard debugger class (pdb.Pdb) is an example.
20n/a """
21n/a
22n/a def __init__(self, skip=None):
23n/a self.skip = set(skip) if skip else None
24n/a self.breaks = {}
25n/a self.fncache = {}
26n/a self.frame_returning = None
27n/a
28n/a def canonic(self, filename):
29n/a if filename == "<" + filename[1:-1] + ">":
30n/a return filename
31n/a canonic = self.fncache.get(filename)
32n/a if not canonic:
33n/a canonic = os.path.abspath(filename)
34n/a canonic = os.path.normcase(canonic)
35n/a self.fncache[filename] = canonic
36n/a return canonic
37n/a
38n/a def reset(self):
39n/a import linecache
40n/a linecache.checkcache()
41n/a self.botframe = None
42n/a self._set_stopinfo(None, None)
43n/a
44n/a def trace_dispatch(self, frame, event, arg):
45n/a if self.quitting:
46n/a return # None
47n/a if event == 'line':
48n/a return self.dispatch_line(frame)
49n/a if event == 'call':
50n/a return self.dispatch_call(frame, arg)
51n/a if event == 'return':
52n/a return self.dispatch_return(frame, arg)
53n/a if event == 'exception':
54n/a return self.dispatch_exception(frame, arg)
55n/a if event == 'c_call':
56n/a return self.trace_dispatch
57n/a if event == 'c_exception':
58n/a return self.trace_dispatch
59n/a if event == 'c_return':
60n/a return self.trace_dispatch
61n/a print('bdb.Bdb.dispatch: unknown debugging event:', repr(event))
62n/a return self.trace_dispatch
63n/a
64n/a def dispatch_line(self, frame):
65n/a if self.stop_here(frame) or self.break_here(frame):
66n/a self.user_line(frame)
67n/a if self.quitting: raise BdbQuit
68n/a return self.trace_dispatch
69n/a
70n/a def dispatch_call(self, frame, arg):
71n/a # XXX 'arg' is no longer used
72n/a if self.botframe is None:
73n/a # First call of dispatch since reset()
74n/a self.botframe = frame.f_back # (CT) Note that this may also be None!
75n/a return self.trace_dispatch
76n/a if not (self.stop_here(frame) or self.break_anywhere(frame)):
77n/a # No need to trace this function
78n/a return # None
79n/a # Ignore call events in generator except when stepping.
80n/a if self.stopframe and frame.f_code.co_flags & CO_GENERATOR:
81n/a return self.trace_dispatch
82n/a self.user_call(frame, arg)
83n/a if self.quitting: raise BdbQuit
84n/a return self.trace_dispatch
85n/a
86n/a def dispatch_return(self, frame, arg):
87n/a if self.stop_here(frame) or frame == self.returnframe:
88n/a # Ignore return events in generator except when stepping.
89n/a if self.stopframe and frame.f_code.co_flags & CO_GENERATOR:
90n/a return self.trace_dispatch
91n/a try:
92n/a self.frame_returning = frame
93n/a self.user_return(frame, arg)
94n/a finally:
95n/a self.frame_returning = None
96n/a if self.quitting: raise BdbQuit
97n/a # The user issued a 'next' or 'until' command.
98n/a if self.stopframe is frame and self.stoplineno != -1:
99n/a self._set_stopinfo(None, None)
100n/a return self.trace_dispatch
101n/a
102n/a def dispatch_exception(self, frame, arg):
103n/a if self.stop_here(frame):
104n/a # When stepping with next/until/return in a generator frame, skip
105n/a # the internal StopIteration exception (with no traceback)
106n/a # triggered by a subiterator run with the 'yield from' statement.
107n/a if not (frame.f_code.co_flags & CO_GENERATOR
108n/a and arg[0] is StopIteration and arg[2] is None):
109n/a self.user_exception(frame, arg)
110n/a if self.quitting: raise BdbQuit
111n/a # Stop at the StopIteration or GeneratorExit exception when the user
112n/a # has set stopframe in a generator by issuing a return command, or a
113n/a # next/until command at the last statement in the generator before the
114n/a # exception.
115n/a elif (self.stopframe and frame is not self.stopframe
116n/a and self.stopframe.f_code.co_flags & CO_GENERATOR
117n/a and arg[0] in (StopIteration, GeneratorExit)):
118n/a self.user_exception(frame, arg)
119n/a if self.quitting: raise BdbQuit
120n/a
121n/a return self.trace_dispatch
122n/a
123n/a # Normally derived classes don't override the following
124n/a # methods, but they may if they want to redefine the
125n/a # definition of stopping and breakpoints.
126n/a
127n/a def is_skipped_module(self, module_name):
128n/a for pattern in self.skip:
129n/a if fnmatch.fnmatch(module_name, pattern):
130n/a return True
131n/a return False
132n/a
133n/a def stop_here(self, frame):
134n/a # (CT) stopframe may now also be None, see dispatch_call.
135n/a # (CT) the former test for None is therefore removed from here.
136n/a if self.skip and \
137n/a self.is_skipped_module(frame.f_globals.get('__name__')):
138n/a return False
139n/a if frame is self.stopframe:
140n/a if self.stoplineno == -1:
141n/a return False
142n/a return frame.f_lineno >= self.stoplineno
143n/a if not self.stopframe:
144n/a return True
145n/a return False
146n/a
147n/a def break_here(self, frame):
148n/a filename = self.canonic(frame.f_code.co_filename)
149n/a if filename not in self.breaks:
150n/a return False
151n/a lineno = frame.f_lineno
152n/a if lineno not in self.breaks[filename]:
153n/a # The line itself has no breakpoint, but maybe the line is the
154n/a # first line of a function with breakpoint set by function name.
155n/a lineno = frame.f_code.co_firstlineno
156n/a if lineno not in self.breaks[filename]:
157n/a return False
158n/a
159n/a # flag says ok to delete temp. bp
160n/a (bp, flag) = effective(filename, lineno, frame)
161n/a if bp:
162n/a self.currentbp = bp.number
163n/a if (flag and bp.temporary):
164n/a self.do_clear(str(bp.number))
165n/a return True
166n/a else:
167n/a return False
168n/a
169n/a def do_clear(self, arg):
170n/a raise NotImplementedError("subclass of bdb must implement do_clear()")
171n/a
172n/a def break_anywhere(self, frame):
173n/a return self.canonic(frame.f_code.co_filename) in self.breaks
174n/a
175n/a # Derived classes should override the user_* methods
176n/a # to gain control.
177n/a
178n/a def user_call(self, frame, argument_list):
179n/a """This method is called when there is the remote possibility
180n/a that we ever need to stop in this function."""
181n/a pass
182n/a
183n/a def user_line(self, frame):
184n/a """This method is called when we stop or break at this line."""
185n/a pass
186n/a
187n/a def user_return(self, frame, return_value):
188n/a """This method is called when a return trap is set here."""
189n/a pass
190n/a
191n/a def user_exception(self, frame, exc_info):
192n/a """This method is called if an exception occurs,
193n/a but only if we are to stop at or just below this level."""
194n/a pass
195n/a
196n/a def _set_stopinfo(self, stopframe, returnframe, stoplineno=0):
197n/a self.stopframe = stopframe
198n/a self.returnframe = returnframe
199n/a self.quitting = False
200n/a # stoplineno >= 0 means: stop at line >= the stoplineno
201n/a # stoplineno -1 means: don't stop at all
202n/a self.stoplineno = stoplineno
203n/a
204n/a # Derived classes and clients can call the following methods
205n/a # to affect the stepping state.
206n/a
207n/a def set_until(self, frame, lineno=None):
208n/a """Stop when the line with the line no greater than the current one is
209n/a reached or when returning from current frame"""
210n/a # the name "until" is borrowed from gdb
211n/a if lineno is None:
212n/a lineno = frame.f_lineno + 1
213n/a self._set_stopinfo(frame, frame, lineno)
214n/a
215n/a def set_step(self):
216n/a """Stop after one line of code."""
217n/a # Issue #13183: pdb skips frames after hitting a breakpoint and running
218n/a # step commands.
219n/a # Restore the trace function in the caller (that may not have been set
220n/a # for performance reasons) when returning from the current frame.
221n/a if self.frame_returning:
222n/a caller_frame = self.frame_returning.f_back
223n/a if caller_frame and not caller_frame.f_trace:
224n/a caller_frame.f_trace = self.trace_dispatch
225n/a self._set_stopinfo(None, None)
226n/a
227n/a def set_next(self, frame):
228n/a """Stop on the next line in or below the given frame."""
229n/a self._set_stopinfo(frame, None)
230n/a
231n/a def set_return(self, frame):
232n/a """Stop when returning from the given frame."""
233n/a if frame.f_code.co_flags & CO_GENERATOR:
234n/a self._set_stopinfo(frame, None, -1)
235n/a else:
236n/a self._set_stopinfo(frame.f_back, frame)
237n/a
238n/a def set_trace(self, frame=None):
239n/a """Start debugging from `frame`.
240n/a
241n/a If frame is not specified, debugging starts from caller's frame.
242n/a """
243n/a if frame is None:
244n/a frame = sys._getframe().f_back
245n/a self.reset()
246n/a while frame:
247n/a frame.f_trace = self.trace_dispatch
248n/a self.botframe = frame
249n/a frame = frame.f_back
250n/a self.set_step()
251n/a sys.settrace(self.trace_dispatch)
252n/a
253n/a def set_continue(self):
254n/a # Don't stop except at breakpoints or when finished
255n/a self._set_stopinfo(self.botframe, None, -1)
256n/a if not self.breaks:
257n/a # no breakpoints; run without debugger overhead
258n/a sys.settrace(None)
259n/a frame = sys._getframe().f_back
260n/a while frame and frame is not self.botframe:
261n/a del frame.f_trace
262n/a frame = frame.f_back
263n/a
264n/a def set_quit(self):
265n/a self.stopframe = self.botframe
266n/a self.returnframe = None
267n/a self.quitting = True
268n/a sys.settrace(None)
269n/a
270n/a # Derived classes and clients can call the following methods
271n/a # to manipulate breakpoints. These methods return an
272n/a # error message is something went wrong, None if all is well.
273n/a # Set_break prints out the breakpoint line and file:lineno.
274n/a # Call self.get_*break*() to see the breakpoints or better
275n/a # for bp in Breakpoint.bpbynumber: if bp: bp.bpprint().
276n/a
277n/a def set_break(self, filename, lineno, temporary=False, cond=None,
278n/a funcname=None):
279n/a filename = self.canonic(filename)
280n/a import linecache # Import as late as possible
281n/a line = linecache.getline(filename, lineno)
282n/a if not line:
283n/a return 'Line %s:%d does not exist' % (filename, lineno)
284n/a list = self.breaks.setdefault(filename, [])
285n/a if lineno not in list:
286n/a list.append(lineno)
287n/a bp = Breakpoint(filename, lineno, temporary, cond, funcname)
288n/a
289n/a def _prune_breaks(self, filename, lineno):
290n/a if (filename, lineno) not in Breakpoint.bplist:
291n/a self.breaks[filename].remove(lineno)
292n/a if not self.breaks[filename]:
293n/a del self.breaks[filename]
294n/a
295n/a def clear_break(self, filename, lineno):
296n/a filename = self.canonic(filename)
297n/a if filename not in self.breaks:
298n/a return 'There are no breakpoints in %s' % filename
299n/a if lineno not in self.breaks[filename]:
300n/a return 'There is no breakpoint at %s:%d' % (filename, lineno)
301n/a # If there's only one bp in the list for that file,line
302n/a # pair, then remove the breaks entry
303n/a for bp in Breakpoint.bplist[filename, lineno][:]:
304n/a bp.deleteMe()
305n/a self._prune_breaks(filename, lineno)
306n/a
307n/a def clear_bpbynumber(self, arg):
308n/a try:
309n/a bp = self.get_bpbynumber(arg)
310n/a except ValueError as err:
311n/a return str(err)
312n/a bp.deleteMe()
313n/a self._prune_breaks(bp.file, bp.line)
314n/a
315n/a def clear_all_file_breaks(self, filename):
316n/a filename = self.canonic(filename)
317n/a if filename not in self.breaks:
318n/a return 'There are no breakpoints in %s' % filename
319n/a for line in self.breaks[filename]:
320n/a blist = Breakpoint.bplist[filename, line]
321n/a for bp in blist:
322n/a bp.deleteMe()
323n/a del self.breaks[filename]
324n/a
325n/a def clear_all_breaks(self):
326n/a if not self.breaks:
327n/a return 'There are no breakpoints'
328n/a for bp in Breakpoint.bpbynumber:
329n/a if bp:
330n/a bp.deleteMe()
331n/a self.breaks = {}
332n/a
333n/a def get_bpbynumber(self, arg):
334n/a if not arg:
335n/a raise ValueError('Breakpoint number expected')
336n/a try:
337n/a number = int(arg)
338n/a except ValueError:
339n/a raise ValueError('Non-numeric breakpoint number %s' % arg)
340n/a try:
341n/a bp = Breakpoint.bpbynumber[number]
342n/a except IndexError:
343n/a raise ValueError('Breakpoint number %d out of range' % number)
344n/a if bp is None:
345n/a raise ValueError('Breakpoint %d already deleted' % number)
346n/a return bp
347n/a
348n/a def get_break(self, filename, lineno):
349n/a filename = self.canonic(filename)
350n/a return filename in self.breaks and \
351n/a lineno in self.breaks[filename]
352n/a
353n/a def get_breaks(self, filename, lineno):
354n/a filename = self.canonic(filename)
355n/a return filename in self.breaks and \
356n/a lineno in self.breaks[filename] and \
357n/a Breakpoint.bplist[filename, lineno] or []
358n/a
359n/a def get_file_breaks(self, filename):
360n/a filename = self.canonic(filename)
361n/a if filename in self.breaks:
362n/a return self.breaks[filename]
363n/a else:
364n/a return []
365n/a
366n/a def get_all_breaks(self):
367n/a return self.breaks
368n/a
369n/a # Derived classes and clients can call the following method
370n/a # to get a data structure representing a stack trace.
371n/a
372n/a def get_stack(self, f, t):
373n/a stack = []
374n/a if t and t.tb_frame is f:
375n/a t = t.tb_next
376n/a while f is not None:
377n/a stack.append((f, f.f_lineno))
378n/a if f is self.botframe:
379n/a break
380n/a f = f.f_back
381n/a stack.reverse()
382n/a i = max(0, len(stack) - 1)
383n/a while t is not None:
384n/a stack.append((t.tb_frame, t.tb_lineno))
385n/a t = t.tb_next
386n/a if f is None:
387n/a i = max(0, len(stack) - 1)
388n/a return stack, i
389n/a
390n/a def format_stack_entry(self, frame_lineno, lprefix=': '):
391n/a import linecache, reprlib
392n/a frame, lineno = frame_lineno
393n/a filename = self.canonic(frame.f_code.co_filename)
394n/a s = '%s(%r)' % (filename, lineno)
395n/a if frame.f_code.co_name:
396n/a s += frame.f_code.co_name
397n/a else:
398n/a s += "<lambda>"
399n/a if '__args__' in frame.f_locals:
400n/a args = frame.f_locals['__args__']
401n/a else:
402n/a args = None
403n/a if args:
404n/a s += reprlib.repr(args)
405n/a else:
406n/a s += '()'
407n/a if '__return__' in frame.f_locals:
408n/a rv = frame.f_locals['__return__']
409n/a s += '->'
410n/a s += reprlib.repr(rv)
411n/a line = linecache.getline(filename, lineno, frame.f_globals)
412n/a if line:
413n/a s += lprefix + line.strip()
414n/a return s
415n/a
416n/a # The following methods can be called by clients to use
417n/a # a debugger to debug a statement or an expression.
418n/a # Both can be given as a string, or a code object.
419n/a
420n/a def run(self, cmd, globals=None, locals=None):
421n/a if globals is None:
422n/a import __main__
423n/a globals = __main__.__dict__
424n/a if locals is None:
425n/a locals = globals
426n/a self.reset()
427n/a if isinstance(cmd, str):
428n/a cmd = compile(cmd, "<string>", "exec")
429n/a sys.settrace(self.trace_dispatch)
430n/a try:
431n/a exec(cmd, globals, locals)
432n/a except BdbQuit:
433n/a pass
434n/a finally:
435n/a self.quitting = True
436n/a sys.settrace(None)
437n/a
438n/a def runeval(self, expr, globals=None, locals=None):
439n/a if globals is None:
440n/a import __main__
441n/a globals = __main__.__dict__
442n/a if locals is None:
443n/a locals = globals
444n/a self.reset()
445n/a sys.settrace(self.trace_dispatch)
446n/a try:
447n/a return eval(expr, globals, locals)
448n/a except BdbQuit:
449n/a pass
450n/a finally:
451n/a self.quitting = True
452n/a sys.settrace(None)
453n/a
454n/a def runctx(self, cmd, globals, locals):
455n/a # B/W compatibility
456n/a self.run(cmd, globals, locals)
457n/a
458n/a # This method is more useful to debug a single function call.
459n/a
460n/a def runcall(self, func, *args, **kwds):
461n/a self.reset()
462n/a sys.settrace(self.trace_dispatch)
463n/a res = None
464n/a try:
465n/a res = func(*args, **kwds)
466n/a except BdbQuit:
467n/a pass
468n/a finally:
469n/a self.quitting = True
470n/a sys.settrace(None)
471n/a return res
472n/a
473n/a
474n/adef set_trace():
475n/a Bdb().set_trace()
476n/a
477n/a
478n/aclass Breakpoint:
479n/a """Breakpoint class.
480n/a
481n/a Implements temporary breakpoints, ignore counts, disabling and
482n/a (re)-enabling, and conditionals.
483n/a
484n/a Breakpoints are indexed by number through bpbynumber and by
485n/a the file,line tuple using bplist. The former points to a
486n/a single instance of class Breakpoint. The latter points to a
487n/a list of such instances since there may be more than one
488n/a breakpoint per line.
489n/a
490n/a """
491n/a
492n/a # XXX Keeping state in the class is a mistake -- this means
493n/a # you cannot have more than one active Bdb instance.
494n/a
495n/a next = 1 # Next bp to be assigned
496n/a bplist = {} # indexed by (file, lineno) tuple
497n/a bpbynumber = [None] # Each entry is None or an instance of Bpt
498n/a # index 0 is unused, except for marking an
499n/a # effective break .... see effective()
500n/a
501n/a def __init__(self, file, line, temporary=False, cond=None, funcname=None):
502n/a self.funcname = funcname
503n/a # Needed if funcname is not None.
504n/a self.func_first_executable_line = None
505n/a self.file = file # This better be in canonical form!
506n/a self.line = line
507n/a self.temporary = temporary
508n/a self.cond = cond
509n/a self.enabled = True
510n/a self.ignore = 0
511n/a self.hits = 0
512n/a self.number = Breakpoint.next
513n/a Breakpoint.next += 1
514n/a # Build the two lists
515n/a self.bpbynumber.append(self)
516n/a if (file, line) in self.bplist:
517n/a self.bplist[file, line].append(self)
518n/a else:
519n/a self.bplist[file, line] = [self]
520n/a
521n/a def deleteMe(self):
522n/a index = (self.file, self.line)
523n/a self.bpbynumber[self.number] = None # No longer in list
524n/a self.bplist[index].remove(self)
525n/a if not self.bplist[index]:
526n/a # No more bp for this f:l combo
527n/a del self.bplist[index]
528n/a
529n/a def enable(self):
530n/a self.enabled = True
531n/a
532n/a def disable(self):
533n/a self.enabled = False
534n/a
535n/a def bpprint(self, out=None):
536n/a if out is None:
537n/a out = sys.stdout
538n/a print(self.bpformat(), file=out)
539n/a
540n/a def bpformat(self):
541n/a if self.temporary:
542n/a disp = 'del '
543n/a else:
544n/a disp = 'keep '
545n/a if self.enabled:
546n/a disp = disp + 'yes '
547n/a else:
548n/a disp = disp + 'no '
549n/a ret = '%-4dbreakpoint %s at %s:%d' % (self.number, disp,
550n/a self.file, self.line)
551n/a if self.cond:
552n/a ret += '\n\tstop only if %s' % (self.cond,)
553n/a if self.ignore:
554n/a ret += '\n\tignore next %d hits' % (self.ignore,)
555n/a if self.hits:
556n/a if self.hits > 1:
557n/a ss = 's'
558n/a else:
559n/a ss = ''
560n/a ret += '\n\tbreakpoint already hit %d time%s' % (self.hits, ss)
561n/a return ret
562n/a
563n/a def __str__(self):
564n/a return 'breakpoint %s at %s:%s' % (self.number, self.file, self.line)
565n/a
566n/a# -----------end of Breakpoint class----------
567n/a
568n/adef checkfuncname(b, frame):
569n/a """Check whether we should break here because of `b.funcname`."""
570n/a if not b.funcname:
571n/a # Breakpoint was set via line number.
572n/a if b.line != frame.f_lineno:
573n/a # Breakpoint was set at a line with a def statement and the function
574n/a # defined is called: don't break.
575n/a return False
576n/a return True
577n/a
578n/a # Breakpoint set via function name.
579n/a
580n/a if frame.f_code.co_name != b.funcname:
581n/a # It's not a function call, but rather execution of def statement.
582n/a return False
583n/a
584n/a # We are in the right frame.
585n/a if not b.func_first_executable_line:
586n/a # The function is entered for the 1st time.
587n/a b.func_first_executable_line = frame.f_lineno
588n/a
589n/a if b.func_first_executable_line != frame.f_lineno:
590n/a # But we are not at the first line number: don't break.
591n/a return False
592n/a return True
593n/a
594n/a# Determines if there is an effective (active) breakpoint at this
595n/a# line of code. Returns breakpoint number or 0 if none
596n/adef effective(file, line, frame):
597n/a """Determine which breakpoint for this file:line is to be acted upon.
598n/a
599n/a Called only if we know there is a bpt at this
600n/a location. Returns breakpoint that was triggered and a flag
601n/a that indicates if it is ok to delete a temporary bp.
602n/a
603n/a """
604n/a possibles = Breakpoint.bplist[file, line]
605n/a for b in possibles:
606n/a if not b.enabled:
607n/a continue
608n/a if not checkfuncname(b, frame):
609n/a continue
610n/a # Count every hit when bp is enabled
611n/a b.hits += 1
612n/a if not b.cond:
613n/a # If unconditional, and ignoring go on to next, else break
614n/a if b.ignore > 0:
615n/a b.ignore -= 1
616n/a continue
617n/a else:
618n/a # breakpoint and marker that it's ok to delete if temporary
619n/a return (b, True)
620n/a else:
621n/a # Conditional bp.
622n/a # Ignore count applies only to those bpt hits where the
623n/a # condition evaluates to true.
624n/a try:
625n/a val = eval(b.cond, frame.f_globals, frame.f_locals)
626n/a if val:
627n/a if b.ignore > 0:
628n/a b.ignore -= 1
629n/a # continue
630n/a else:
631n/a return (b, True)
632n/a # else:
633n/a # continue
634n/a except:
635n/a # if eval fails, most conservative thing is to stop on
636n/a # breakpoint regardless of ignore count. Don't delete
637n/a # temporary, as another hint to user.
638n/a return (b, False)
639n/a return (None, None)
640n/a
641n/a
642n/a# -------------------- testing --------------------
643n/a
644n/aclass Tdb(Bdb):
645n/a def user_call(self, frame, args):
646n/a name = frame.f_code.co_name
647n/a if not name: name = '???'
648n/a print('+++ call', name, args)
649n/a def user_line(self, frame):
650n/a import linecache
651n/a name = frame.f_code.co_name
652n/a if not name: name = '???'
653n/a fn = self.canonic(frame.f_code.co_filename)
654n/a line = linecache.getline(fn, frame.f_lineno, frame.f_globals)
655n/a print('+++', fn, frame.f_lineno, name, ':', line.strip())
656n/a def user_return(self, frame, retval):
657n/a print('+++ return', retval)
658n/a def user_exception(self, frame, exc_stuff):
659n/a print('+++ exception', exc_stuff)
660n/a self.set_continue()
661n/a
662n/adef foo(n):
663n/a print('foo(', n, ')')
664n/a x = bar(n*10)
665n/a print('bar returned', x)
666n/a
667n/adef bar(a):
668n/a print('bar(', a, ')')
669n/a return a/2
670n/a
671n/adef test():
672n/a t = Tdb()
673n/a t.run('import bdb; bdb.foo(10)')