| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with |
| # this work for additional information regarding copyright ownership. |
| # The ASF licenses this file to You under the Apache License, Version 2.0 |
| # (the "License"); you may not use this file except in compliance with |
| # the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| """ |
| Utilities for extracting and formatting Python exceptions. |
| """ |
| |
| import sys |
| import linecache |
| import StringIO |
| import traceback as tb |
| |
| import jsonpickle |
| |
| from .console import (puts, indent, Colored) |
| |
| |
| ENTRY_FORMAT = 'File "{filename}", line {lineno}, in {name}' |
| |
| |
| def print_exception(e, full=True, cause=False, traceback=None): |
| """ |
| Prints the exception with nice colors and such. |
| """ |
| def format_heading(e): |
| return '{0}{1}: {2}'.format( |
| Colored.red('Caused by ') if cause else '', |
| Colored.red(e.__class__.__name__, bold=True), |
| Colored.red(e)) |
| |
| puts(format_heading(e)) |
| if full: |
| if cause: |
| if traceback: |
| print_traceback(traceback, True) |
| else: |
| print_traceback() |
| if hasattr(e, 'cause') and e.cause: |
| traceback = e.cause_traceback if hasattr(e, 'cause_traceback') else None |
| print_exception(e.cause, full=full, cause=True, traceback=traceback) |
| |
| |
| def print_traceback(traceback=None, print_last_stack=False): |
| """ |
| Prints the traceback with nice colors and such. |
| """ |
| |
| if traceback is None: |
| _, _, traceback = sys.exc_info() |
| while traceback is not None: |
| frame = traceback.tb_frame |
| code = frame.f_code |
| filename = code.co_filename |
| lineno = traceback.tb_lineno |
| name = code.co_name |
| with indent(2): |
| puts(ENTRY_FORMAT.format(filename=Colored.blue(filename), |
| lineno=Colored.cyan(lineno), |
| name=Colored.cyan(name))) |
| linecache.checkcache(filename) |
| line = linecache.getline(filename, lineno, frame.f_globals) |
| if line: |
| with indent(2): |
| puts(line.strip()) |
| traceback = traceback.tb_next |
| if print_last_stack and (traceback is None): |
| # Print stack of *last* traceback |
| _print_stack(frame) |
| |
| |
| def _print_stack(frame): |
| entries = tb.extract_stack(frame) |
| if not entries: |
| return |
| puts(Colored.red('Call stack:')) |
| with indent(2): |
| for filename, lineno, name, line in entries: |
| puts(ENTRY_FORMAT.format(filename=Colored.blue(filename), |
| lineno=Colored.cyan(lineno), |
| name=Colored.cyan(name))) |
| with indent(2): |
| puts(line) |
| |
| |
| def get_exception_as_string(exc_type, exc_val, traceback): |
| s_traceback = StringIO.StringIO() |
| tb.print_exception( |
| etype=exc_type, |
| value=exc_val, |
| tb=traceback, |
| file=s_traceback) |
| return s_traceback.getvalue() |
| |
| |
| class _WrappedException(Exception): |
| |
| def __init__(self, exception_type, exception_str): |
| super(_WrappedException, self).__init__(exception_type, exception_str) |
| self.exception_type = exception_type |
| self.exception_str = exception_str |
| |
| |
| def wrap_if_needed(exception): |
| try: |
| jsonpickle.loads(jsonpickle.dumps(exception)) |
| return exception |
| except BaseException: |
| return _WrappedException(type(exception).__name__, str(exception)) |