| 22 | | def get_template_exception_info(exc_type, exc_value, tb): |
|---|
| 23 | | origin, (start, end) = exc_value.source |
|---|
| 24 | | template_source = origin.reload() |
|---|
| 25 | | context_lines = 10 |
|---|
| 26 | | line = 0 |
|---|
| 27 | | upto = 0 |
|---|
| 28 | | source_lines = [] |
|---|
| 29 | | before = during = after = "" |
|---|
| 30 | | for num, next in enumerate(linebreak_iter(template_source)): |
|---|
| 31 | | if start >= upto and end <= next: |
|---|
| 32 | | line = num |
|---|
| 33 | | before = escape(template_source[upto:start]) |
|---|
| 34 | | during = escape(template_source[start:end]) |
|---|
| 35 | | after = escape(template_source[end:next]) |
|---|
| 36 | | source_lines.append( (num, escape(template_source[upto:next])) ) |
|---|
| 37 | | upto = next |
|---|
| 38 | | total = len(source_lines) |
|---|
| 39 | | |
|---|
| 40 | | top = max(1, line - context_lines) |
|---|
| 41 | | bottom = min(total, line + 1 + context_lines) |
|---|
| 42 | | |
|---|
| 43 | | template_info = { |
|---|
| 44 | | 'message': exc_value.args[0], |
|---|
| 45 | | 'source_lines': source_lines[top:bottom], |
|---|
| 46 | | 'before': before, |
|---|
| 47 | | 'during': during, |
|---|
| 48 | | 'after': after, |
|---|
| 49 | | 'top': top, |
|---|
| 50 | | 'bottom': bottom, |
|---|
| 51 | | 'total': total, |
|---|
| 52 | | 'line': line, |
|---|
| 53 | | 'name': origin.name, |
|---|
| 54 | | } |
|---|
| 55 | | exc_info = hasattr(exc_value, 'exc_info') and exc_value.exc_info or (exc_type, exc_value, tb) |
|---|
| 56 | | return exc_info + (template_info,) |
|---|
| 57 | | |
|---|
| 77 | | def get_traceback_html(request, exc_type, exc_value, tb): |
|---|
| 78 | | "Return HTML code for traceback." |
|---|
| 79 | | template_info = None |
|---|
| 80 | | template_does_not_exist = False |
|---|
| 81 | | loader_debug_info = None |
|---|
| 82 | | |
|---|
| 83 | | # Handle deprecated string exceptions |
|---|
| 84 | | if isinstance(exc_type, basestring): |
|---|
| 85 | | exc_value = Exception('Deprecated String Exception: %r' % exc_type) |
|---|
| 86 | | exc_type = type(exc_value) |
|---|
| 87 | | |
|---|
| 88 | | if issubclass(exc_type, TemplateDoesNotExist): |
|---|
| 89 | | from django.template.loader import template_source_loaders |
|---|
| 90 | | template_does_not_exist = True |
|---|
| 91 | | loader_debug_info = [] |
|---|
| 92 | | for loader in template_source_loaders: |
|---|
| | 42 | class ExceptionReporter: |
|---|
| | 43 | """ |
|---|
| | 44 | A class to organize and coordinate reporting on exceptions. |
|---|
| | 45 | """ |
|---|
| | 46 | def __init__(self, request, exc_type, exc_value, tb): |
|---|
| | 47 | self.request = request |
|---|
| | 48 | self.exc_type = exc_type |
|---|
| | 49 | self.exc_value = exc_value |
|---|
| | 50 | self.tb = tb |
|---|
| | 51 | |
|---|
| | 52 | self.template_info = None |
|---|
| | 53 | self.template_does_not_exist = False |
|---|
| | 54 | self.loader_debug_info = None |
|---|
| | 55 | |
|---|
| | 56 | # Handle deprecated string exceptions |
|---|
| | 57 | if isinstance(self.exc_type, basestring): |
|---|
| | 58 | self.exc_value = Exception('Deprecated String Exception: %r' % self.exc_type) |
|---|
| | 59 | self.exc_type = type(self.exc_value) |
|---|
| | 60 | |
|---|
| | 61 | def get_traceback_html(self): |
|---|
| | 62 | "Return HTML code for traceback." |
|---|
| | 63 | |
|---|
| | 64 | if issubclass(self.exc_type, TemplateDoesNotExist): |
|---|
| | 65 | from django.template.loader import template_source_loaders |
|---|
| | 66 | self.template_does_not_exist = True |
|---|
| | 67 | self.loader_debug_info = [] |
|---|
| | 68 | for loader in template_source_loaders: |
|---|
| | 69 | try: |
|---|
| | 70 | source_list_func = getattr(__import__(loader.__module__, {}, {}, ['get_template_sources']), 'get_template_sources') |
|---|
| | 71 | # NOTE: This assumes exc_value is the name of the template that |
|---|
| | 72 | # the loader attempted to load. |
|---|
| | 73 | template_list = [{'name': t, 'exists': os.path.exists(t)} \ |
|---|
| | 74 | for t in source_list_func(str(self.exc_value))] |
|---|
| | 75 | except (ImportError, AttributeError): |
|---|
| | 76 | template_list = [] |
|---|
| | 77 | self.loader_debug_info.append({ |
|---|
| | 78 | 'loader': loader.__module__ + '.' + loader.__name__, |
|---|
| | 79 | 'templates': template_list, |
|---|
| | 80 | }) |
|---|
| | 81 | if settings.TEMPLATE_DEBUG and hasattr(self.exc_value, 'source'): |
|---|
| | 82 | self.get_template_exception_info() |
|---|
| | 83 | |
|---|
| | 84 | frames = self.get_traceback_frames() |
|---|
| | 85 | |
|---|
| | 86 | unicode_hint = '' |
|---|
| | 87 | if issubclass(self.exc_type, UnicodeError): |
|---|
| | 88 | start = getattr(self.exc_value, 'start', None) |
|---|
| | 89 | end = getattr(self.exc_value, 'end', None) |
|---|
| | 90 | if start is not None and end is not None: |
|---|
| | 91 | unicode_str = self.exc_value.args[1] |
|---|
| | 92 | unicode_hint = smart_unicode(unicode_str[max(start-5, 0):min(end+5, len(unicode_str))], 'ascii', errors='replace') |
|---|
| | 93 | from django import get_version |
|---|
| | 94 | t = Template(TECHNICAL_500_TEMPLATE, name='Technical 500 template') |
|---|
| | 95 | c = Context({ |
|---|
| | 96 | 'exception_type': self.exc_type.__name__, |
|---|
| | 97 | 'exception_value': smart_unicode(self.exc_value, errors='replace'), |
|---|
| | 98 | 'unicode_hint': unicode_hint, |
|---|
| | 99 | 'frames': frames, |
|---|
| | 100 | 'lastframe': frames[-1], |
|---|
| | 101 | 'request': self.request, |
|---|
| | 102 | 'request_protocol': self.request.is_secure() and "https" or "http", |
|---|
| | 103 | 'settings': get_safe_settings(), |
|---|
| | 104 | 'sys_executable': sys.executable, |
|---|
| | 105 | 'sys_version_info': '%d.%d.%d' % sys.version_info[0:3], |
|---|
| | 106 | 'server_time': datetime.datetime.now(), |
|---|
| | 107 | 'django_version_info': get_version(), |
|---|
| | 108 | 'sys_path' : sys.path, |
|---|
| | 109 | 'template_info': self.template_info, |
|---|
| | 110 | 'template_does_not_exist': self.template_does_not_exist, |
|---|
| | 111 | 'loader_debug_info': self.loader_debug_info, |
|---|
| | 112 | }) |
|---|
| | 113 | return t.render(c) |
|---|
| | 114 | |
|---|
| | 115 | def get_template_exception_info(self): |
|---|
| | 116 | origin, (start, end) = self.exc_value.source |
|---|
| | 117 | template_source = origin.reload() |
|---|
| | 118 | context_lines = 10 |
|---|
| | 119 | line = 0 |
|---|
| | 120 | upto = 0 |
|---|
| | 121 | source_lines = [] |
|---|
| | 122 | before = during = after = "" |
|---|
| | 123 | for num, next in enumerate(linebreak_iter(template_source)): |
|---|
| | 124 | if start >= upto and end <= next: |
|---|
| | 125 | line = num |
|---|
| | 126 | before = escape(template_source[upto:start]) |
|---|
| | 127 | during = escape(template_source[start:end]) |
|---|
| | 128 | after = escape(template_source[end:next]) |
|---|
| | 129 | source_lines.append( (num, escape(template_source[upto:next])) ) |
|---|
| | 130 | upto = next |
|---|
| | 131 | total = len(source_lines) |
|---|
| | 132 | |
|---|
| | 133 | top = max(1, line - context_lines) |
|---|
| | 134 | bottom = min(total, line + 1 + context_lines) |
|---|
| | 135 | |
|---|
| | 136 | self.template_info = { |
|---|
| | 137 | 'message': self.exc_value.args[0], |
|---|
| | 138 | 'source_lines': source_lines[top:bottom], |
|---|
| | 139 | 'before': before, |
|---|
| | 140 | 'during': during, |
|---|
| | 141 | 'after': after, |
|---|
| | 142 | 'top': top, |
|---|
| | 143 | 'bottom': bottom, |
|---|
| | 144 | 'total': total, |
|---|
| | 145 | 'line': line, |
|---|
| | 146 | 'name': origin.name, |
|---|
| | 147 | } |
|---|
| | 148 | if hasattr(self.exc_value, 'exc_info') and self.exc_value.exc_info: |
|---|
| | 149 | exc_type, exc_value, tb = self.exc_value.exc_info |
|---|
| | 150 | |
|---|
| | 151 | def _get_lines_from_file(self, filename, lineno, context_lines, loader=None, module_name=None): |
|---|
| | 152 | """ |
|---|
| | 153 | Returns context_lines before and after lineno from file. |
|---|
| | 154 | Returns (pre_context_lineno, pre_context, context_line, post_context). |
|---|
| | 155 | """ |
|---|
| | 156 | source = None |
|---|
| | 157 | if loader is not None and hasattr(loader, "get_source"): |
|---|
| | 158 | source = loader.get_source(module_name) |
|---|
| | 159 | if source is not None: |
|---|
| | 160 | source = source.splitlines() |
|---|
| | 161 | if source is None: |
|---|
| 94 | | source_list_func = getattr(__import__(loader.__module__, {}, {}, ['get_template_sources']), 'get_template_sources') |
|---|
| 95 | | # NOTE: This assumes exc_value is the name of the template that |
|---|
| 96 | | # the loader attempted to load. |
|---|
| 97 | | template_list = [{'name': t, 'exists': os.path.exists(t)} \ |
|---|
| 98 | | for t in source_list_func(str(exc_value))] |
|---|
| 99 | | except (ImportError, AttributeError): |
|---|
| 100 | | template_list = [] |
|---|
| 101 | | loader_debug_info.append({ |
|---|
| 102 | | 'loader': loader.__module__ + '.' + loader.__name__, |
|---|
| 103 | | 'templates': template_list, |
|---|
| 104 | | }) |
|---|
| 105 | | if settings.TEMPLATE_DEBUG and hasattr(exc_value, 'source'): |
|---|
| 106 | | exc_type, exc_value, tb, template_info = get_template_exception_info(exc_type, exc_value, tb) |
|---|
| 107 | | frames = [] |
|---|
| 108 | | while tb is not None: |
|---|
| 109 | | # support for __traceback_hide__ which is used by a few libraries |
|---|
| 110 | | # to hide internal frames. |
|---|
| 111 | | if tb.tb_frame.f_locals.get('__traceback_hide__'): |
|---|
| | 163 | f = open(filename) |
|---|
| | 164 | try: |
|---|
| | 165 | source = f.readlines() |
|---|
| | 166 | finally: |
|---|
| | 167 | f.close() |
|---|
| | 168 | except (OSError, IOError): |
|---|
| | 169 | pass |
|---|
| | 170 | if source is None: |
|---|
| | 171 | return None, [], None, [] |
|---|
| | 172 | |
|---|
| | 173 | encoding = 'ascii' |
|---|
| | 174 | for line in source[:2]: |
|---|
| | 175 | # File coding may be specified. Match pattern from PEP-263 |
|---|
| | 176 | # (http://www.python.org/dev/peps/pep-0263/) |
|---|
| | 177 | match = re.search(r'coding[:=]\s*([-\w.]+)', line) |
|---|
| | 178 | if match: |
|---|
| | 179 | encoding = match.group(1) |
|---|
| | 180 | break |
|---|
| | 181 | source = [unicode(sline, encoding, 'replace') for sline in source] |
|---|
| | 182 | |
|---|
| | 183 | lower_bound = max(0, lineno - context_lines) |
|---|
| | 184 | upper_bound = lineno + context_lines |
|---|
| | 185 | |
|---|
| | 186 | pre_context = [line.strip('\n') for line in source[lower_bound:lineno]] |
|---|
| | 187 | context_line = source[lineno].strip('\n') |
|---|
| | 188 | post_context = [line.strip('\n') for line in source[lineno+1:upper_bound]] |
|---|
| | 189 | |
|---|
| | 190 | return lower_bound, pre_context, context_line, post_context |
|---|
| | 191 | |
|---|
| | 192 | def get_traceback_frames(self): |
|---|
| | 193 | frames = [] |
|---|
| | 194 | tb = self.tb |
|---|
| | 195 | while tb is not None: |
|---|
| | 196 | # support for __traceback_hide__ which is used by a few libraries |
|---|
| | 197 | # to hide internal frames. |
|---|
| | 198 | if tb.tb_frame.f_locals.get('__traceback_hide__'): |
|---|
| | 199 | tb = tb.tb_next |
|---|
| | 200 | continue |
|---|
| | 201 | filename = tb.tb_frame.f_code.co_filename |
|---|
| | 202 | function = tb.tb_frame.f_code.co_name |
|---|
| | 203 | lineno = tb.tb_lineno - 1 |
|---|
| | 204 | loader = tb.tb_frame.f_globals.get('__loader__') |
|---|
| | 205 | module_name = tb.tb_frame.f_globals.get('__name__') |
|---|
| | 206 | pre_context_lineno, pre_context, context_line, post_context = self._get_lines_from_file(filename, lineno, 7, loader, module_name) |
|---|
| | 207 | if pre_context_lineno is not None: |
|---|
| | 208 | frames.append({ |
|---|
| | 209 | 'tb': tb, |
|---|
| | 210 | 'filename': filename, |
|---|
| | 211 | 'function': function, |
|---|
| | 212 | 'lineno': lineno + 1, |
|---|
| | 213 | 'vars': tb.tb_frame.f_locals.items(), |
|---|
| | 214 | 'id': id(tb), |
|---|
| | 215 | 'pre_context': pre_context, |
|---|
| | 216 | 'context_line': context_line, |
|---|
| | 217 | 'post_context': post_context, |
|---|
| | 218 | 'pre_context_lineno': pre_context_lineno + 1, |
|---|
| | 219 | }) |
|---|
| 113 | | continue |
|---|
| 114 | | filename = tb.tb_frame.f_code.co_filename |
|---|
| 115 | | function = tb.tb_frame.f_code.co_name |
|---|
| 116 | | lineno = tb.tb_lineno - 1 |
|---|
| 117 | | loader = tb.tb_frame.f_globals.get('__loader__') |
|---|
| 118 | | module_name = tb.tb_frame.f_globals.get('__name__') |
|---|
| 119 | | pre_context_lineno, pre_context, context_line, post_context = _get_lines_from_file(filename, lineno, 7, loader, module_name) |
|---|
| 120 | | if pre_context_lineno is not None: |
|---|
| 121 | | frames.append({ |
|---|
| 122 | | 'tb': tb, |
|---|
| 123 | | 'filename': filename, |
|---|
| 124 | | 'function': function, |
|---|
| 125 | | 'lineno': lineno + 1, |
|---|
| 126 | | 'vars': tb.tb_frame.f_locals.items(), |
|---|
| 127 | | 'id': id(tb), |
|---|
| 128 | | 'pre_context': pre_context, |
|---|
| 129 | | 'context_line': context_line, |
|---|
| 130 | | 'post_context': post_context, |
|---|
| 131 | | 'pre_context_lineno': pre_context_lineno + 1, |
|---|
| 132 | | }) |
|---|
| 133 | | tb = tb.tb_next |
|---|
| 134 | | |
|---|
| 135 | | if not frames: |
|---|
| 136 | | frames = [{ |
|---|
| 137 | | 'filename': '<unknown>', |
|---|
| 138 | | 'function': '?', |
|---|
| 139 | | 'lineno': '?', |
|---|
| 140 | | }] |
|---|
| 141 | | |
|---|
| 142 | | unicode_hint = '' |
|---|
| 143 | | if issubclass(exc_type, UnicodeError): |
|---|
| 144 | | start = getattr(exc_value, 'start', None) |
|---|
| 145 | | end = getattr(exc_value, 'end', None) |
|---|
| 146 | | if start is not None and end is not None: |
|---|
| 147 | | unicode_str = exc_value.args[1] |
|---|
| 148 | | unicode_hint = smart_unicode(unicode_str[max(start-5, 0):min(end+5, len(unicode_str))], 'ascii', errors='replace') |
|---|
| 149 | | from django import get_version |
|---|
| 150 | | t = Template(TECHNICAL_500_TEMPLATE, name='Technical 500 template') |
|---|
| 151 | | c = Context({ |
|---|
| 152 | | 'exception_type': exc_type.__name__, |
|---|
| 153 | | 'exception_value': smart_unicode(exc_value, errors='replace'), |
|---|
| 154 | | 'unicode_hint': unicode_hint, |
|---|
| 155 | | 'frames': frames, |
|---|
| 156 | | 'lastframe': frames[-1], |
|---|
| 157 | | 'request': request, |
|---|
| 158 | | 'request_protocol': request.is_secure() and "https" or "http", |
|---|
| 159 | | 'settings': get_safe_settings(), |
|---|
| 160 | | 'sys_executable': sys.executable, |
|---|
| 161 | | 'sys_version_info': '%d.%d.%d' % sys.version_info[0:3], |
|---|
| 162 | | 'server_time': datetime.datetime.now(), |
|---|
| 163 | | 'django_version_info': get_version(), |
|---|
| 164 | | 'sys_path' : sys.path, |
|---|
| 165 | | 'template_info': template_info, |
|---|
| 166 | | 'template_does_not_exist': template_does_not_exist, |
|---|
| 167 | | 'loader_debug_info': loader_debug_info, |
|---|
| 168 | | }) |
|---|
| 169 | | return t.render(c) |
|---|
| | 221 | |
|---|
| | 222 | if not frames: |
|---|
| | 223 | frames = [{ |
|---|
| | 224 | 'filename': '<unknown>', |
|---|
| | 225 | 'function': '?', |
|---|
| | 226 | 'lineno': '?', |
|---|
| | 227 | 'context_line': '???', |
|---|
| | 228 | }] |
|---|
| | 229 | |
|---|
| | 230 | return frames |
|---|
| | 231 | |
|---|
| | 232 | def format_exception(self): |
|---|
| | 233 | """ |
|---|
| | 234 | Return the same data as from traceback.format_exception. |
|---|
| | 235 | """ |
|---|
| | 236 | import traceback |
|---|
| | 237 | frames = self.get_traceback_frames() |
|---|
| | 238 | tb = [ (f['filename'], f['lineno'], f['function'], f['context_line']) for f in frames ] |
|---|
| | 239 | list = ['Traceback (most recent call last):\n'] |
|---|
| | 240 | list += traceback.format_list(tb) |
|---|
| | 241 | list += traceback.format_exception_only(self.exc_type, self.exc_value) |
|---|
| | 242 | return list |
|---|
| | 243 | |
|---|
| 202 | | def _get_lines_from_file(filename, lineno, context_lines, loader=None, module_name=None): |
|---|
| 203 | | """ |
|---|
| 204 | | Returns context_lines before and after lineno from file. |
|---|
| 205 | | Returns (pre_context_lineno, pre_context, context_line, post_context). |
|---|
| 206 | | """ |
|---|
| 207 | | source = None |
|---|
| 208 | | if loader is not None and hasattr(loader, "get_source"): |
|---|
| 209 | | source = loader.get_source(module_name) |
|---|
| 210 | | if source is not None: |
|---|
| 211 | | source = source.splitlines() |
|---|
| 212 | | if source is None: |
|---|
| 213 | | try: |
|---|
| 214 | | f = open(filename) |
|---|
| 215 | | try: |
|---|
| 216 | | source = f.readlines() |
|---|
| 217 | | finally: |
|---|
| 218 | | f.close() |
|---|
| 219 | | except (OSError, IOError): |
|---|
| 220 | | pass |
|---|
| 221 | | if source is None: |
|---|
| 222 | | return None, [], None, [] |
|---|
| 223 | | |
|---|
| 224 | | encoding = 'ascii' |
|---|
| 225 | | for line in source[:2]: |
|---|
| 226 | | # File coding may be specified. Match pattern from PEP-263 |
|---|
| 227 | | # (http://www.python.org/dev/peps/pep-0263/) |
|---|
| 228 | | match = re.search(r'coding[:=]\s*([-\w.]+)', line) |
|---|
| 229 | | if match: |
|---|
| 230 | | encoding = match.group(1) |
|---|
| 231 | | break |
|---|
| 232 | | source = [unicode(sline, encoding, 'replace') for sline in source] |
|---|
| 233 | | |
|---|
| 234 | | lower_bound = max(0, lineno - context_lines) |
|---|
| 235 | | upper_bound = lineno + context_lines |
|---|
| 236 | | |
|---|
| 237 | | pre_context = [line.strip('\n') for line in source[lower_bound:lineno]] |
|---|
| 238 | | context_line = source[lineno].strip('\n') |
|---|
| 239 | | post_context = [line.strip('\n') for line in source[lineno+1:upper_bound]] |
|---|
| 240 | | |
|---|
| 241 | | return lower_bound, pre_context, context_line, post_context |
|---|
| 242 | | |
|---|