|
1 import py |
|
2 import sys, os.path |
|
3 |
|
4 builtin_repr = repr |
|
5 |
|
6 reprlib = py.builtin._tryimport('repr', 'reprlib') |
|
7 |
|
8 class Code(object): |
|
9 """ wrapper around Python code objects """ |
|
10 def __init__(self, rawcode): |
|
11 rawcode = py.code.getrawcode(rawcode) |
|
12 self.raw = rawcode |
|
13 try: |
|
14 self.filename = rawcode.co_filename |
|
15 self.firstlineno = rawcode.co_firstlineno - 1 |
|
16 self.name = rawcode.co_name |
|
17 except AttributeError: |
|
18 raise TypeError("not a code object: %r" %(rawcode,)) |
|
19 |
|
20 def __eq__(self, other): |
|
21 return self.raw == other.raw |
|
22 |
|
23 def __ne__(self, other): |
|
24 return not self == other |
|
25 |
|
26 def path(self): |
|
27 """ return a path object pointing to source code""" |
|
28 p = py.path.local(self.raw.co_filename) |
|
29 if not p.check(): |
|
30 # XXX maybe try harder like the weird logic |
|
31 # in the standard lib [linecache.updatecache] does? |
|
32 p = self.raw.co_filename |
|
33 return p |
|
34 |
|
35 path = property(path, None, None, "path of this code object") |
|
36 |
|
37 def fullsource(self): |
|
38 """ return a py.code.Source object for the full source file of the code |
|
39 """ |
|
40 from py._code import source |
|
41 full, _ = source.findsource(self.raw) |
|
42 return full |
|
43 fullsource = property(fullsource, None, None, |
|
44 "full source containing this code object") |
|
45 |
|
46 def source(self): |
|
47 """ return a py.code.Source object for the code object's source only |
|
48 """ |
|
49 # return source only for that part of code |
|
50 return py.code.Source(self.raw) |
|
51 |
|
52 def getargs(self): |
|
53 """ return a tuple with the argument names for the code object |
|
54 """ |
|
55 # handfull shortcut for getting args |
|
56 raw = self.raw |
|
57 return raw.co_varnames[:raw.co_argcount] |
|
58 |
|
59 class Frame(object): |
|
60 """Wrapper around a Python frame holding f_locals and f_globals |
|
61 in which expressions can be evaluated.""" |
|
62 |
|
63 def __init__(self, frame): |
|
64 self.code = py.code.Code(frame.f_code) |
|
65 self.lineno = frame.f_lineno - 1 |
|
66 self.f_globals = frame.f_globals |
|
67 self.f_locals = frame.f_locals |
|
68 self.raw = frame |
|
69 |
|
70 def statement(self): |
|
71 if self.code.fullsource is None: |
|
72 return py.code.Source("") |
|
73 return self.code.fullsource.getstatement(self.lineno) |
|
74 statement = property(statement, None, None, |
|
75 "statement this frame is at") |
|
76 |
|
77 def eval(self, code, **vars): |
|
78 """ evaluate 'code' in the frame |
|
79 |
|
80 'vars' are optional additional local variables |
|
81 |
|
82 returns the result of the evaluation |
|
83 """ |
|
84 f_locals = self.f_locals.copy() |
|
85 f_locals.update(vars) |
|
86 return eval(code, self.f_globals, f_locals) |
|
87 |
|
88 def exec_(self, code, **vars): |
|
89 """ exec 'code' in the frame |
|
90 |
|
91 'vars' are optiona; additional local variables |
|
92 """ |
|
93 f_locals = self.f_locals.copy() |
|
94 f_locals.update(vars) |
|
95 py.builtin.exec_(code, self.f_globals, f_locals ) |
|
96 |
|
97 def repr(self, object): |
|
98 """ return a 'safe' (non-recursive, one-line) string repr for 'object' |
|
99 """ |
|
100 return py.io.saferepr(object) |
|
101 |
|
102 def is_true(self, object): |
|
103 return object |
|
104 |
|
105 def getargs(self): |
|
106 """ return a list of tuples (name, value) for all arguments |
|
107 """ |
|
108 retval = [] |
|
109 for arg in self.code.getargs(): |
|
110 try: |
|
111 retval.append((arg, self.f_locals[arg])) |
|
112 except KeyError: |
|
113 pass # this can occur when using Psyco |
|
114 return retval |
|
115 |
|
116 class TracebackEntry(object): |
|
117 """ a single entry in a traceback """ |
|
118 |
|
119 exprinfo = None |
|
120 |
|
121 def __init__(self, rawentry): |
|
122 self._rawentry = rawentry |
|
123 self.frame = py.code.Frame(rawentry.tb_frame) |
|
124 # Ugh. 2.4 and 2.5 differs here when encountering |
|
125 # multi-line statements. Not sure about the solution, but |
|
126 # should be portable |
|
127 self.lineno = rawentry.tb_lineno - 1 |
|
128 self.relline = self.lineno - self.frame.code.firstlineno |
|
129 |
|
130 def __repr__(self): |
|
131 return "<TracebackEntry %s:%d>" %(self.frame.code.path, self.lineno+1) |
|
132 |
|
133 def statement(self): |
|
134 """ return a py.code.Source object for the current statement """ |
|
135 source = self.frame.code.fullsource |
|
136 return source.getstatement(self.lineno) |
|
137 statement = property(statement, None, None, |
|
138 "statement of this traceback entry.") |
|
139 |
|
140 def path(self): |
|
141 return self.frame.code.path |
|
142 path = property(path, None, None, "path to the full source code") |
|
143 |
|
144 def getlocals(self): |
|
145 return self.frame.f_locals |
|
146 locals = property(getlocals, None, None, "locals of underlaying frame") |
|
147 |
|
148 def reinterpret(self): |
|
149 """Reinterpret the failing statement and returns a detailed information |
|
150 about what operations are performed.""" |
|
151 if self.exprinfo is None: |
|
152 source = str(self.statement).strip() |
|
153 x = py.code._reinterpret(source, self.frame, should_fail=True) |
|
154 if not isinstance(x, str): |
|
155 raise TypeError("interpret returned non-string %r" % (x,)) |
|
156 self.exprinfo = x |
|
157 return self.exprinfo |
|
158 |
|
159 def getfirstlinesource(self): |
|
160 # on Jython this firstlineno can be -1 apparently |
|
161 return max(self.frame.code.firstlineno, 0) |
|
162 |
|
163 def getsource(self): |
|
164 """ return failing source code. """ |
|
165 source = self.frame.code.fullsource |
|
166 if source is None: |
|
167 return None |
|
168 start = self.getfirstlinesource() |
|
169 end = self.lineno |
|
170 try: |
|
171 _, end = source.getstatementrange(end) |
|
172 except IndexError: |
|
173 end = self.lineno + 1 |
|
174 # heuristic to stop displaying source on e.g. |
|
175 # if something: # assume this causes a NameError |
|
176 # # _this_ lines and the one |
|
177 # below we don't want from entry.getsource() |
|
178 for i in range(self.lineno, end): |
|
179 if source[i].rstrip().endswith(':'): |
|
180 end = i + 1 |
|
181 break |
|
182 return source[start:end] |
|
183 source = property(getsource) |
|
184 |
|
185 def ishidden(self): |
|
186 """ return True if the current frame has a var __tracebackhide__ |
|
187 resolving to True |
|
188 |
|
189 mostly for internal use |
|
190 """ |
|
191 try: |
|
192 return self.frame.eval("__tracebackhide__") |
|
193 except py.builtin._sysex: |
|
194 raise |
|
195 except: |
|
196 return False |
|
197 |
|
198 def __str__(self): |
|
199 try: |
|
200 fn = str(self.path) |
|
201 except py.error.Error: |
|
202 fn = '???' |
|
203 name = self.frame.code.name |
|
204 try: |
|
205 line = str(self.statement).lstrip() |
|
206 except KeyboardInterrupt: |
|
207 raise |
|
208 except: |
|
209 line = "???" |
|
210 return " File %r:%d in %s\n %s\n" %(fn, self.lineno+1, name, line) |
|
211 |
|
212 def name(self): |
|
213 return self.frame.code.raw.co_name |
|
214 name = property(name, None, None, "co_name of underlaying code") |
|
215 |
|
216 class Traceback(list): |
|
217 """ Traceback objects encapsulate and offer higher level |
|
218 access to Traceback entries. |
|
219 """ |
|
220 Entry = TracebackEntry |
|
221 def __init__(self, tb): |
|
222 """ initialize from given python traceback object. """ |
|
223 if hasattr(tb, 'tb_next'): |
|
224 def f(cur): |
|
225 while cur is not None: |
|
226 yield self.Entry(cur) |
|
227 cur = cur.tb_next |
|
228 list.__init__(self, f(tb)) |
|
229 else: |
|
230 list.__init__(self, tb) |
|
231 |
|
232 def cut(self, path=None, lineno=None, firstlineno=None, excludepath=None): |
|
233 """ return a Traceback instance wrapping part of this Traceback |
|
234 |
|
235 by provding any combination of path, lineno and firstlineno, the |
|
236 first frame to start the to-be-returned traceback is determined |
|
237 |
|
238 this allows cutting the first part of a Traceback instance e.g. |
|
239 for formatting reasons (removing some uninteresting bits that deal |
|
240 with handling of the exception/traceback) |
|
241 """ |
|
242 for x in self: |
|
243 code = x.frame.code |
|
244 codepath = code.path |
|
245 if ((path is None or codepath == path) and |
|
246 (excludepath is None or not hasattr(codepath, 'relto') or |
|
247 not codepath.relto(excludepath)) and |
|
248 (lineno is None or x.lineno == lineno) and |
|
249 (firstlineno is None or x.frame.code.firstlineno == firstlineno)): |
|
250 return Traceback(x._rawentry) |
|
251 return self |
|
252 |
|
253 def __getitem__(self, key): |
|
254 val = super(Traceback, self).__getitem__(key) |
|
255 if isinstance(key, type(slice(0))): |
|
256 val = self.__class__(val) |
|
257 return val |
|
258 |
|
259 def filter(self, fn=lambda x: not x.ishidden()): |
|
260 """ return a Traceback instance with certain items removed |
|
261 |
|
262 fn is a function that gets a single argument, a TracebackItem |
|
263 instance, and should return True when the item should be added |
|
264 to the Traceback, False when not |
|
265 |
|
266 by default this removes all the TracebackItems which are hidden |
|
267 (see ishidden() above) |
|
268 """ |
|
269 return Traceback(filter(fn, self)) |
|
270 |
|
271 def getcrashentry(self): |
|
272 """ return last non-hidden traceback entry that lead |
|
273 to the exception of a traceback. |
|
274 """ |
|
275 tb = self.filter() |
|
276 if not tb: |
|
277 tb = self |
|
278 return tb[-1] |
|
279 |
|
280 def recursionindex(self): |
|
281 """ return the index of the frame/TracebackItem where recursion |
|
282 originates if appropriate, None if no recursion occurred |
|
283 """ |
|
284 cache = {} |
|
285 for i, entry in enumerate(self): |
|
286 key = entry.frame.code.path, entry.lineno |
|
287 #print "checking for recursion at", key |
|
288 l = cache.setdefault(key, []) |
|
289 if l: |
|
290 f = entry.frame |
|
291 loc = f.f_locals |
|
292 for otherloc in l: |
|
293 if f.is_true(f.eval(co_equal, |
|
294 __recursioncache_locals_1=loc, |
|
295 __recursioncache_locals_2=otherloc)): |
|
296 return i |
|
297 l.append(entry.frame.f_locals) |
|
298 return None |
|
299 |
|
300 co_equal = compile('__recursioncache_locals_1 == __recursioncache_locals_2', |
|
301 '?', 'eval') |
|
302 |
|
303 class ExceptionInfo(object): |
|
304 """ wraps sys.exc_info() objects and offers |
|
305 help for navigating the traceback. |
|
306 """ |
|
307 _striptext = '' |
|
308 def __init__(self, tup=None, exprinfo=None): |
|
309 # NB. all attributes are private! Subclasses or other |
|
310 # ExceptionInfo-like classes may have different attributes. |
|
311 if tup is None: |
|
312 tup = sys.exc_info() |
|
313 if exprinfo is None and isinstance(tup[1], py.code._AssertionError): |
|
314 exprinfo = getattr(tup[1], 'msg', None) |
|
315 if exprinfo is None: |
|
316 exprinfo = str(tup[1]) |
|
317 if exprinfo and exprinfo.startswith('assert '): |
|
318 self._striptext = 'AssertionError: ' |
|
319 self._excinfo = tup |
|
320 self.type, self.value, tb = self._excinfo |
|
321 self.typename = self.type.__name__ |
|
322 self.traceback = py.code.Traceback(tb) |
|
323 |
|
324 def __repr__(self): |
|
325 return "<ExceptionInfo %s tblen=%d>" % (self.typename, len(self.traceback)) |
|
326 |
|
327 def exconly(self, tryshort=False): |
|
328 """ return the exception as a string |
|
329 |
|
330 when 'tryshort' resolves to True, and the exception is a |
|
331 py.code._AssertionError, only the actual exception part of |
|
332 the exception representation is returned (so 'AssertionError: ' is |
|
333 removed from the beginning) |
|
334 """ |
|
335 lines = py.std.traceback.format_exception_only(self.type, self.value) |
|
336 text = ''.join(lines) |
|
337 text = text.rstrip() |
|
338 if tryshort: |
|
339 if text.startswith(self._striptext): |
|
340 text = text[len(self._striptext):] |
|
341 return text |
|
342 |
|
343 def errisinstance(self, exc): |
|
344 """ return True if the exception is an instance of exc """ |
|
345 return isinstance(self.value, exc) |
|
346 |
|
347 def _getreprcrash(self): |
|
348 exconly = self.exconly(tryshort=True) |
|
349 entry = self.traceback.getcrashentry() |
|
350 path, lineno = entry.path, entry.lineno |
|
351 reprcrash = ReprFileLocation(path, lineno+1, exconly) |
|
352 return reprcrash |
|
353 |
|
354 def getrepr(self, showlocals=False, style="long", |
|
355 abspath=False, tbfilter=True, funcargs=False): |
|
356 """ return str()able representation of this exception info. |
|
357 showlocals: show locals per traceback entry |
|
358 style: long|short|no|native traceback style |
|
359 tbfilter: hide entries (where __tracebackhide__ is true) |
|
360 """ |
|
361 if style == 'native': |
|
362 import traceback |
|
363 return ''.join(traceback.format_exception( |
|
364 self.type, |
|
365 self.value, |
|
366 self.traceback[0]._rawentry, |
|
367 )) |
|
368 |
|
369 fmt = FormattedExcinfo(showlocals=showlocals, style=style, |
|
370 abspath=abspath, tbfilter=tbfilter, funcargs=funcargs) |
|
371 return fmt.repr_excinfo(self) |
|
372 |
|
373 def __str__(self): |
|
374 entry = self.traceback[-1] |
|
375 loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly()) |
|
376 return str(loc) |
|
377 |
|
378 def __unicode__(self): |
|
379 entry = self.traceback[-1] |
|
380 loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly()) |
|
381 return unicode(loc) |
|
382 |
|
383 |
|
384 class FormattedExcinfo(object): |
|
385 """ presenting information about failing Functions and Generators. """ |
|
386 # for traceback entries |
|
387 flow_marker = ">" |
|
388 fail_marker = "E" |
|
389 |
|
390 def __init__(self, showlocals=False, style="long", abspath=True, tbfilter=True, funcargs=False): |
|
391 self.showlocals = showlocals |
|
392 self.style = style |
|
393 self.tbfilter = tbfilter |
|
394 self.funcargs = funcargs |
|
395 self.abspath = abspath |
|
396 |
|
397 def _getindent(self, source): |
|
398 # figure out indent for given source |
|
399 try: |
|
400 s = str(source.getstatement(len(source)-1)) |
|
401 except KeyboardInterrupt: |
|
402 raise |
|
403 except: |
|
404 try: |
|
405 s = str(source[-1]) |
|
406 except KeyboardInterrupt: |
|
407 raise |
|
408 except: |
|
409 return 0 |
|
410 return 4 + (len(s) - len(s.lstrip())) |
|
411 |
|
412 def _getentrysource(self, entry): |
|
413 source = entry.getsource() |
|
414 if source is not None: |
|
415 source = source.deindent() |
|
416 return source |
|
417 |
|
418 def _saferepr(self, obj): |
|
419 return py.io.saferepr(obj) |
|
420 |
|
421 def repr_args(self, entry): |
|
422 if self.funcargs: |
|
423 args = [] |
|
424 for argname, argvalue in entry.frame.getargs(): |
|
425 args.append((argname, self._saferepr(argvalue))) |
|
426 return ReprFuncArgs(args) |
|
427 |
|
428 def get_source(self, source, line_index=-1, excinfo=None, short=False): |
|
429 """ return formatted and marked up source lines. """ |
|
430 lines = [] |
|
431 if source is None: |
|
432 source = py.code.Source("???") |
|
433 line_index = 0 |
|
434 if line_index < 0: |
|
435 line_index += len(source) |
|
436 for i in range(len(source)): |
|
437 if i == line_index: |
|
438 prefix = self.flow_marker + " " |
|
439 else: |
|
440 if short: |
|
441 continue |
|
442 prefix = " " |
|
443 line = prefix + source[i] |
|
444 lines.append(line) |
|
445 if excinfo is not None: |
|
446 indent = self._getindent(source) |
|
447 lines.extend(self.get_exconly(excinfo, indent=indent, markall=True)) |
|
448 return lines |
|
449 |
|
450 def get_exconly(self, excinfo, indent=4, markall=False): |
|
451 lines = [] |
|
452 indent = " " * indent |
|
453 # get the real exception information out |
|
454 exlines = excinfo.exconly(tryshort=True).split('\n') |
|
455 failindent = self.fail_marker + indent[1:] |
|
456 for line in exlines: |
|
457 lines.append(failindent + line) |
|
458 if not markall: |
|
459 failindent = indent |
|
460 return lines |
|
461 |
|
462 def repr_locals(self, locals): |
|
463 if self.showlocals: |
|
464 lines = [] |
|
465 keys = list(locals) |
|
466 keys.sort() |
|
467 for name in keys: |
|
468 value = locals[name] |
|
469 if name == '__builtins__': |
|
470 lines.append("__builtins__ = <builtins>") |
|
471 else: |
|
472 # This formatting could all be handled by the |
|
473 # _repr() function, which is only reprlib.Repr in |
|
474 # disguise, so is very configurable. |
|
475 str_repr = self._saferepr(value) |
|
476 #if len(str_repr) < 70 or not isinstance(value, |
|
477 # (list, tuple, dict)): |
|
478 lines.append("%-10s = %s" %(name, str_repr)) |
|
479 #else: |
|
480 # self._line("%-10s =\\" % (name,)) |
|
481 # # XXX |
|
482 # py.std.pprint.pprint(value, stream=self.excinfowriter) |
|
483 return ReprLocals(lines) |
|
484 |
|
485 def repr_traceback_entry(self, entry, excinfo=None): |
|
486 # excinfo is not None if this is the last tb entry |
|
487 source = self._getentrysource(entry) |
|
488 if source is None: |
|
489 source = py.code.Source("???") |
|
490 line_index = 0 |
|
491 else: |
|
492 # entry.getfirstlinesource() can be -1, should be 0 on jython |
|
493 line_index = entry.lineno - max(entry.getfirstlinesource(), 0) |
|
494 |
|
495 lines = [] |
|
496 if self.style in ("short", "long"): |
|
497 short = self.style == "short" |
|
498 reprargs = None |
|
499 if not short: |
|
500 reprargs = self.repr_args(entry) |
|
501 s = self.get_source(source, line_index, excinfo, short=short) |
|
502 lines.extend(s) |
|
503 if short: |
|
504 message = "in %s" %(entry.name) |
|
505 else: |
|
506 message = excinfo and excinfo.typename or "" |
|
507 path = self._makepath(entry.path) |
|
508 filelocrepr = ReprFileLocation(path, entry.lineno+1, message) |
|
509 localsrepr = None |
|
510 if not short: |
|
511 localsrepr = self.repr_locals(entry.locals) |
|
512 return ReprEntry(lines, reprargs, localsrepr, filelocrepr, short) |
|
513 if excinfo: |
|
514 lines.extend(self.get_exconly(excinfo, indent=4)) |
|
515 return ReprEntry(lines, None, None, None, False) |
|
516 |
|
517 def _makepath(self, path): |
|
518 if not self.abspath: |
|
519 np = py.path.local().bestrelpath(path) |
|
520 if len(np) < len(str(path)): |
|
521 path = np |
|
522 return path |
|
523 |
|
524 def repr_traceback(self, excinfo): |
|
525 traceback = excinfo.traceback |
|
526 if self.tbfilter: |
|
527 traceback = traceback.filter() |
|
528 recursionindex = None |
|
529 if excinfo.errisinstance(RuntimeError): |
|
530 recursionindex = traceback.recursionindex() |
|
531 last = traceback[-1] |
|
532 entries = [] |
|
533 extraline = None |
|
534 for index, entry in enumerate(traceback): |
|
535 einfo = (last == entry) and excinfo or None |
|
536 reprentry = self.repr_traceback_entry(entry, einfo) |
|
537 entries.append(reprentry) |
|
538 if index == recursionindex: |
|
539 extraline = "!!! Recursion detected (same locals & position)" |
|
540 break |
|
541 return ReprTraceback(entries, extraline, style=self.style) |
|
542 |
|
543 def repr_excinfo(self, excinfo): |
|
544 reprtraceback = self.repr_traceback(excinfo) |
|
545 reprcrash = excinfo._getreprcrash() |
|
546 return ReprExceptionInfo(reprtraceback, reprcrash) |
|
547 |
|
548 class TerminalRepr: |
|
549 def __str__(self): |
|
550 s = self.__unicode__() |
|
551 if sys.version_info[0] < 3: |
|
552 s = s.encode('utf-8') |
|
553 return s |
|
554 |
|
555 def __unicode__(self): |
|
556 l = [] |
|
557 tw = py.io.TerminalWriter(l.append) |
|
558 self.toterminal(tw) |
|
559 l = map(unicode_or_repr, l) |
|
560 return "".join(l).strip() |
|
561 |
|
562 def __repr__(self): |
|
563 return "<%s instance at %0x>" %(self.__class__, id(self)) |
|
564 |
|
565 def unicode_or_repr(obj): |
|
566 try: |
|
567 return py.builtin._totext(obj) |
|
568 except KeyboardInterrupt: |
|
569 raise |
|
570 except Exception: |
|
571 return "<print-error: %r>" % py.io.saferepr(obj) |
|
572 |
|
573 class ReprExceptionInfo(TerminalRepr): |
|
574 def __init__(self, reprtraceback, reprcrash): |
|
575 self.reprtraceback = reprtraceback |
|
576 self.reprcrash = reprcrash |
|
577 self.sections = [] |
|
578 |
|
579 def addsection(self, name, content, sep="-"): |
|
580 self.sections.append((name, content, sep)) |
|
581 |
|
582 def toterminal(self, tw): |
|
583 self.reprtraceback.toterminal(tw) |
|
584 for name, content, sep in self.sections: |
|
585 tw.sep(sep, name) |
|
586 tw.line(content) |
|
587 |
|
588 class ReprTraceback(TerminalRepr): |
|
589 entrysep = "_ " |
|
590 |
|
591 def __init__(self, reprentries, extraline, style): |
|
592 self.reprentries = reprentries |
|
593 self.extraline = extraline |
|
594 self.style = style |
|
595 |
|
596 def toterminal(self, tw): |
|
597 sepok = False |
|
598 for entry in self.reprentries: |
|
599 if self.style == "long": |
|
600 if sepok: |
|
601 tw.sep(self.entrysep) |
|
602 tw.line("") |
|
603 sepok = True |
|
604 entry.toterminal(tw) |
|
605 if self.extraline: |
|
606 tw.line(self.extraline) |
|
607 |
|
608 class ReprEntry(TerminalRepr): |
|
609 localssep = "_ " |
|
610 |
|
611 def __init__(self, lines, reprfuncargs, reprlocals, filelocrepr, short): |
|
612 self.lines = lines |
|
613 self.reprfuncargs = reprfuncargs |
|
614 self.reprlocals = reprlocals |
|
615 self.reprfileloc = filelocrepr |
|
616 self.short = short |
|
617 |
|
618 def toterminal(self, tw): |
|
619 if self.short: |
|
620 self.reprfileloc.toterminal(tw) |
|
621 for line in self.lines: |
|
622 red = line.startswith("E ") |
|
623 tw.line(line, bold=True, red=red) |
|
624 #tw.line("") |
|
625 return |
|
626 if self.reprfuncargs: |
|
627 self.reprfuncargs.toterminal(tw) |
|
628 for line in self.lines: |
|
629 red = line.startswith("E ") |
|
630 tw.line(line, bold=True, red=red) |
|
631 if self.reprlocals: |
|
632 #tw.sep(self.localssep, "Locals") |
|
633 tw.line("") |
|
634 self.reprlocals.toterminal(tw) |
|
635 if self.reprfileloc: |
|
636 tw.line("") |
|
637 self.reprfileloc.toterminal(tw) |
|
638 |
|
639 def __str__(self): |
|
640 return "%s\n%s\n%s" % ("\n".join(self.lines), |
|
641 self.reprlocals, |
|
642 self.reprfileloc) |
|
643 |
|
644 class ReprFileLocation(TerminalRepr): |
|
645 def __init__(self, path, lineno, message): |
|
646 self.path = str(path) |
|
647 self.lineno = lineno |
|
648 self.message = message |
|
649 |
|
650 def toterminal(self, tw): |
|
651 # filename and lineno output for each entry, |
|
652 # using an output format that most editors unterstand |
|
653 msg = self.message |
|
654 i = msg.find("\n") |
|
655 if i != -1: |
|
656 msg = msg[:i] |
|
657 tw.line("%s:%s: %s" %(self.path, self.lineno, msg)) |
|
658 |
|
659 class ReprLocals(TerminalRepr): |
|
660 def __init__(self, lines): |
|
661 self.lines = lines |
|
662 |
|
663 def toterminal(self, tw): |
|
664 for line in self.lines: |
|
665 tw.line(line) |
|
666 |
|
667 class ReprFuncArgs(TerminalRepr): |
|
668 def __init__(self, args): |
|
669 self.args = args |
|
670 |
|
671 def toterminal(self, tw): |
|
672 if self.args: |
|
673 linesofar = "" |
|
674 for name, value in self.args: |
|
675 ns = "%s = %s" %(name, value) |
|
676 if len(ns) + len(linesofar) + 2 > tw.fullwidth: |
|
677 if linesofar: |
|
678 tw.line(linesofar) |
|
679 linesofar = ns |
|
680 else: |
|
681 if linesofar: |
|
682 linesofar += ", " + ns |
|
683 else: |
|
684 linesofar = ns |
|
685 if linesofar: |
|
686 tw.line(linesofar) |
|
687 tw.line("") |
|
688 |
|
689 |
|
690 |
|
691 oldbuiltins = {} |
|
692 |
|
693 def patch_builtins(assertion=True, compile=True): |
|
694 """ put compile and AssertionError builtins to Python's builtins. """ |
|
695 if assertion: |
|
696 from py._code import assertion |
|
697 l = oldbuiltins.setdefault('AssertionError', []) |
|
698 l.append(py.builtin.builtins.AssertionError) |
|
699 py.builtin.builtins.AssertionError = assertion.AssertionError |
|
700 if compile: |
|
701 l = oldbuiltins.setdefault('compile', []) |
|
702 l.append(py.builtin.builtins.compile) |
|
703 py.builtin.builtins.compile = py.code.compile |
|
704 |
|
705 def unpatch_builtins(assertion=True, compile=True): |
|
706 """ remove compile and AssertionError builtins from Python builtins. """ |
|
707 if assertion: |
|
708 py.builtin.builtins.AssertionError = oldbuiltins['AssertionError'].pop() |
|
709 if compile: |
|
710 py.builtin.builtins.compile = oldbuiltins['compile'].pop() |
|
711 |
|
712 def getrawcode(obj): |
|
713 """ return code object for given function. """ |
|
714 try: |
|
715 return obj.__code__ |
|
716 except AttributeError: |
|
717 obj = getattr(obj, 'im_func', obj) |
|
718 obj = getattr(obj, 'func_code', obj) |
|
719 obj = getattr(obj, 'f_code', obj) |
|
720 obj = getattr(obj, '__code__', obj) |
|
721 return obj |
|
722 |