Ticket #3527: django_debugger_r5620.diff
File django_debugger_r5620.diff, 13.4 KB (added by , 17 years ago) |
---|
-
django/conf/global_settings.py
11 11 12 12 DEBUG = False 13 13 TEMPLATE_DEBUG = False 14 DEBUGGER_ENABLED = False 14 15 15 16 # Whether to use the "Etag" header. This saves bandwidth but slows down performance. 16 17 USE_ETAGS = False -
django/views/debug.py
3 3 from django.utils.html import escape 4 4 from django.http import HttpResponseServerError, HttpResponseNotFound 5 5 from django.utils.encoding import smart_unicode 6 import os, re, sys 6 import os, re, sys, code, threading 7 7 8 8 HIDDEN_SETTINGS = re.compile('SECRET|PASSWORD|PROFANITIES_LIST') 9 9 10 def new_debugger_id(): 11 import time 12 import md5 13 return md5.new(str(time.time())).hexdigest() 14 10 15 def linebreak_iter(template_source): 11 16 yield 0 12 17 p = template_source.find('\n') … … 67 72 Create a technical server error response. The last three arguments are 68 73 the values returned from sys.exc_info() and friends. 69 74 """ 75 if settings.DEBUGGER_ENABLED: 76 debugger_id = new_debugger_id() 77 debugger_enabled = True 78 frame_storage = console.debug_sessions[debugger_id] = {} 79 else: 80 debugger_id = None 81 debugger_enabled = False 70 82 template_info = None 71 83 template_does_not_exist = False 72 84 loader_debug_info = None … … 96 108 if tb.tb_frame.f_locals.get('__traceback_hide__'): 97 109 tb = tb.tb_next 98 110 continue 111 if debugger_enabled: 112 frame_storage[str(id(tb))] = tb.tb_frame 99 113 filename = tb.tb_frame.f_code.co_filename 100 114 function = tb.tb_frame.f_code.co_name 101 115 lineno = tb.tb_lineno - 1 … … 125 139 }] 126 140 t = Template(TECHNICAL_500_TEMPLATE, name='Technical 500 template') 127 141 c = Context({ 142 'debugger_id': debugger_id, 143 'debugger_enabled': debugger_enabled, 128 144 'exception_type': exc_type.__name__, 129 145 'exception_value': smart_unicode(exc_value, errors='replace'), 130 146 'frames': frames, … … 211 227 212 228 return lower_bound, pre_context, context_line, post_context 213 229 230 class DebugCapture(object): 231 """ 232 Class that wraps sys.stdout in order to get the output 233 for the debugger. threadsafe but quite slow. 234 """ 235 _orig = None 236 237 def __init__(self): 238 self._buffer = {} 239 240 def install(cls): 241 if cls._orig: 242 return 243 cls._orig = sys.stdout 244 sys.stdout = cls() 245 install = classmethod(install) 246 247 def push(self): 248 from cStringIO import StringIO 249 tid = threading.currentThread() 250 self._buffer[tid] = StringIO() 251 252 def release(self): 253 tid = threading.currentThread() 254 if tid in self._buffer: 255 result = self._buffer[tid].getvalue() 256 del self._buffer[tid] 257 else: 258 result = '' 259 return result 260 261 def write(self, d): 262 tid = threading.currentThread() 263 if tid in self._buffer: 264 self._buffer[tid].write(d) 265 else: 266 self._orig.write(d) 267 268 269 class PlainDebugger(code.InteractiveInterpreter): 270 """ 271 Subclass of the python interactive interpreter that 272 automatically captures stdout and buffers older input. 273 """ 274 275 def __init__(self, locals=None, globals=None): 276 self.globals = globals 277 code.InteractiveInterpreter.__init__(self, locals) 278 self.prompt = '>>> ' 279 self.buffer = [] 280 281 def runsource(self, source): 282 # installs the debug capture on first access 283 DebugCapture.install() 284 prompt = self.prompt 285 sys.stdout.push() 286 try: 287 source_to_eval = ''.join(self.buffer + [source]) 288 if code.InteractiveInterpreter.runsource(self, 289 source_to_eval, '<debugger>', 'single'): 290 self.prompt = '... ' 291 self.buffer.append(source) 292 else: 293 self.prompt = '>>> ' 294 del self.buffer[:] 295 finally: 296 return prompt + source + sys.stdout.release() 297 298 def runcode(self, code): 299 try: 300 exec code in self.globals, self.locals 301 except: 302 self.showtraceback() 303 304 def write(self, data): 305 sys.stdout.write(data) 306 307 308 class AjaxDebugger(object): 309 """ 310 The AJAX Debugger 311 """ 312 313 def __init__(self): 314 self.debug_sessions = {} 315 self.consoles = {} 316 317 def send(self, debugger, frame, cmd): 318 if debugger not in self.debug_sessions: 319 return '!!! expired debugger !!!' 320 session = self.debug_sessions[debugger] 321 if frame not in session: 322 return '!!! unknown frame !!!' 323 key = '%s|%s' % (debugger, frame) 324 if key not in self.consoles: 325 self.consoles[key] = PlainDebugger( 326 session[frame].f_globals, 327 session[frame].f_locals 328 ) 329 return self.consoles[key].runsource(cmd) 330 331 332 console = AjaxDebugger() 333 214 334 # 215 335 # Templates are embedded in the file so that we know the error handler will 216 336 # always work even if the template loader is broken. … … 244 364 table td.code div { overflow:hidden; } 245 365 table.source th { color:#666; } 246 366 table.source td { font-family:monospace; white-space:pre; border-bottom:1px solid #eee; } 367 form.debug { display: block; padding: 10px 20px 10px 40px; } 368 form.debug input { margin-top: 5px; width: 100%; } 247 369 ul.traceback { list-style-type:none; } 248 370 ul.traceback li.frame { margin-bottom:1em; } 249 371 div.context { margin: 10px 0; } … … 253 375 div.context ol.context-line li span { float: right; } 254 376 div.commands { margin-left: 40px; } 255 377 div.commands a { color:black; text-decoration:none; } 378 ul.commands { margin-left: 40px; padding: 0; list-style: none; } 379 ul.commands a { color: black; text-decoration: none; } 256 380 #summary { background: #ffc; } 257 381 #summary h2 { font-weight: normal; color: #666; } 258 382 #explanation { background:#eee; } … … 268 392 </style> 269 393 <script type="text/javascript"> 270 394 //<!-- 271 function getElementsByClassName(oElm, strTagName, strClassName){ 272 // Written by Jonathan Snook, http://www.snook.ca/jon; Add-ons by Robert Nyman, http://www.robertnyman.com 273 var arrElements = (strTagName == "*" && document.all)? document.all : 395 var DEBUG_ID = {% if debugger_enabled %}'{{ debugger_id }}'{% else %}null{% endif %}; 396 397 function getElementsByClassName(oElm, strTagName, strClassName) { 398 // Written by Jonathan Snook, http://www.snook.ca/jon; Add-ons by Robert Nyman, http://www.robertnyman.com 399 var arrElements = (strTagName == "*" && document.all) ? document.all : 274 400 oElm.getElementsByTagName(strTagName); 275 var arrReturnElements = new Array(); 276 strClassName = strClassName.replace(/\-/g, "\\-"); 277 var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)"); 278 var oElement; 279 for(var i=0; i<arrElements.length; i++){ 280 oElement = arrElements[i]; 281 if(oRegExp.test(oElement.className)){ 282 arrReturnElements.push(oElement); 283 } 401 var arrReturnElements = new Array(); 402 strClassName = strClassName.replace(/\-/g, "\\-"); 403 var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)"); 404 var oElement; 405 for(var i=0; i<arrElements.length; i++) { 406 oElement = arrElements[i]; 407 if(oRegExp.test(oElement.className)) { 408 arrReturnElements.push(oElement); 284 409 } 285 return (arrReturnElements) 410 } 411 return arrReturnElements; 286 412 } 287 413 function hideAll(elems) { 288 414 for (var e = 0; e < elems.length; e++) { … … 291 417 } 292 418 window.onload = function() { 293 419 hideAll(getElementsByClassName(document, 'table', 'vars')); 420 hideAll(getElementsByClassName(document, 'form', 'debug')); 294 421 hideAll(getElementsByClassName(document, 'ol', 'pre-context')); 295 422 hideAll(getElementsByClassName(document, 'ol', 'post-context')); 296 423 hideAll(getElementsByClassName(document, 'div', 'pastebin')); … … 312 439 s.innerHTML = s.innerHTML == uarr ? darr : uarr; 313 440 return false; 314 441 } 442 function debugToggle(link, id) { 443 toggle('d' + id); 444 var s = link.getElementsByTagName('span')[0]; 445 var uarr = String.fromCharCode(0x25b6); 446 var darr = String.fromCharCode(0x25bc); 447 s.innerHTML = s.innerHTML == uarr ? darr : uarr; 448 return false; 449 } 315 450 function switchPastebinFriendly(link) { 316 451 s1 = "Switch to copy-and-paste view"; 317 452 s2 = "Switch back to interactive view"; … … 319 454 toggle('browserTraceback', 'pastebinTraceback'); 320 455 return false; 321 456 } 457 function sendDebugCommand(frameID, command, callback) { 458 var activex = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0']; 459 var con = null; 460 try { 461 con = new XMLHttpRequest(); 462 } 463 catch (e) { 464 for (var i=0; i < activex.length; i++) { 465 try { 466 con = new ActiveXObject(activex[i]); 467 } 468 catch (e) {}; 469 if (con) { 470 break; 471 } 472 } 473 } 474 var data = 'tb=' + DEBUG_ID + '&' + 475 'frame=' + frameID + '&' + 476 'cmd=' + encodeURIComponent(command); 477 con.onreadystatechange = function() { 478 if (con.readyState == 4) { 479 callback(con.responseText); 480 } 481 }; 482 con.open('POST', '?__send_to_django_debugger__=yes'); 483 con.send(data); 484 } 485 function submitCommand(frameID) { 486 var input = document.getElementById('di' + frameID); 487 var output = document.getElementById('do' + frameID); 488 output = (output.firstChild || output.appendChild(document.createTextNode(''))); 489 if (input.value == 'clear') { 490 output.nodeValue = ''; 491 } 492 else { 493 sendDebugCommand(frameID, input.value + '\\n', function(value) { 494 output.nodeValue += value; 495 }); 496 } 497 input.value = ''; 498 input.focus(); 499 return false; 500 } 322 501 //--> 323 502 </script> 324 503 </head> … … 415 594 </div> 416 595 {% endif %} 417 596 418 {% if frame.vars %} 419 <div class="commands"> 420 <a href="#" onclick="return varToggle(this, '{{ frame.id }}')"><span>▶</span> Local vars</a> 421 </div> 422 <table class="vars" id="v{{ frame.id }}"> 423 <thead> 424 <tr> 425 <th>Variable</th> 426 <th>Value</th> 427 </tr> 428 </thead> 429 <tbody> 430 {% for var in frame.vars|dictsort:"0" %} 597 {% if frame.vars or debugger_enabled %} 598 <ul class="commands"> 599 {% if frame.vars %} 600 <li><a href="#" onclick="return varToggle(this, '{{ frame.id }}')"><span>▶</span> Local vars</a></li> 601 {% endif %} 602 {% if debugger_enabled %} 603 <li><a href="#" onclick="return debugToggle(this, '{{ frame.id }}')"><span>▶</span> Debug</a></li> 604 {% endif %} 605 </ul> 606 {% if frame.vars %} 607 <table class="vars" id="v{{ frame.id }}"> 608 <thead> 431 609 <tr> 432 <t d>{{ var.0 }}</td>433 <t d class="code"><div>{{ var.1|pprint|escape }}</div></td>610 <th>Variable</th> 611 <th>Value</th> 434 612 </tr> 435 {% endfor %} 436 </tbody> 437 </table> 613 </thead> 614 <tbody> 615 {% for var in frame.vars|dictsort:"0" %} 616 <tr> 617 <td>{{ var.0 }}</td> 618 <td class="code"><div>{{ var.1|pprint|escape }}</div></td> 619 </tr> 620 {% endfor %} 621 </tbody> 622 </table> 623 {% endif %} 624 {% if debugger_enabled %} 625 <form class="debug" id="d{{ frame.id }}" onsubmit="return submitCommand('{{ frame.id }}')"> 626 <pre id="do{{ frame.id }}"></pre> 627 <input type="text" id="di{{ frame.id }}"> 628 </form> 629 {% endif %} 438 630 {% endif %} 439 631 </li> 440 632 {% endfor %} -
django/middleware/common.py
25 25 settings.APPEND_SLASH and settings.PREPEND_WWW 26 26 """ 27 27 28 # Handle Debugger AJAX Requests 29 if settings.DEBUGGER_ENABLED and \ 30 request.GET.get('__send_to_django_debugger__') == 'yes': 31 from django.views.debug import console 32 rv = console.send( 33 debugger=request.POST.get('tb'), 34 frame=request.POST.get('frame'), 35 cmd=request.POST.get('cmd', '') 36 ) 37 return http.HttpResponse(rv, mimetype='text/plain') 38 28 39 # Check for denied User-Agents 29 40 if 'HTTP_USER_AGENT' in request.META: 30 41 for user_agent_regex in settings.DISALLOWED_USER_AGENTS: