Merge lp:~pconnell/entrails/sextant into lp:entrails
- sextant
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | ChrisD |
Approved revision: | 40 |
Merged at revision: | 25 |
Proposed branch: | lp:~pconnell/entrails/sextant |
Merge into: | lp:entrails |
Diff against target: |
497 lines (+199/-89) 4 files modified
src/entrails/_filter.py (+5/-6) src/entrails/_output.py (+51/-24) src/entrails/_trace.py (+140/-56) src/trace.py (+3/-3) |
To merge this branch: | bzr merge lp:~pconnell/entrails/sextant |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
ChrisD | Approve | ||
Martin Morrison | Pending | ||
Review via email: mp+233257@code.launchpad.net |
This proposal supersedes a proposal from 2014-08-15.
Commit message
Various changes made while getting entrails working with sextant.
Main externally-visible change is that entrails makes a slightly better effort to find the names of classes corresponding to method calls (i.e. get qualnames rather than just function names).
Some bits of internal cleanup too: have a properly structured 'Event' object rather than the weird sort-of-
Description of the change
Various changes made while getting entrails working with sextant.
Main externally-visible change is that entrails makes a slightly better effort to find the names of classes corresponding to method calls (i.e. get qualnames rather than just function names).
Some bits of internal cleanup too: have a properly structured 'Event' object rather than the weird sort-of-
Martin Morrison (isoschiz) wrote : Posted in a previous version of this proposal | # |
Phil Connell (pconnell) wrote : Posted in a previous version of this proposal | # |
> EnTrails could really do with some UT to make me more confident that you
> hadn't missed any of the name changes... There are at least a couple inline
> that look wrong, but I've not read every line carefully to be sure there
> aren't others.
Completely agree on the UT front, but I'm not especially concerned about this right now:
- I'm pretty sure nobody uses any of this stuff.
- Outputting to sextant is likely to supercede the existing output formats for all practical purposes, and I think the most risk is to the outputs.
As it happens, after taking another look I'm pretty convinced that the HTMLOutput stuff was already not working...
ChrisD (gingerchris) wrote : | # |
Ideal
ChrisD (gingerchris) : | # |
Preview Diff
1 | === modified file 'src/entrails/_filter.py' | |||
2 | --- src/entrails/_filter.py 2014-07-10 18:08:27 +0000 | |||
3 | +++ src/entrails/_filter.py 2014-09-03 20:05:48 +0000 | |||
4 | @@ -36,9 +36,9 @@ | |||
5 | 36 | """ | 36 | """ |
6 | 37 | matches = ("/usr/lib", "/usr/local/lib", "/usr/lib64", "/twisted", "/zope") | 37 | matches = ("/usr/lib", "/usr/local/lib", "/usr/lib64", "/twisted", "/zope") |
7 | 38 | 38 | ||
9 | 39 | def filter(self, frameobject): | 39 | def filter(self, event): |
10 | 40 | for m in self.matches: | 40 | for m in self.matches: |
12 | 41 | if m in frameobject['co_filename']: | 41 | if m in event.code.co_filename: |
13 | 42 | return False | 42 | return False |
14 | 43 | return True | 43 | return True |
15 | 44 | 44 | ||
16 | @@ -54,8 +54,7 @@ | |||
17 | 54 | "_utils", | 54 | "_utils", |
18 | 55 | )) | 55 | )) |
19 | 56 | 56 | ||
24 | 57 | def filter(self, frameobject): | 57 | def filter(self, event): |
25 | 58 | filename = frameobject["co_filename"] | 58 | filename = event.code.co_filename |
26 | 59 | return not any(filename.endswith(module) | 59 | return not any(filename.endswith(module) for module in self.modules) |
23 | 60 | for module in self.modules) | ||
27 | 61 | 60 | ||
28 | 62 | 61 | ||
29 | === modified file 'src/entrails/_output.py' | |||
30 | --- src/entrails/_output.py 2014-07-07 12:48:49 +0000 | |||
31 | +++ src/entrails/_output.py 2014-09-03 20:05:48 +0000 | |||
32 | @@ -36,14 +36,25 @@ | |||
33 | 36 | def __init__(self): | 36 | def __init__(self): |
34 | 37 | pass | 37 | pass |
35 | 38 | def close(self): | 38 | def close(self): |
42 | 39 | pass | 39 | """Invoked when no further tracing data is to be output.""" |
43 | 40 | def enter(self, o): | 40 | pass |
44 | 41 | pass | 41 | def enter(self, event): |
45 | 42 | def exit(self, o): | 42 | """Invoked with an entrails event, when a function is called.""" |
46 | 43 | pass | 43 | pass |
47 | 44 | def exception(self, o): | 44 | def exit(self, event): |
48 | 45 | """Invoked with an entrails event, when a function returns.""" | ||
49 | 46 | pass | ||
50 | 47 | def exception(self, event): | ||
51 | 48 | """ | ||
52 | 49 | Invoked with an entrails event, when an exception is raised. | ||
53 | 50 | |||
54 | 51 | As an exception propagates up the call stack, this method is called for | ||
55 | 52 | every frame in which the exception is raised. | ||
56 | 53 | |||
57 | 54 | """ | ||
58 | 45 | pass | 55 | pass |
59 | 46 | def logging(self, o, event_type_string): | 56 | def logging(self, o, event_type_string): |
60 | 57 | #@@@ not used? | ||
61 | 47 | pass | 58 | pass |
62 | 48 | 59 | ||
63 | 49 | 60 | ||
64 | @@ -76,15 +87,15 @@ | |||
65 | 76 | return "Call " + o.short_call_string() | 87 | return "Call " + o.short_call_string() |
66 | 77 | 88 | ||
67 | 78 | def _default_exit_func(self, o): | 89 | def _default_exit_func(self, o): |
69 | 79 | return "Return from {0} with value {1}".format(o["co_name"], | 90 | return "Return from {0} with value {1}".format(o.code.co_name, |
70 | 80 | o.short_return_value()) | 91 | o.short_return_value()) |
71 | 81 | 92 | ||
72 | 82 | def _default_exception_func(self, o): | 93 | def _default_exception_func(self, o): |
73 | 83 | return "Encountered exception" #@@@ more info TODO | 94 | return "Encountered exception" #@@@ more info TODO |
74 | 84 | 95 | ||
75 | 85 | def _default_prefix_func(self, o): | 96 | def _default_prefix_func(self, o): |
78 | 86 | return ("[%s][%03d:%s]" + " "*self.depth) % (o["label"], | 97 | return ("[%s][%03d:%s]" + " "*self.depth) % (o.label, |
79 | 87 | o["f_lineno"], | 98 | o.frame.f_lineno, |
80 | 88 | _utils.pretty_resize_string(o.nice_filename(), 15, exact=True)) | 99 | _utils.pretty_resize_string(o.nice_filename(), 15, exact=True)) |
81 | 89 | 100 | ||
82 | 90 | def enter(self, o): | 101 | def enter(self, o): |
83 | @@ -103,7 +114,7 @@ | |||
84 | 103 | 114 | ||
85 | 104 | def logging(self, o, event_type_string): | 115 | def logging(self, o, event_type_string): |
86 | 105 | self.safe_write("ERROR TRACING with event: %s on line %d in %s\n" % ( | 116 | self.safe_write("ERROR TRACING with event: %s on line %d in %s\n" % ( |
88 | 106 | event_type_string, o["f_lineno"], o["co_filename"])) | 117 | event_type_string, o.frame.f_lineno, o.code.co_filename)) |
89 | 107 | return None | 118 | return None |
90 | 108 | 119 | ||
91 | 109 | 120 | ||
92 | @@ -160,17 +171,17 @@ | |||
93 | 160 | return None | 171 | return None |
94 | 161 | 172 | ||
95 | 162 | def exception(self, o): | 173 | def exception(self, o): |
98 | 163 | print("EXCEPTION on line {0} in {1}".format(o["f_lineno"], | 174 | print("EXCEPTION on line {0} in {1}".format(o.frame.f_lineno, |
99 | 164 | o["co_filename"])) | 175 | o.code.co_filename)) |
100 | 165 | for k, v in o.items(): | 176 | for k, v in o.items(): |
101 | 166 | print(k + ": " + str(v)) | 177 | print(k + ": " + str(v)) |
102 | 167 | return None | 178 | return None |
103 | 168 | 179 | ||
104 | 169 | def logging(self, o, event_type_string): | 180 | def logging(self, o, event_type_string): |
109 | 170 | print("ERROR TRACING with event: {0} on line {1} in {2}".format(event_type_string, | 181 | print("ERROR TRACING with event: {0} on line {1} in {2}".format( |
110 | 171 | o["f_lineno"], | 182 | event_type_string, |
111 | 172 | o["co_filename"])) | 183 | o.frame.f_lineno, |
112 | 173 | return None | 184 | o.code.co_filename)) |
113 | 174 | 185 | ||
114 | 175 | 186 | ||
115 | 176 | class HTMLOutput(EntrailsOutput): | 187 | class HTMLOutput(EntrailsOutput): |
116 | @@ -186,14 +197,18 @@ | |||
117 | 186 | self.f.write("\n</body></html>\n") | 197 | self.f.write("\n</body></html>\n") |
118 | 187 | self.f.close() | 198 | self.f.close() |
119 | 188 | 199 | ||
121 | 189 | def enter(self, codeframe): | 200 | def enter(self, event): |
122 | 190 | self.depth += 1 | 201 | self.depth += 1 |
124 | 191 | self.f.write(" > "*self.depth + "<b>Now entering:</b> " + codeframe["name"] + "<br />\n") | 202 | self.f.write(" > " * self.depth + |
125 | 203 | "<b>Now entering:</b> " + | ||
126 | 204 | event.code.co_name + "<br />\n") | ||
127 | 192 | return None | 205 | return None |
128 | 193 | 206 | ||
130 | 194 | def exit(self, codeframe): | 207 | def exit(self, event): |
131 | 195 | self.depth -= 1 | 208 | self.depth -= 1 |
133 | 196 | self.f.write(" > "*(self.depth+1) + "<b>and exiting:</b> " + codeframe["name"] + " with return value " + str(codeframe["return_value"]) + "<br />\n") | 209 | self.f.write(" > " * (self.depth + 1) + "<b>and exiting:</b> " + |
134 | 210 | event.code.co_name + " with return value " + | ||
135 | 211 | str(event.return_value) + "<br />\n") | ||
136 | 197 | return None | 212 | return None |
137 | 198 | 213 | ||
138 | 199 | def exception(self, o): | 214 | def exception(self, o): |
139 | @@ -216,10 +231,22 @@ | |||
140 | 216 | 231 | ||
141 | 217 | def write_out(self, codeframe, event_type): | 232 | def write_out(self, codeframe, event_type): |
142 | 218 | event = ET.SubElement(self.root, "event") | 233 | event = ET.SubElement(self.root, "event") |
147 | 219 | event.attrib = {"timestamp" : "{0:f}".format(codeframe["timestamp"])} | 234 | event.attrib = {"timestamp" : "{0:f}".format(codeframe.timestamp)} |
148 | 220 | for k, v in codeframe.items(): | 235 | info = { |
149 | 221 | if k != "timestamp": | 236 | "label": codeframe.label, |
150 | 222 | ET.SubElement(event, k).text = str(v) | 237 | "f_lineno": codeframe.frame.f_lineno, |
151 | 238 | "f_back": codeframe.frame.f_back, | ||
152 | 239 | "f_locals": codeframe.frame.f_locals, | ||
153 | 240 | "co_name": codeframe.code.co_name, | ||
154 | 241 | "co_argcount": codeframe.code.co_argcount, | ||
155 | 242 | "co_filename": codeframe.code.co_filename, | ||
156 | 243 | "co_names": codeframe.code.co_names, | ||
157 | 244 | "co_varnames": codeframe.code.co_varnames, | ||
158 | 245 | } | ||
159 | 246 | if codeframe.return_value is not None: | ||
160 | 247 | info["return_value"] = codeframe.return_value | ||
161 | 248 | for k, v in info.items(): | ||
162 | 249 | ET.SubElement(event, k).text = str(v) | ||
163 | 223 | ET.SubElement(event, "type").text = event_type | 250 | ET.SubElement(event, "type").text = event_type |
164 | 224 | ET.SubElement(event, "call_string").text = codeframe.call_string() | 251 | ET.SubElement(event, "call_string").text = codeframe.call_string() |
165 | 225 | ET.SubElement(event, "short_call_string").text = codeframe.short_call_string() | 252 | ET.SubElement(event, "short_call_string").text = codeframe.short_call_string() |
166 | 226 | 253 | ||
167 | === modified file 'src/entrails/_trace.py' | |||
168 | --- src/entrails/_trace.py 2014-07-07 12:48:49 +0000 | |||
169 | +++ src/entrails/_trace.py 2014-09-03 20:05:48 +0000 | |||
170 | @@ -12,6 +12,7 @@ | |||
171 | 12 | 12 | ||
172 | 13 | __all__ = ( | 13 | __all__ = ( |
173 | 14 | "Entrails", | 14 | "Entrails", |
174 | 15 | "Event", | ||
175 | 15 | ) | 16 | ) |
176 | 16 | 17 | ||
177 | 17 | 18 | ||
178 | @@ -19,29 +20,85 @@ | |||
179 | 19 | import os.path | 20 | import os.path |
180 | 20 | import sys | 21 | import sys |
181 | 21 | import time | 22 | import time |
182 | 23 | import types | ||
183 | 22 | 24 | ||
184 | 23 | from . import _filter | 25 | from . import _filter |
185 | 24 | from . import _utils | 26 | from . import _utils |
186 | 25 | 27 | ||
187 | 26 | 28 | ||
198 | 27 | class _Frame(dict): | 29 | class Event(object): |
199 | 28 | """ | 30 | """ |
200 | 29 | Holds information about a frame. | 31 | Holds information about a traced event. |
201 | 30 | 32 | ||
202 | 31 | Use as a dict to access the important bits of the frame and arg | 33 | Attributes: |
203 | 32 | parameters, along with timestamp and label. Also has convienience | 34 | arg: Additional argument passed to the sys.settrace-installed function. |
204 | 33 | methods to generate function call string and class info. This is | 35 | code: Code object being executed. |
205 | 34 | supplied to an output adapter's call(), return() and exception() | 36 | frame: Frame object for the executing code. |
206 | 35 | methods. | 37 | label: String passed when event was created. |
207 | 36 | """ | 38 | return_value: For function exit events the value returned, otherwise |
208 | 39 | None. | ||
209 | 40 | timestamp: Time, in seconds relative to initialisation, at which the | ||
210 | 41 | event occurred. | ||
211 | 42 | |||
212 | 43 | Instances are supplied to an output adapter's call(), return() and | ||
213 | 44 | exception() methods. | ||
214 | 45 | |||
215 | 46 | """ | ||
216 | 47 | |||
217 | 48 | def __init__(self, frame, event, arg, | ||
218 | 49 | label, timestamp): | ||
219 | 50 | """ | ||
220 | 51 | Initialise this event. | ||
221 | 52 | |||
222 | 53 | frame: | ||
223 | 54 | Frame object passed to trace function. | ||
224 | 55 | event: | ||
225 | 56 | Event string passed to trace function. | ||
226 | 57 | arg: | ||
227 | 58 | Final argument passed to trace function. | ||
228 | 59 | label: | ||
229 | 60 | String identifying the context for the event. | ||
230 | 61 | timestamp: | ||
231 | 62 | Time, in seconds relative to initialisation, at which the event | ||
232 | 63 | occurred. | ||
233 | 64 | |||
234 | 65 | """ | ||
235 | 66 | self.arg = arg | ||
236 | 67 | self.code = frame.f_code | ||
237 | 68 | self.frame = frame | ||
238 | 69 | self.label = label | ||
239 | 70 | self.timestamp = timestamp | ||
240 | 71 | |||
241 | 72 | self.return_value = (arg if event == "return" else None) | ||
242 | 73 | |||
243 | 74 | def basename(self): | ||
244 | 75 | """Returns the name of the function for this event's frame.""" | ||
245 | 76 | cls, method = self._retrieve_cls_method() | ||
246 | 77 | if method is not None: | ||
247 | 78 | try: | ||
248 | 79 | name = method.__qualname__ | ||
249 | 80 | except AttributeError: | ||
250 | 81 | # Old version, < 3.3. | ||
251 | 82 | name = "{}.{}".format(cls.__name__, method.__name__) | ||
252 | 83 | else: | ||
253 | 84 | name = self.frame.f_code.co_name | ||
254 | 85 | |||
255 | 86 | return name | ||
256 | 87 | |||
257 | 88 | def qualname(self): | ||
258 | 89 | """Returns the fully-qualified function name for this event's frame.""" | ||
259 | 90 | return "{}.{}".format(self.frame.f_globals["__name__"], | ||
260 | 91 | self.basename()) | ||
261 | 92 | |||
262 | 37 | def parameters(self): | 93 | def parameters(self): |
263 | 38 | """ | 94 | """ |
264 | 39 | Returns dict of form {parameter_name: argument_value}. | 95 | Returns dict of form {parameter_name: argument_value}. |
265 | 40 | """ | 96 | """ |
266 | 41 | # We rely on CPython putting arguments at start of f_locals (in reverse | 97 | # We rely on CPython putting arguments at start of f_locals (in reverse |
267 | 42 | # order for some reason...). This is undocumented, but consistent. | 98 | # order for some reason...). This is undocumented, but consistent. |
270 | 43 | parameters = [(k, v) for k, v in self["f_locals"].items() | 99 | parameters = [(k, v) |
271 | 44 | if k in self["co_varnames"][0:self["co_argcount"]]] | 100 | for k, v in self.frame.f_locals.items() |
272 | 101 | if k in self.code.co_varnames[0:self.code.co_argcount]] | ||
273 | 45 | parameters.reverse() | 102 | parameters.reverse() |
274 | 46 | return collections.OrderedDict(parameters) | 103 | return collections.OrderedDict(parameters) |
275 | 47 | 104 | ||
276 | @@ -79,14 +136,13 @@ | |||
277 | 79 | 136 | ||
278 | 80 | Only designed for 'call' event (i.e. function entry) | 137 | Only designed for 'call' event (i.e. function entry) |
279 | 81 | """ | 138 | """ |
280 | 139 | output = '{}({})'.format(self.qualname(), | ||
281 | 140 | ', '.join(self.string_arguments())) | ||
282 | 82 | if self.is_method(): | 141 | if self.is_method(): |
288 | 83 | return '{0}.{1}({2})'.format(self.instance_name(), self['co_name'], | 142 | output += ' on instance {}'.format(self.instance_name()) |
289 | 84 | ', '.join(self.string_arguments())) | 143 | return output |
285 | 85 | else: | ||
286 | 86 | return '{0}({1})'.format(self['co_name'], | ||
287 | 87 | ', '.join(self.string_arguments())) | ||
290 | 88 | 144 | ||
292 | 89 | def short_call_string(self, length=40): | 145 | def short_call_string(self, length=80): |
293 | 90 | """ | 146 | """ |
294 | 91 | Returns a pretty string of the function call, shortened to at | 147 | Returns a pretty string of the function call, shortened to at |
295 | 92 | most length characters. | 148 | most length characters. |
296 | @@ -95,7 +151,7 @@ | |||
297 | 95 | """ | 151 | """ |
298 | 96 | return _utils.pretty_resize_string(self.call_string(), length) | 152 | return _utils.pretty_resize_string(self.call_string(), length) |
299 | 97 | 153 | ||
301 | 98 | def short_return_value(self, length=40): | 154 | def short_return_value(self, length=80): |
302 | 99 | """ | 155 | """ |
303 | 100 | Returns a pretty string representation of the return value, | 156 | Returns a pretty string representation of the return value, |
304 | 101 | shortened to at most length characters. | 157 | shortened to at most length characters. |
305 | @@ -103,7 +159,7 @@ | |||
306 | 103 | Only designed for 'call' event (i.e. function entry) | 159 | Only designed for 'call' event (i.e. function entry) |
307 | 104 | """ | 160 | """ |
308 | 105 | try: | 161 | try: |
310 | 106 | return _utils.pretty_resize_string(repr(self['return_value']), length) | 162 | return _utils.pretty_resize_string(repr(self.return_value), length) |
311 | 107 | except: | 163 | except: |
312 | 108 | return _utils.pretty_resize_string('<repr error>', length) | 164 | return _utils.pretty_resize_string('<repr error>', length) |
313 | 109 | 165 | ||
314 | @@ -120,7 +176,7 @@ | |||
315 | 120 | """ | 176 | """ |
316 | 121 | Nice filename, without directory segment. | 177 | Nice filename, without directory segment. |
317 | 122 | """ | 178 | """ |
319 | 123 | return os.path.basename(self["co_filename"]) | 179 | return os.path.basename(self.code.co_filename) |
320 | 124 | 180 | ||
321 | 125 | def is_method(self): | 181 | def is_method(self): |
322 | 126 | """ | 182 | """ |
323 | @@ -131,7 +187,7 @@ | |||
324 | 131 | 187 | ||
325 | 132 | TODO: What happens for class methods/other decorated methods? | 188 | TODO: What happens for class methods/other decorated methods? |
326 | 133 | """ | 189 | """ |
328 | 134 | if self['co_varnames'] and self['co_varnames'][0] == 'self': | 190 | if self.code.co_varnames and self.code.co_varnames[0] == 'self': |
329 | 135 | return True | 191 | return True |
330 | 136 | return False | 192 | return False |
331 | 137 | 193 | ||
332 | @@ -141,9 +197,12 @@ | |||
333 | 141 | Only designed for 'call' event (i.e. function entry) | 197 | Only designed for 'call' event (i.e. function entry) |
334 | 142 | We cache value after first proper lookup. | 198 | We cache value after first proper lookup. |
335 | 143 | """ | 199 | """ |
339 | 144 | if 'instance_name' not in self: | 200 | try: |
340 | 145 | self['instance_name'] = self._retrieve_instance_name() | 201 | name = self._instance_name |
341 | 146 | return self['instance_name'] | 202 | except AttributeError: |
342 | 203 | name = self._retrieve_instance_name() | ||
343 | 204 | self._instance_name = name | ||
344 | 205 | return name | ||
345 | 147 | 206 | ||
346 | 148 | def _retrieve_instance_name(self): | 207 | def _retrieve_instance_name(self): |
347 | 149 | """ | 208 | """ |
348 | @@ -153,22 +212,59 @@ | |||
349 | 153 | try: | 212 | try: |
350 | 154 | # This is confusing. We go into the previous (parent) frame and | 213 | # This is confusing. We go into the previous (parent) frame and |
351 | 155 | # find the local variable name in that pointing to a our class. | 214 | # find the local variable name in that pointing to a our class. |
354 | 156 | for k, v in list(self['f_back'].f_locals.items()): | 215 | for k, v in list(self.frame.f_back.f_locals.items()): |
355 | 157 | if v == self['f_locals']['self']: | 216 | if v == self.frame.f_locals['self']: |
356 | 158 | return k | 217 | return k |
357 | 159 | except: | 218 | except: |
358 | 160 | return '<lookup error>' | 219 | return '<lookup error>' |
359 | 161 | 220 | ||
370 | 162 | def class_name(self): | 221 | def method(self): |
371 | 163 | """ | 222 | """ |
372 | 164 | Class name of method, or empty string if not method. | 223 | Attempt to find the method corresponding to this event, if any. |
373 | 165 | Only designed for 'call' event (i.e. function entry) | 224 | |
374 | 166 | Should only be called on methods. | 225 | On success, returns the function found. If the frame doesn't look like |
375 | 167 | """ | 226 | it corresponds to a method, `None` is returned. |
376 | 168 | try: | 227 | |
377 | 169 | return self["f_locals"]["self"].__class__.__name__ | 228 | """ |
378 | 170 | except: | 229 | return self._retrieve_cls_method()[1] |
379 | 171 | return '<lookup error>' | 230 | |
380 | 231 | def _retrieve_cls_method(self): | ||
381 | 232 | """ | ||
382 | 233 | Attempt to find the method and class corresponding to this event. | ||
383 | 234 | |||
384 | 235 | On success returns a class, method tuple, where the method is an | ||
385 | 236 | attribute on the class returned (helpful for <3.3 where __qualname__ | ||
386 | 237 | isn't available). | ||
387 | 238 | |||
388 | 239 | If the event doesn't look like it corresponds to a method, returns | ||
389 | 240 | None, None. | ||
390 | 241 | |||
391 | 242 | """ | ||
392 | 243 | if not self.code.co_varnames or self.code.co_varnames[0] != "self": | ||
393 | 244 | return None, None | ||
394 | 245 | |||
395 | 246 | meth_self = self.frame.f_locals["self"] | ||
396 | 247 | meth_name = self.code.co_name | ||
397 | 248 | is_unbound_method = False | ||
398 | 249 | |||
399 | 250 | # Alternative approaches: | ||
400 | 251 | # - Source code parsing via inspect. | ||
401 | 252 | # - gc.get_referrers | ||
402 | 253 | # - ? | ||
403 | 254 | # | ||
404 | 255 | # GC is plausible (but won't scale very well with number of objects). | ||
405 | 256 | |||
406 | 257 | for cls in type(meth_self).mro(): | ||
407 | 258 | try: | ||
408 | 259 | candidate = getattr(cls, meth_name) | ||
409 | 260 | except AttributeError: | ||
410 | 261 | continue | ||
411 | 262 | |||
412 | 263 | if callable(candidate): | ||
413 | 264 | return cls, candidate | ||
414 | 265 | |||
415 | 266 | else: | ||
416 | 267 | return None, None | ||
417 | 172 | 268 | ||
418 | 173 | 269 | ||
419 | 174 | class Entrails(object): | 270 | class Entrails(object): |
420 | @@ -184,7 +280,9 @@ | |||
421 | 184 | 280 | ||
422 | 185 | def __init__(self, | 281 | def __init__(self, |
423 | 186 | label='<untitled>', | 282 | label='<untitled>', |
425 | 187 | filters=(_filter.LibraryFilter(), _filter.SelfFilter())): | 283 | filters=None): |
426 | 284 | if filters is None: | ||
427 | 285 | filters = (_filter.LibraryFilter(), _filter.SelfFilter()) | ||
428 | 188 | self._outputs = [] | 286 | self._outputs = [] |
429 | 189 | self._init_time = time.time() | 287 | self._init_time = time.time() |
430 | 190 | self._label = label | 288 | self._label = label |
431 | @@ -198,19 +296,10 @@ | |||
432 | 198 | return True | 296 | return True |
433 | 199 | return False | 297 | return False |
434 | 200 | 298 | ||
448 | 201 | def _trace_function(self, trace, event, arg): | 299 | def _trace_function(self, frame, event, arg): |
449 | 202 | # Build Frame | 300 | obj = Event(frame, event, arg, |
450 | 203 | obj = _Frame() | 301 | self._label, |
451 | 204 | obj["label"] = self._label | 302 | time.time() - self._init_time) |
439 | 205 | obj["timestamp"] = time.time() - self._init_time | ||
440 | 206 | obj["f_lineno"] = trace.f_lineno | ||
441 | 207 | obj["f_back"] = trace.f_back | ||
442 | 208 | obj["f_locals"] = trace.f_locals | ||
443 | 209 | obj["co_name"] = trace.f_code.co_name | ||
444 | 210 | obj["co_argcount"] = trace.f_code.co_argcount | ||
445 | 211 | obj["co_filename"] = trace.f_code.co_filename | ||
446 | 212 | obj["co_names"] = trace.f_code.co_names | ||
447 | 213 | obj["co_varnames"] = trace.f_code.co_varnames | ||
452 | 214 | 303 | ||
453 | 215 | # Filter | 304 | # Filter |
454 | 216 | # If this object is to be discarded, return trace_function to | 305 | # If this object is to be discarded, return trace_function to |
455 | @@ -218,11 +307,6 @@ | |||
456 | 218 | if not self.filter(obj): | 307 | if not self.filter(obj): |
457 | 219 | return self._trace_function | 308 | return self._trace_function |
458 | 220 | 309 | ||
459 | 221 | # Possibly add return_value. If event is non-return, arg isn't really | ||
460 | 222 | # that useful. | ||
461 | 223 | if event is 'return': | ||
462 | 224 | obj["return_value"] = arg | ||
463 | 225 | |||
464 | 226 | if event in self.event_names: | 310 | if event in self.event_names: |
465 | 227 | for output in self._outputs: | 311 | for output in self._outputs: |
466 | 228 | #try: | 312 | #try: |
467 | 229 | 313 | ||
468 | === modified file 'src/trace.py' | |||
469 | --- src/trace.py 2014-07-07 12:48:49 +0000 | |||
470 | +++ src/trace.py 2014-09-03 20:05:48 +0000 | |||
471 | @@ -47,7 +47,7 @@ | |||
472 | 47 | first=True | 47 | first=True |
473 | 48 | for line in fileinput.input(filename, inplace=1, backup=".backup"): | 48 | for line in fileinput.input(filename, inplace=1, backup=".backup"): |
474 | 49 | if first and line.startswith("#!/"): | 49 | if first and line.startswith("#!/"): |
476 | 50 | print(line, end=' ') | 50 | print(line, end='') |
477 | 51 | continue | 51 | continue |
478 | 52 | if first: | 52 | if first: |
479 | 53 | print(MARKER) | 53 | print(MARKER) |
480 | @@ -61,7 +61,7 @@ | |||
481 | 61 | print("_entrails_t.start_trace()") | 61 | print("_entrails_t.start_trace()") |
482 | 62 | print(ENDMARKER) | 62 | print(ENDMARKER) |
483 | 63 | first = False | 63 | first = False |
485 | 64 | print(line, end=' ') | 64 | print(line, end='') |
486 | 65 | fileinput.close() | 65 | fileinput.close() |
487 | 66 | 66 | ||
488 | 67 | 67 | ||
489 | @@ -131,7 +131,7 @@ | |||
490 | 131 | if line.startswith(MARKER): | 131 | if line.startswith(MARKER): |
491 | 132 | extra = True | 132 | extra = True |
492 | 133 | if not extra: | 133 | if not extra: |
494 | 134 | print(line, end=' ') | 134 | print(line, end='') |
495 | 135 | if line.startswith(ENDMARKER): | 135 | if line.startswith(ENDMARKER): |
496 | 136 | extra = False | 136 | extra = False |
497 | 137 | fileinput.close() | 137 | fileinput.close() |
EnTrails could really do with some UT to make me more confident that you hadn't missed any of the name changes... There are at least a couple inline that look wrong, but I've not read every line carefully to be sure there aren't others.