diff -r d48ac94e365f Lib/traceback.py --- a/Lib/traceback.py Wed Oct 02 19:15:54 2013 +0300 +++ b/Lib/traceback.py Wed Oct 02 11:41:21 2013 -0700 @@ -15,7 +15,8 @@ # def _format_list_iter(extracted_list): - for filename, lineno, name, line in extracted_list: + for extract in extracted_list: + filename, lineno, name, line, *_ = extract item = ' File "{}", line {}, in {}\n'.format(filename, lineno, name) if line: item = item + ' {}\n'.format(line.strip()) @@ -45,23 +46,24 @@ # Printing and Extracting Tracebacks. # -# extractor takes curr and needs to return a tuple of: -# - Frame object -# - Line number -# - Next item (same type as curr) -# In practice, curr is either a traceback or a frame. -def _extract_tb_or_stack_iter(curr, limit, extractor): +# Iterator arg yields pairs of (frame object, line number). +# Yields tuples of (filename, lineno, name, line, frame). +def _extract_tb_or_stack_iter(it, limit): if limit is None: limit = getattr(sys, 'tracebacklimit', None) + if limit is not None and limit <= 0: + return - n = 0 - while curr is not None and (limit is None or n < limit): - f, lineno, next_item = extractor(curr) + checked = set() + for f, lineno in it: co = f.f_code filename = co.co_filename name = co.co_name - linecache.checkcache(filename) + # Don't check the same file twice in one traceback. + if filename not in checked: + checked.add(filename) + linecache.checkcache(filename) line = linecache.getline(filename, lineno, f.f_globals) if line: @@ -69,22 +71,29 @@ else: line = None - yield (filename, lineno, name, line) - curr = next_item - n += 1 + yield (filename, lineno, name, line, f) + + if limit is not None: + limit -= 1 + if limit <= 0: + break def _extract_tb_iter(tb, limit): - return _extract_tb_or_stack_iter( - tb, limit, - operator.attrgetter("tb_frame", "tb_lineno", "tb_next")) + def _extractor(tb): + while tb is not None: + yield tb.tb_frame, tb.tb_lineno + tb = tb.tb_next + return _extract_tb_or_stack_iter(_extractor(tb), limit) def print_tb(tb, limit=None, file=None): """Print up to 'limit' stack trace entries from the traceback 'tb'. - If 'limit' is omitted or None, all entries are printed. If 'file' - is omitted or None, the output goes to sys.stderr; otherwise - 'file' should be an open file or file-like object with a write() - method. + If 'limit' is omitted or None, all entries are printed; otherwise + the oldest frames up to 'limit' are printed. + + If 'file' is omitted or None, the output goes to sys.stderr; + otherwise 'file' should be an open file or file-like object with a + write() method. """ print_list(extract_tb(tb, limit=limit), file=file) @@ -93,16 +102,22 @@ return format_list(extract_tb(tb, limit=limit)) def extract_tb(tb, limit=None): - """Return list of up to limit pre-processed entries from traceback. + """Return list of up to 'limit' pre-processed entries from traceback. This is useful for alternate formatting of stack traces. If - 'limit' is omitted or None, all entries are extracted. A - pre-processed stack trace entry is a quadruple (filename, line + 'limit' is omitted or None, all entries are extracted; otherwise + the oldest frames up to 'limit' are extracted. + + A pre-processed stack trace entry is a quadruple (filename, line number, function name, text) representing the information that is usually printed for a stack trace. The text is a string with leading and trailing whitespace stripped; if the source is not available it is None. """ + return [t[:4] for t in _extract_tb_iter(tb, limit=limit)] + +def extract_tb_ex(tb, limit=None): + """Like extract_tb(), but also return the frames.""" return list(_extract_tb_iter(tb, limit=limit)) # @@ -267,8 +282,11 @@ # def _extract_stack_iter(f, limit=None): - return _extract_tb_or_stack_iter( - f, limit, lambda f: (f, f.f_lineno, f.f_back)) + def _extractor(f): + while f is not None: + yield f, f.f_lineno + f = f.f_back + return _extract_tb_or_stack_iter(_extractor(f), limit) def _get_stack(f): if f is None: @@ -280,7 +298,9 @@ The optional 'f' argument can be used to specify an alternate stack frame at which to start. The optional 'limit' and 'file' - arguments have the same meaning as for print_exception(). + arguments have the same meaning as for print_exception(); + however, if 'limit' is not None, the newest frames up to 'limit' + are printed. """ print_list(extract_stack(_get_stack(f), limit=limit), file=file) @@ -293,10 +313,17 @@ The return value has the same format as for extract_tb(). The optional 'f' and 'limit' arguments have the same meaning as for - print_stack(). Each item in the list is a quadruple (filename, - line number, function name, text), and the entries are in order - from oldest to newest stack frame. + print_stack(); however, if 'limit' is not None, the newest frames + up to 'limit' are extracted. Each item in the list is a quadruple + (filename, line number, function name, text), and the entries are + in order from oldest to newest stack frame. """ + stack = [t[:4] for t in _extract_stack_iter(_get_stack(f), limit=limit)] + stack.reverse() + return stack + +def extract_stack_ex(f=None, limit=None): + """Like extract_stack(), but also return the frames.""" stack = list(_extract_stack_iter(_get_stack(f), limit=limit)) stack.reverse() return stack