eggs/py-1.4.0-py2.6.egg/py/_code/code.py
changeset 69 c6bca38c1cbf
equal deleted inserted replaced
68:5ff1fc726848 69:c6bca38c1cbf
       
     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