16 __all__
= ["LogReader", "ENTER", "EXIT", "LINE"]
27 StopIteration = IndexError
31 def __init__(self
, logfn
):
34 # (fileno, lineno) -> filename, funcname
37 self
._reader
= _hotshot
.logreader(logfn
)
38 self
._nextitem
= self
._reader
.next
39 self
._info
= self
._reader
.info
40 if self
._info
.has_key('current-directory'):
41 self
.cwd
= self
._info
['current-directory']
45 self
._append
= self
._stack
.append
46 self
._pop
= self
._stack
.pop
48 def addinfo(self
, key
, value
):
49 """This method is called for each additional ADD_INFO record.
51 This can be overridden by applications that want to receive
52 these events. The default implementation does not need to be
53 called by alternate implementations.
55 The initial set of ADD_INFO records do not pass through this
56 mechanism; this is only needed to receive notification when
57 new values are added. Subclasses can inspect self._info after
58 calling LogReader.__init__().
62 def get_filename(self
, fileno
):
64 return self
._filemap
[fileno
]
66 raise ValueError, "unknown fileno"
68 def get_filenames(self
):
69 return self
._filemap
.values()
71 def get_fileno(self
, filename
):
72 filename
= os
.path
.normcase(os
.path
.normpath(filename
))
73 for fileno
, name
in self
._filemap
.items():
76 raise ValueError, "unknown filename"
78 def get_funcname(self
, fileno
, lineno
):
80 return self
._funcmap
[(fileno
, lineno
)]
82 raise ValueError, "unknown function location"
85 # This adds an optional (& ignored) parameter to next() so that the
86 # same bound method can be used as the __getitem__() method -- this
87 # avoids using an additional method call which kills the performance.
89 def next(self
, index
=0):
92 what
, tdelta
, fileno
, lineno
= self
._nextitem
()
94 # logreader().next() returns None at the end
98 # handle the most common cases first
100 if what
== WHAT_ENTER
:
101 filename
, funcname
= self
._decode
_location
(fileno
, lineno
)
102 self
._append
((filename
, funcname
, lineno
))
103 return what
, (filename
, lineno
, funcname
), tdelta
105 if what
== WHAT_EXIT
:
106 filename
, funcname
, lineno
= self
._pop
()
107 return what
, (filename
, lineno
, funcname
), tdelta
109 if what
== WHAT_LINENO
:
110 filename
, funcname
, firstlineno
= self
._stack
[-1]
111 return what
, (filename
, lineno
, funcname
), tdelta
113 if what
== WHAT_DEFINE_FILE
:
114 filename
= os
.path
.normcase(os
.path
.normpath(tdelta
))
115 self
._filemap
[fileno
] = filename
116 elif what
== WHAT_DEFINE_FUNC
:
117 filename
= self
._filemap
[fileno
]
118 self
._funcmap
[(fileno
, lineno
)] = (filename
, tdelta
)
119 elif what
== WHAT_ADD_INFO
:
120 # value already loaded into self.info; call the
121 # overridable addinfo() handler so higher-level code
122 # can pick up the new value
123 if tdelta
== 'current-directory':
125 self
.addinfo(tdelta
, lineno
)
127 raise ValueError, "unknown event type"
129 if sys
.version
< "2.2":
130 # Don't add this for newer Python versions; we only want iteration
131 # support, not general sequence support.
141 def _decode_location(self
, fileno
, lineno
):
143 return self
._funcmap
[(fileno
, lineno
)]
146 # This should only be needed when the log file does not
147 # contain all the DEFINE_FUNC records needed to allow the
148 # function name to be retrieved from the log file.
150 if self
._loadfile
(fileno
):
151 filename
= funcname
= None
153 filename
, funcname
= self
._funcmap
[(fileno
, lineno
)]
155 filename
= self
._filemap
.get(fileno
)
157 self
._funcmap
[(fileno
, lineno
)] = (filename
, funcname
)
158 return filename
, funcname
160 def _loadfile(self
, fileno
):
162 filename
= self
._filemap
[fileno
]
164 print "Could not identify fileId", fileno
168 absname
= os
.path
.normcase(os
.path
.join(self
.cwd
, filename
))
174 st
= parser
.suite(fp
.read())
177 # Scan the tree looking for def and lambda nodes, filling in
178 # self._funcmap with all the available information.
179 funcdef
= symbol
.funcdef
180 lambdef
= symbol
.lambdef
182 stack
= [st
.totuple(1)]
188 except (IndexError, TypeError):
191 self
._funcmap
[(fileno
, tree
[2][2])] = filename
, tree
[2][1]
193 self
._funcmap
[(fileno
, tree
[1][2])] = filename
, "<lambda>"
194 stack
.extend(list(tree
[1:]))