File: pydb.py

package info (click to toggle)
pydb 1.01-6
  • links: PTS
  • area: main
  • in suites: woody
  • size: 100 kB
  • ctags: 157
  • sloc: python: 1,122; makefile: 40; sh: 18
file content (1069 lines) | stat: -rw-r--r-- 35,555 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
#! /usr/bin/python

# pydb.py -- a Python debugger!
# Modified original pdb.py

# (See pdb.doc for original documentation.)

import string
import sys
import linecache
import cmd
import bdb
import repr
import os
import posix


import pydbcmd
from pydbsupt import Breakpoint, effective, Display, DisplayNode

# Interaction prompt line will separate file and call info from code
# text using value of line_prefix string.  A newline and arrow may
# be to your liking.  You can set it once pydb is imported using the
# command "pydb.line_prefix = '\n% '".
# line_prefix = ': '    # Use this to get the old situation back
line_prefix = '\n-> '    # Probably a better default

class Pdb(bdb.Bdb, pydbcmd.Cmd):
    
    def __init__(self):
        bdb.Bdb.__init__(self)
        pydbcmd.Cmd.__init__(self)
        self.prompt = '(Pdb) '
        self.running = 0
        self.filetodebug = ""
        self.moduletodebug = ""
        self.nextbpnumber = 1
        self.latestbp = None     # for stack print routine
        self.cmdqueue = []
    
    def reset(self):
        bdb.Bdb.reset(self)
        self.forget()
    
    def forget(self):
        self.lineno = None
        self.stack = []
        self.curindex = 0
        self.curframe = None
    
    def setup(self, f, t):
        self.forget()
        self.stack, self.curindex = self.get_stack(f, t)
        self.curframe = self.stack[self.curindex][0]
    
    # Override Bdb methods (except user_call, for now)
    
    def user_line(self, frame):
        # This function is called when we stop or break at this line
        self.interaction(frame, None)
    
    def user_return(self, frame, return_value):
        # This function is called when a return trap is set here
        frame.f_locals['__return__'] = return_value
        print '--Return--'
        self.interaction(frame, None)
    
    def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
        # This function is called if an exception occurs,
        # but only if we are to stop at or just below this level
        frame.f_locals['__exception__'] = exc_type, exc_value
        if type(exc_type) == type(''):
            exc_type_name = exc_type
        else: exc_type_name = exc_type.__name__
        print exc_type_name + ':', repr.repr(exc_value)
        self.interaction(frame, exc_traceback)
    
    # General interaction function
    
    def interaction(self, frame, traceback):
        if self.running != 0:
            self.setup(frame, traceback)
            self.print_stack_entry(self.stack[self.curindex])
            self.display.displayAny(self.curframe)
            try:
                self.cmdloop()
            except KeyboardInterrupt:
                self.do_kill(None)
        self.latestbp = None    # Can forget it now that we're moving on
        self.forget()

    def default(self, line):
        if line[:1] == '!': line = line[1:]
        # Try both the local and "outer" environments.
        both = 0
        try:
            locals = self.curframe.f_locals
            globals = self.curframe.f_globals
            both = 1
        except:
            locals = self.outerlocals
            globals = self.outerglobals
        globals['__privileged__'] = 1
        try:
            code = compile(line + '\n', '<stdin>', 'single')
            exec code in globals, locals
        except:
            if both:
                # Failed with current frame, so try 'outer' world.
                locals = self.outerlocals
                globals = self.outerglobals
                globals['__privileged__'] = 1
                try:
                    code = compile(line + '\n', '<stdin>', 'single')
                    exec code in globals, locals
                except:
                    t, v = sys.exc_info()[:2]
                    if type(t) == type(''):
                        exc_type_name = t
                    else: exc_type_name = t.__name__
                    print '***', exc_type_name + ':', v
            else:
                t, v = sys.exc_info()[:2]
                if type(t) == type(''):
                    exc_type_name = t
                else: exc_type_name = t.__name__
                print '***', exc_type_name + ':', v

    # Override Bdb (note number of args is different)
    def set_break(self, filename, lineno, temporary=0):
        import linecache # Import as late as possible
        line = linecache.getline(filename, lineno)
        if not line:
            return 'That line does not exist!'
        if not self.breaks.has_key(filename):
            self.breaks[filename] = []
        list = self.breaks[filename]
        if not lineno in list:
            list.append(lineno)
        bp = Breakpoint(filename, lineno, temporary)
        print 'Breakpoint %d, at %s:%d.' %(bp.number, filename, lineno)
    
    # Override Bdb
    def break_here(self, frame):
        filename=frame.f_code.co_filename
        if not self.breaks.has_key(filename):
            return 0
        lineno=frame.f_lineno
        if not lineno in self.breaks[filename]:
            return 0
        # flag says ok to delete temp. bp
        (bp, flag) = effective(filename, lineno, frame)
        if bp:
            self.latestbp = bp.number
            if (flag and bp.temporary):
                self.do_delete(str(bp.number))
            return 1
        else:
            return 0

    # Command definitions, called by cmdloop()
    # The argument is the remaining string on the command line
    # Return true to exit from the command loop 
    
    do_h = pydbcmd.Cmd.do_help

    def do_break(self, arg, temporary = 0):
        if not arg:
            print self.get_all_breaks()
            return
        f_l = string.split(arg, ':')
        # Setting a breakpoint at func:line
        if len(f_l) == 2:
            filename = f_l[0]
            lineno = int(f_l[1])
            line = self.checkline(filename, lineno)
            if line:
                # now set the break point
                err = self.set_break(filename, lineno, temporary)
                if err: print '***', err
            return

        try:    
            lineno = int(arg)
            try:
                filename = self.curframe.f_code.co_filename
            except:
                filename = self.filetodebug # not tracing yet
        except:
            # Try function name as the argument
            try:
                func = arg
                if hasattr(func, 'im_func'):
                    func = func.im_func
                code = func.func_code
                lineno = code.co_firstlineno
                filename = code.co_filename
            except:
                # One more thing to try:
                answer = self.lineinfo("'" + arg + "'")
                if not answer[0]:
                    print '*** The specified object "', arg,
                    print '" is not a function'
                    return
                (junk, filename, l) = answer
                lineno = int(l)
        line = self.checkline(filename, lineno)
        if line:
            # now set the break point
            err = self.set_break(filename, line, temporary)
            if err: print '***', err
        return

    do_b = do_break
    
    def do_tbreak(self, arg):
        self.do_break(arg, 1)

    def checkline(self, filename, lineno):
        """Return line number of first line at or after input argument
        such that if the input points to a 'def', the returned line number
        is the first non-blank/non-comment line to follow.  If the input
        points to a blank or comment line, return 0.  At end of file,
        also return 0."""

        line = linecache.getline(filename, lineno)
        if not line:
            print 'End of file'
            return 0
        line = string.strip(line)
        # Don't allow setting breakpoint at a blank line
        if ( not line or (line[0] == '#') or
         (line[:3] == '"""') or line[:3] == "'''" ):
            print '*** Blank or comment'
            return 0
        # When a file is read in and a breakpoint is at
        # the 'def' statement, the system stops there at
        # code parse time.  We don't want that, so all breakpoints
        # set at 'def' statements are moved one line onward
        if line[:3] == 'def':
            incomment = ''
            while 1:
                lineno = lineno+1
                line = linecache.getline(filename, lineno)
                if not line:
                    print 'end of file'
                    return 0
                line = string.strip(line)
                if incomment:
                    if len(line) < 3: continue
                    if (line[-3:] == incomment):
                        incomment = ''
                    continue
                if not line: continue	# Blank line
                if len(line) >= 3:
                    if ( line[:3] == '"""' or line[:3] == "'''" ):
                        incomment = line[:3]
                        continue
                if line[0] != '#': break
        return lineno

    def do_enable(self, arg):
        args = string.split(arg)
        if args[0] == 'display':
            self.display_enable(args[1:], 1)
            return
        for i in args:
            bp = Breakpoint.bpbynumber[int(i)]
            if bp:
                bp.enable()

    def do_disable(self, arg):
        args = string.split(arg)
        if args[0] == 'display':
            self.display_enable(args[1:], 0)
            return
        for i in args:
            bp = Breakpoint.bpbynumber[int(i)]
            if bp:
                bp.disable()

    def display_enable(self, arg, flag):
        # arg is list of display points
        for i in arg:
            self.display.enable(int(i), flag)

    def do_condition(self, arg):
        # arg is breakpoint number and condition
        args = string.split(arg, ' ', 1)
        bpnum = int(string.strip(args[0]))
        try:
            cond = args[1]
        except:
            cond = None
        bp = Breakpoint.bpbynumber[bpnum]
        if bp:
            bp.cond = cond
            if not cond:
                print 'Breakpoint %d is now unconditional.' % (bpnum,)

    def do_ignore(self,arg):
        # arg is bp number followed by ignore count
        args = string.split(arg)
        bpnum = int(string.strip(args[0]))
        try:
            count = int(string.strip(args[1]))
        except:
            count = 0
        bp = Breakpoint.bpbynumber[bpnum]
        if bp:
            bp.ignore = count
            if (count > 0):
                reply = 'Will ignore next '
                if (count > 1):
                    reply = reply + '%d crossings' % (count,)
                else:
                    reply = reply + '1 crossing'
                print reply + ' of breakpoint %d.' % (bpnum,)
            else:
                print 'Will stop next time breakpoint %d is reached.' % (bpnum,)

    def do_delete(self, arg):
        # Delete from a list, or delete all if no arg
        numberlist = string.split(arg)
        if numberlist:
            for i in numberlist:
                number = int(i)
                bp = Breakpoint.bpbynumber[number]
                filename = bp.file
                lineno = bp.line
                if not self.breaks.has_key(filename):
                    return 'There are no breakpoints in that file!'
                if lineno not in self.breaks[filename]:
                    return 'There is no breakpoint there!'
                # If there's only one bp in the list for that file,line
                # pair, then remove the breaks entry
                if len(Breakpoint.bplist[filename, lineno]) == 1:
                    self.breaks[filename].remove(lineno)
                if not self.breaks[filename]:
                    del self.breaks[filename]
                bp.deleteMe()
                print 'Deleted breakpoint %d ' % (number,)
        else:  # delete them all
            for bp in Breakpoint.bpbynumber:
                if bp:
                    bp.deleteMe()
            self.breaks = {}
    
    def do_where(self, arg):
        if arg:
            count = int(string.strip(arg))
            # neg count possible ?? XXX
        else:
            count = 10000   # 'huge' number
        # Last two frames are internal to debugger and don't
        # get printed
        for j in range(len(self.stack)-1, 1, -1):
            self.frame_print(j)
            count = count -1
            if count == 0:
                break
    do_w = do_where

    def frame_print(self, j):
        prefix = '#%d ' % (len(self.stack) -1 - j,)
        res = self.format_stack_entry(self.stack[j], prefix)
        print string.split(res, '\n', 1)[0]

    def do_frame(self, arg):
        if not arg:
            self.frame_print(self.curindex)
        else:
            goto = int(string.strip(arg))
            # Should never happen, but if you want to be sure:
            # Replace -1 with -3 if limited to frames of debugged program
            if (goto < 0 or goto > len(self.stack)-1):
                return
            self.curindex = len(self.stack) - 1 - goto
            self.curframe = self.stack[self.curindex][0]
            self.frame_print(self.curindex)
            self.lineno = None
        
    
    def do_up(self, arg):
        # Replace 0 with 2 if up is limited to frames of debugged program
        if self.curindex == 2:
            print 'Initial frame selected; you cannot go up.'
        else:
            self.curindex = self.curindex - 1
            self.curframe = self.stack[self.curindex][0]
            self.frame_print(self.curindex)
            self.lineno = None
    do_u = do_up
    
    def do_down(self, arg):
        if self.curindex + 1 == len(self.stack):
            print 'Bottom (i.e., innermost) frame selected; you cannot go down.'
        else:
            self.curindex = self.curindex + 1
            self.curframe = self.stack[self.curindex][0]
            self.frame_print(self.curindex)
            self.lineno = None
    do_d = do_down
    
    def do_step(self, arg):
        self.set_step()
        return 1
    do_s = do_step
    
    def do_next(self, arg):
        self.set_next(self.curframe)
        return 1
    do_n = do_next
    
    def do_return(self, arg):
        self.set_return(self.curframe)
        return 1
    do_r = do_return
    
    def do_continue(self, arg):
        self.set_continue()
        return 1
    do_c = do_cont = do_continue
    
    def do_quit(self, arg):
        if self.running:
            self.set_quit()
            self.running = 0
        self.alldie = 1
        return 1
    do_q = do_quit

    def do_kill(self, arg):
        self.set_quit()
        self.running = 0
        return 1
    do_k = do_kill
    
    def do_args(self, arg):
        f = self.curframe
        co = f.f_code
        dict = f.f_locals
        n = co.co_argcount
        if co.co_flags & 4: n = n+1
        if co.co_flags & 8: n = n+1
        for i in range(n):
            name = co.co_varnames[i]
            print name, '=',
            if dict.has_key(name): print dict[name]
            else: print "*** undefined ***"
    do_a = do_args
    
    def do_retval(self, arg):
        if self.curframe.f_locals.has_key('__return__'):
            print self.curframe.f_locals['__return__']
        else:
            print '*** Not yet returned!'
    do_rv = do_retval
    
    def do_p(self, arg):
        try:
           l = self.curframe.f_locals
           g = self.curframe.f_globals
        except:
           l = self.outerlocals
           g = self.outerglobals
        g['__privileged__'] = 1
        try:
            value = eval(arg, g, l)
        except:
            return

        print `value`

    def do_list(self, arg):
        if self.filetodebug == "":
            print 'No symbol table is loaded.  Use the "file" command.'
            return
        self.lastcmd = 'list'
        last = None
        if arg:
            try:
                x = eval(arg, {}, {})
                if type(x) == type(()):
                    first, last = x
                    first = int(first)
                    last = int(last)
                    if last < first:
                        # Assume it's a count
                        last = first + last
                else:
                    first = max(1, int(x) - 5)
            except:
                print '*** Error in argument:', `arg`
                return
        elif self.lineno is None:
            if self.running == 0:
                first = 1
            else:
                first = max(1, self.curframe.f_lineno - 5)
        else:
            first = self.lineno + 1
        if last == None:
            last = first + 10
        if self.running == 0:
            filename = self.filetodebug
        else:
            filename = self.curframe.f_code.co_filename
        try:
            for lineno in range(first, last+1):
                line = linecache.getline(filename, lineno)
                if not line:
                    print '[EOF]'
                    break
                else:
                    s = string.rjust(`lineno`, 3)
                    if len(s) < 4: s = s + ' '
                    print s + '\t' + line,
                    self.lineno = lineno
        except KeyboardInterrupt:
            pass
    do_l = do_list

    def do_whatis(self, arg):
        try:
            value = eval(arg, self.curframe.f_globals, \
                    self.curframe.f_locals)
        except:
            print 'No symbol "%s" in current context.' % (arg,)
            return
        code = None
        # Is it a function?
        try: code = value.func_code
        except: pass
        if code:
            print 'type = Function', code.co_name
            return
        # Is it an instance method?
        try: code = value.im_func.func_code
        except: pass
        if code:
            print 'type = Method', code.co_name
            return
        # None of the above...
        print 'type = ' + type(value)
    do_ptype = do_whatis
    
    # Print a traceback starting at the top stack frame.
    # The most recently entered frame is printed last;
    # this is different from dbx and gdb, but consistent with
    # the Python interpreter's stack trace.
    # It is also consistent with the up/down commands (which are
    # compatible with dbx and gdb: up moves towards 'main()'
    # and down moves towards the most recent stack frame).
    
    def print_stack_trace(self):
        try:
            for frame_lineno in self.stack:
                self.print_stack_entry(frame_lineno)
        except KeyboardInterrupt:
            pass
    
    def print_stack_entry(self, frame_lineno, prompt_prefix=line_prefix):
        frame, lineno = frame_lineno
        prefix = ""
        if not self.stop_here(frame) and self.latestbp:
            prefix = "Breakpoint %d, "% (self.latestbp,)
        print self.format_stack_entry(frame_lineno, prefix)

    def format_stack_entry(self, frame_lineno, prefix=""):
        import linecache, repr, string
        frame, lineno = frame_lineno
        filename = frame.f_code.co_filename
        s = ' at ' + filename + ':' + `lineno`
        if frame.f_code.co_name:
            f = frame.f_code.co_name
        else:
            f = "<lambda>"
        if frame.f_locals.has_key('__args__'):
            args = frame.f_locals['__args__']
        else:
            args = None
        if args:
            a = '(' + repr.repr(args) + ')'
        else:
            a = '()'
        first_line = prefix + f + a + s + '\n'
        # Don't want the ?() at <string>:  line printed out; confuses ddd
        if first_line[:15] == '?() at <string>':
            return 'Issue "continue" command'
        second_line = `lineno` + '    ' 
        line = linecache.getline(filename, lineno)
        if line: second_line = second_line + string.strip(line)
        result = first_line + second_line
        if frame.f_locals.has_key('__return__'):
            rv = frame.f_locals['__return__']
            third_line = 'Value returned is $1 = ' + repr.repr(rv)
            result = result + '\n' + third_line
        return result
    
    def do_info(self,args):
        arglist = string.split(args)
        arg = arglist[0]
        if arg == 'program':
            if not self.running:
                print 'The program being debugged is not being run.'
            else:
                print 'Program stopped.\n'
                if self.latestbp:
                    print 'It stopped at breakpoint %d.' % (self.latestbp,)
            return
        if arg == 'line':
            #info line identifier
            if len(arglist) == 2:
                # lineinfo returns (item, file, lineno) or (None,)
                answer = self.lineinfo(arglist[1])
                if answer[0]:
                    print 'Lineinfo <%s> at %s:%s' % answer
                return
            #info line
            if self.lineno == None:
                try:
                    file = self.curframe.f_code.co_filename
                    line = self.curframe.f_lineno
                    print 'Line %s of "%s"' % (line, file)
                except:
                    print 'No line number information available.'
            else:
                print 'Line %s of "%s"' %\
                    (self.lineno, self.curframe.f_code.co_filename)
            return
        if arg == 'files':
            if self.filetodebug == "":
                pass
            else:
                print 'Symbols from "%s".' % (self.filetodebug,)
            return
        if arg == 'breakpoints':
            any = 0
            for b in Breakpoint.bpbynumber:
                if b:
                    if not any:
                        print "Num Type           Disp Enb Where"
                    any = 1
                    b.bpprint()
            if not any:
                print 'No breakpoints or watchpoints.'
            return
        if arg == 'display':
            if not self.display.displayAll():
                print 'There are no auto-display expressions now.'
            return
        print 'Undefined info command: "'+arg+'".  Try "help info".'

    def lineinfo(self, identifier):
        # Input is identifier in single quotes
        idstring = string.split(identifier, "'")
        if len(idstring) == 1:
            # not in single quotes
            id = string.strip(idstring[0])
        elif len(idstring) == 3:
            # quoted
            id = string.strip(idstring[1])
        else:
            return (None,)
        if id == '': return (None,)
        parts = string.split(id, '.')
        if parts[0] == 'self':
            # What could we do here?
            return (None,)
        # Best first guess at file to look at
        try:
            fname = self.curframe.f_code.co_filename
        except:
            fname = self.filetodebug # not tracing yet
        if len(parts) == 1:
            item = parts[0]
        else:
            # more than one part.  First is module, second is method/class
            module = parts[0] + '.py'
            item = parts[1]
            for dirname in sys.path:
                while os.path.islink(dirname):
                    dirname = os.readlink(dirname)
                fname = os.path.join(dirname, module)
                if os.path.exists(fname):
                    break    # take the first
        grepstring = 'egrep -n "(def|class) *%s *[(:]" %s /dev/null' % (item, fname)
        answer = os.popen(grepstring, 'r').readline()
        if answer:
            f, line, junk = string.split(answer, ':', 2)
            return(item, f,line)
        else:
            return (None,)

    def do_run(self, arg):
        # if running, clean up by killing up to outer loop and then run
        if self.running:
           self.cmdqueue = ['k', 'k', 'run '+arg]
           return
        print 'Starting program:'
        if self.filetodebug == "":
           print 'No executable file specified.'
           return
        oldp = self.prompt
        self.prompt = '(pydb) '
        # Mark running, reset command count
        self.running = 1
        # Clear hit counts on breakpoints
        for b in Breakpoint.bpbynumber:
            if b:
                b.hits = 0
        oldarg = sys.argv
        argv = [self.filetodebug]
        if arg:
            args = string.split(arg)
            argv = argv + args
        sys.argv = argv
        self.run('execfile(' + `self.filetodebug` + ')',
               {'__name__': '__main__'})
        sys.argv = oldarg
        self.prompt = oldp

    def do_file(self, fname):
        if fname == "":
            if self.filetodebug == "":
                print 'No exec file now.\nNo symbol file now.'
                return
            else:
                # should confirm this per
                #Discard symbol table from `/work/.../ddd/cxxtest'? (y or n) n
                # XXX 
                # del module
                sys.path = self.originalpath[:]
                self.filetodebug == ""
        else:
            path, module = os.path.split(fname)
            module = string.split(module, '.')[0]
            sys.path = self.originalpath[:]
            sys.path.insert(0, path)
            # XXX how to clean up name space ??
            if self.moduletodebug:
                try:
                    # del self.moduletodebug
                    self.moduletodebug = ''
                except:
                    pass
            line = "import " + module + '\n'
            code = compile(line, '<stdin>', 'single')
            exec code in self.outerglobals, self.outerlocals
            # Could print 'Reading symbols from fname...done.' for gdb conformance
            self.filetodebug = fname
            self.moduletodebug = module

    # Ultimately for interrogating settable attributes of the debugger
    def do_show(self, arg):
        if arg == 'language':
            print 'The current source language is "auto; currently python".'

    def do_pwd(self, arg):
        print 'Working directory ' + os.getcwd() + '.'

    def do_cd(self, arg):
        if arg == '':
           do_pwd(arg)
        else:
           os.chdir(arg)
        return

    def do_display(self, arg):
        if not arg:
            # Display anything active
            self.display.displayAny(self.curframe)
        else:
            # Set up a display
            arglist = string.split(arg)
            if len(arglist) == 2:
               (format, variable) = tuple(arglist)
            else:
               format = ""
               variable = arglist[0]
            dp = DisplayNode(self.curframe, variable, format)
            res = dp.checkValid(self.curframe)
            print res
        return
    
    def do_undisplay(self, arg):
        args = string.split(arg)
        for i in args:
            self.display.deleteOne(int(i))

    # Help methods (originally derived from pdb.doc)

    def help_help(self):
        self.help_h()

    def help_h(self):
        print """h(elp)
    Without argument, print the list of available commands.
    With a command name as argument, print help about that command.
    "help exec" gives help on the ! command"""

    def help_where(self):
        self.help_w()

    def help_w(self):
        print """w(here)
    Print a stack trace, with the most recent frame at the bottom."""

    def help_down(self):
        self.help_d()

    def help_d(self):
        print """d(own)
    Move the current frame one level down in the stack trace
    (to an older frame)."""

    def help_up(self):
        self.help_u()

    def help_u(self):
        print """u(p)
    Move the current frame one level up in the stack trace
    (to a newer frame)."""

    def help_break(self):
        self.help_b()

    def help_b(self):
        print """b(reak) [file:lineno] [lineno | method]
    With a line number argument, set a break there in the current
    or specified file.  With a method name, set a break at the first
    executable line after the 'def'.  Without argument, list all breaks."""

    def help_tbreak(self):
        print """tbreak  same arguments as break, but breakpoint is
    removed when first hit."""

    def help_delete(self):
        print """delete [bpnumbers]
    With a space separated list of breakpoint numbers, deletes
    those breakpoints.  Without the argument, deletes all breakpoints."""

    def help_enable(self):
        print """enable [display] bpnumber
    Enables the breakpoints given as a space separated list of bp numbers.
    If the word 'display' is present, the indicated display(s) is/are
    enabled."""

    def help_disable(self):
        print """disable [display] bpnumber
    Disables the breakpoints given as a space separated list of bp numbers.
    If the word 'display' is present, the indicated display(s) is/are
    disabled."""

    def help_ignore(self):
        print """ignore bpnumber count
    Sets the ignore count for the given breakpoint number.  A breakpoint
    becomes active when the ignore count is zero.  When non-zero, the
    count is decremented each time the breakpoint is reached and the
    breakpoint is not disabled and any associated condition evaluates
    to true."""

    def help_condition(self):
        print """condition bpnumber str_condition
    str_condition is a string specifying an expression which
    must evaluate to true before the breakpoint is honored.
    If str_condition is absent, any existing condition is removed;
    i.e., the breakpoint is made unconditional."""

    def help_step(self):
        self.help_s()

    def help_s(self):
        print """s(tep)
    Execute the current line, stop at the first possible occasion
    (either in a function that is called or in the current function)."""

    def help_next(self):
        self.help_n()

    def help_n(self):
        print """n(ext)
    Continue execution until the next line in the current function
    is reached or it returns."""

    def help_return(self):
        self.help_r()

    def help_r(self):
        print """r(eturn)
    Continue execution until the current function returns."""

    def help_continue(self):
        self.help_c()

    def help_cont(self):
        self.help_c()

    def help_c(self):
        print """c(ont(inue))
    Continue execution, only stop when a breakpoint is encountered."""

    def help_list(self):
        self.help_l()

    def help_l(self):
        print """l(ist) [first [,last]]
    List source code for the current file.
    Without arguments, list 11 lines around the current line
    or continue the previous listing.
    With one argument, list 11 lines starting at that line.
    With two arguments, list the given range;
    if the second argument is less than the first, it is a count."""

    def help_args(self):
        self.help_a()

    def help_a(self):
        print """a(rgs)
    Print the arguments of the current function."""

    def help_p(self):
        print """p expression
    Print the value of the expression."""

    def help_exec(self):
        print """(!) statement
    Execute the (one-line) statement in the context of
    the current stack frame.
    The exclamation point can be omitted unless the first word
    of the statement resembles a debugger command.
    To assign to a global variable you must always prefix the
    command with a 'global' command, e.g.:
    (Pdb) global list_options; list_options = ['-l']
    (Pdb)"""

    def help_quit(self):
        self.help_q()

    def help_q(self):
        print """Quit from the debugger."""

    def help_kill(self):
        self.help_k()

    def help_k(self):
        print """Kill the program being debugged
    The program being executed is aborted."""

    def help_info(self):
        print """Generic command for showing things about the program being debugged.

        List of info subcommands:

        info program     -- Execution status of the program
        info line        -- If "info line arg", line number and full path to file containing
                            'def' for arg; if arg omitted, display where currently stopped.
        info display     -- Expressions to display when program stops
        info breakpoints -- Status of user-settable breakpoints
        info files       -- Names of targets and files being debugged"""

    def help_show(self):
        print """Generic command for showing things about the debugger.

        List of show subcommands:

        show language -- Show the current source language """

    def help_cd(self):
        print 'Change to directory specified by argument.'

    def help_display(self):
        print """display [format] expression
        Displays the value of the expression whenever the program stops at
        a breakpoint and the expression is valid in the current namespace
        which must be the one in which the display command was issued.
        Format is /a where a is one of[xotfcs] for hex, octal, binary (twos),
        float, chr and string."""

    def help_undisplay(self):
        print """undisplay dnumber
        Delete the displays listed in the space separated list dnumber."""

    def help_file(self):
        print """Use 'file' as the program to be debugged.  It is the
        program executed when you use the 'run' command."""
    
    def help_frame(self):
        print """frame [arg]
        Prints the current frame location if no argument is given;
        Changes to the frame 'arg' if 'arg' is present.  Frame 0 is
        the newest one."""

    def help_pwd(self):
        print 'Print working directory.'

    def help_run(self):
        print """run [arg1 [arg2 [...]]]
        Run the program to be debugged.  Arguments
        are space separated and are inserted into sys.argv
        following the file name from the 'file' command, all
        for the delectation of the program under study."""
    
    def help_whatis(self):
        print """whatis arg
        Prints the type of the argument."""

    def help_ptype(self):
        print """ptype arg
        Prints the type of the argument."""

    help_pydb = help_help

# Simplified interface

def run(statement, globals=None, locals=None):
    Pdb().run(statement, globals, locals)

def runeval(expression, globals=None, locals=None):
    return Pdb().runeval(expression, globals, locals)

def runctx(statement, globals, locals):
    # B/W compatibility
    run(statement, globals, locals)

def runcall(*args):
    return apply(Pdb().runcall, args)

def set_trace():
    Pdb().set_trace()

# Post-Mortem interface

def post_mortem(t):
    p = Pdb()
    p.reset()
    while t.tb_next <> None: t = t.tb_next
    p.interaction(t.tb_frame, t)

def pm():
    import sys
    post_mortem(sys.last_traceback)


# When invoked as main program, invoke the debugger on a script
if __name__=='__main__':
    import sys
    import os

    outer = Pdb()
    outer.prompt = "(Pdb) "
    outer.alldie = 0
    outer.display = Display()
    outer.reset()
    outer.originalpath = sys.path[:]
    while not outer.alldie:
        try:
          1 + ''
        except:
          frame = sys.exc_info()[2].tb_frame
          outer.outerlocals = frame.f_locals
          outer.outerglobals = frame.f_locals
        try:
            outer.cmdloop()
        except AttributeError, msg:
            print "Invalid command in outer loop: %s\n" % (msg,)
        except KeyboardInterrupt:
            pass