Django

Code

Ticket #3527: django_debugger_r5588.diff

File django_debugger_r5588.diff, 13.9 kB (added by John Shaffer <jshaffer2112@gmail.com>, 1 year ago)

Patch against [5588]

  • conf/global_settings.py

    old new  
    1111 
    1212DEBUG = False 
    1313TEMPLATE_DEBUG = False 
     14DEBUGGER_ENABLED = False 
    1415 
    1516# Whether to use the "Etag" header. This saves bandwidth but slows down performance. 
    1617USE_ETAGS = False 
  • views/debug.py

    old new  
    22from django.template import Template, Context, TemplateDoesNotExist 
    33from django.utils.html import escape 
    44from django.http import HttpResponseServerError, HttpResponseNotFound 
    5 import os, re, sys 
     5import os, re, sys, code, threading 
    66 
    77HIDDEN_SETTINGS = re.compile('SECRET|PASSWORD|PROFANITIES_LIST') 
    88 
     9def new_debugger_id(): 
     10    import time 
     11    import md5 
     12    return md5.new(str(time.time())).hexdigest() 
     13 
    914def linebreak_iter(template_source): 
    1015    yield 0 
    1116    p = template_source.find('\n') 
     
    6671    Create a technical server error response. The last three arguments are 
    6772    the values returned from sys.exc_info() and friends. 
    6873    """ 
     74    if settings.DEBUGGER_ENABLED: 
     75        debugger_id = new_debugger_id() 
     76        debugger_enabled = True 
     77        frame_storage = console.debug_sessions[debugger_id] = {} 
     78    else: 
     79        debugger_id = None 
     80        debugger_enabled = False 
    6981    template_info = None 
    7082    template_does_not_exist = False 
    7183    loader_debug_info = None 
     
    95107        if tb.tb_frame.f_locals.get('__traceback_hide__'): 
    96108            tb = tb.tb_next 
    97109            continue 
     110        if debugger_enabled: 
     111            frame_storage[str(id(tb))] = tb.tb_frame 
    98112        filename = tb.tb_frame.f_code.co_filename 
    99113        function = tb.tb_frame.f_code.co_name 
    100114        lineno = tb.tb_lineno - 1 
     
    124138        }] 
    125139    t = Template(TECHNICAL_500_TEMPLATE, name='Technical 500 template') 
    126140    c = Context({ 
     141        'debugger_id': debugger_id, 
     142        'debugger_enabled': debugger_enabled, 
    127143        'exception_type': exc_type.__name__, 
    128144        'exception_value': exc_value, 
    129145        'frames': frames, 
     
    199215 
    200216    return lower_bound, pre_context, context_line, post_context 
    201217 
     218class DebugCapture(object): 
     219    """ 
     220    Class that wraps sys.stdout in order to get the output 
     221    for the debugger. threadsafe but quite slow. 
     222    """ 
     223    _orig = None 
     224 
     225    def __init__(self): 
     226        self._buffer = {} 
     227 
     228    def install(cls): 
     229        if cls._orig: 
     230            return 
     231        cls._orig = sys.stdout 
     232        sys.stdout = cls() 
     233    install = classmethod(install) 
     234 
     235    def push(self): 
     236        from cStringIO import StringIO 
     237        tid = threading.currentThread() 
     238        self._buffer[tid] = StringIO() 
     239 
     240    def release(self): 
     241        tid = threading.currentThread() 
     242        if tid in self._buffer: 
     243            result = self._buffer[tid].getvalue() 
     244            del self._buffer[tid] 
     245        else: 
     246            result = '' 
     247        return result 
     248 
     249    def write(self, d): 
     250        tid = threading.currentThread() 
     251        if tid in self._buffer: 
     252            self._buffer[tid].write(d) 
     253        else: 
     254            self._orig.write(d) 
     255 
     256 
     257class PlainDebugger(code.InteractiveInterpreter): 
     258    """ 
     259    Subclass of the python interactive interpreter that 
     260    automatically captures stdout and buffers older input. 
     261    """ 
     262 
     263    def __init__(self, locals=None, globals=None): 
     264        self.globals = globals 
     265        code.InteractiveInterpreter.__init__(self, locals) 
     266        self.prompt = '>>> ' 
     267        self.buffer = [] 
     268 
     269    def runsource(self, source): 
     270        # installs the debug capture on first access 
     271        DebugCapture.install() 
     272        prompt = self.prompt 
     273        sys.stdout.push() 
     274        try: 
     275            source_to_eval = ''.join(self.buffer + [source]) 
     276            if code.InteractiveInterpreter.runsource(self, 
     277               source_to_eval, '<debugger>', 'single'): 
     278                self.prompt = '... ' 
     279                self.buffer.append(source) 
     280            else: 
     281                self.prompt = '>>> ' 
     282                del self.buffer[:] 
     283        finally: 
     284            return prompt + source + sys.stdout.release() 
     285 
     286    def runcode(self, code): 
     287        try: 
     288            exec code in self.globals, self.locals 
     289        except: 
     290            self.showtraceback() 
     291 
     292    def write(self, data): 
     293        sys.stdout.write(data) 
     294 
     295 
     296class AjaxDebugger(object): 
     297    """ 
     298    The AJAX Debugger 
     299    """ 
     300 
     301    def __init__(self): 
     302        self.debug_sessions = {} 
     303        self.consoles = {} 
     304 
     305    def send(self, debugger, frame, cmd): 
     306        if debugger not in self.debug_sessions: 
     307            return '!!! expired debugger !!!' 
     308        session = self.debug_sessions[debugger] 
     309        if frame not in session: 
     310            return '!!! unknown frame !!!' 
     311        key = '%s|%s' % (debugger, frame) 
     312        if key not in self.consoles: 
     313            self.consoles[key] = PlainDebugger( 
     314                session[frame].f_globals, 
     315                session[frame].f_locals 
     316            ) 
     317        return self.consoles[key].runsource(cmd) 
     318 
     319 
     320console = AjaxDebugger() 
     321 
    202322# 
    203323# Templates are embedded in the file so that we know the error handler will 
    204324# always work even if the template loader is broken. 
    205325# 
    206326 
    207 TECHNICAL_500_TEMPLATE = """ 
     327TECHNICAL_500_TEMPLATE = r""" 
    208328<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 
    209329<html lang="en"> 
    210330<head> 
     
    217337    body * * { padding:0; } 
    218338    body { font:small sans-serif; } 
    219339    body>div { border-bottom:1px solid #ddd; } 
     340    a { color: #333; } 
    220341    h1 { font-weight:normal; } 
    221342    h2 { margin-bottom:.8em; } 
    222343    h2 span { font-size:80%; color:#666; font-weight:normal; } 
     
    232353    table td.code div { overflow:hidden; } 
    233354    table.source th { color:#666; } 
    234355    table.source td { font-family:monospace; white-space:pre; border-bottom:1px solid #eee; } 
     356    form.debug { display: block; padding: 10px 20px 10px 40px; } 
     357    form.debug input { margin-top: 5px; width: 100%; } 
    235358    ul.traceback { list-style-type:none; } 
    236359    ul.traceback li.frame { margin-bottom:1em; } 
    237360    div.context { margin: 10px 0; } 
     
    239362    div.context ol li { font-family:monospace; white-space:pre; color:#666; cursor:pointer; } 
    240363    div.context ol.context-line li { color:black; background-color:#ccc; } 
    241364    div.context ol.context-line li span { float: right; } 
    242     div.commands { margin-left: 40px; } 
    243     div.commands a { color:black; text-decoration:none; } 
     365    ul.commands { margin-left: 40px; padding: 0; list-style: none; } 
     366    ul.commands a { color:black; text-decoration:none; } 
    244367    #summary { background: #ffc; } 
    245368    #summary h2 { font-weight: normal; color: #666; } 
    246369    #explanation { background:#eee; } 
     
    256379  </style> 
    257380  <script type="text/javascript"> 
    258381  //<!-- 
    259     function getElementsByClassName(oElm, strTagName, strClassName){ 
    260         // Written by Jonathan Snook, http://www.snook.ca/jon; Add-ons by Robert Nyman, http://www.robertnyman.com 
    261         var arrElements = (strTagName == "*" && document.all)? document.all : 
     382    var DEBUG_ID = {% if debugger_enabled %}'{{ debugger_id }}'{% else %}null{% endif %}; 
     383 
     384    function getElementsByClassName(oElm, strTagName, strClassName) { 
     385      // Written by Jonathan Snook, http://www.snook.ca/jon; Add-ons by Robert Nyman, http://www.robertnyman.com 
     386      var arrElements = (strTagName == "*" && document.all) ? document.all : 
    262387        oElm.getElementsByTagName(strTagName); 
    263         var arrReturnElements = new Array(); 
    264         strClassName = strClassName.replace(/\-/g, "\\-"); 
    265         var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)"); 
    266         var oElement; 
    267         for(var i=0; i<arrElements.length; i++){ 
    268             oElement = arrElements[i]; 
    269             if(oRegExp.test(oElement.className)){ 
    270                 arrReturnElements.push(oElement); 
    271             } 
     388      var arrReturnElements = new Array(); 
     389      strClassName = strClassName.replace(/\-/g, "\\-"); 
     390      var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)"); 
     391      var oElement; 
     392      for(var i=0; i<arrElements.length; i++) { 
     393        oElement = arrElements[i]; 
     394        if(oRegExp.test(oElement.className)) { 
     395          arrReturnElements.push(oElement); 
    272396        } 
    273         return (arrReturnElements) 
     397      } 
     398      return arrReturnElements; 
    274399    } 
    275400    function hideAll(elems) { 
    276401      for (var e = 0; e < elems.length; e++) { 
     
    279404    } 
    280405    window.onload = function() { 
    281406      hideAll(getElementsByClassName(document, 'table', 'vars')); 
     407      hideAll(getElementsByClassName(document, 'form', 'debug')); 
    282408      hideAll(getElementsByClassName(document, 'ol', 'pre-context')); 
    283409      hideAll(getElementsByClassName(document, 'ol', 'post-context')); 
    284410      hideAll(getElementsByClassName(document, 'div', 'pastebin')); 
     
    300426      s.innerHTML = s.innerHTML == uarr ? darr : uarr; 
    301427      return false; 
    302428    } 
     429    function debugToggle(link, id) { 
     430      toggle('d' + id); 
     431      var s = link.getElementsByTagName('span')[0]; 
     432      var uarr = String.fromCharCode(0x25b6); 
     433      var darr = String.fromCharCode(0x25bc); 
     434      s.innerHTML = s.innerHTML == uarr ? darr : uarr; 
     435      return false; 
     436    } 
    303437    function switchPastebinFriendly(link) { 
    304438      s1 = "Switch to copy-and-paste view"; 
    305439      s2 = "Switch back to interactive view"; 
     
    307441      toggle('browserTraceback', 'pastebinTraceback'); 
    308442      return false; 
    309443    } 
     444    function sendDebugCommand(frameID, command, callback) { 
     445      var activex = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0']; 
     446      var con = null; 
     447      try { 
     448        con = new XMLHttpRequest(); 
     449      } 
     450      catch (e) { 
     451        for (var i=0; i < activex.length; i++) { 
     452          try { 
     453            con = new ActiveXObject(activex[i]); 
     454          } 
     455          catch (e) {}; 
     456          if (con) { 
     457            break; 
     458          } 
     459        } 
     460      } 
     461      var data = 'tb=' + DEBUG_ID + '&' + 
     462                 'frame=' + frameID + '&' + 
     463                 'cmd=' + encodeURIComponent(command); 
     464      con.onreadystatechange = function() { 
     465        if (con.readyState == 4) { 
     466          callback(con.responseText); 
     467        } 
     468      }; 
     469      con.open('POST', '?__send_to_django_debugger__=yes'); 
     470      con.send(data); 
     471    } 
     472    function submitCommand(frameID) { 
     473      var input = document.getElementById('di' + frameID); 
     474      var output = document.getElementById('do' + frameID); 
     475      output = (output.firstChild || output.appendChild(document.createTextNode(''))); 
     476      if (input.value == 'clear') { 
     477        output.nodeValue = ''; 
     478      } 
     479      else { 
     480        sendDebugCommand(frameID, input.value + '\n', function(value) { 
     481          output.nodeValue += value; 
     482        }); 
     483      } 
     484      input.value = ''; 
     485      input.focus(); 
     486      return false; 
     487    } 
    310488    //--> 
    311489  </script> 
    312490</head> 
     
    403581            </div> 
    404582          {% endif %} 
    405583 
    406           {% if frame.vars %} 
    407             <div class="commands"> 
    408                 <a href="#" onclick="return varToggle(this, '{{ frame.id }}')"><span>&#x25b6;</span> Local vars</a> 
    409             </div> 
    410             <table class="vars" id="v{{ frame.id }}"> 
    411               <thead> 
    412                 <tr> 
    413                   <th>Variable</th> 
    414                   <th>Value</th> 
    415                 </tr> 
    416               </thead> 
    417               <tbody> 
    418                 {% for var in frame.vars|dictsort:"0" %} 
     584          {% if frame.vars or debugger_enabled %} 
     585            <ul class="commands"> 
     586              {% if frame.vars %} 
     587                <li><a href="#" onclick="return varToggle(this, '{{ frame.id }}')"><span>&#x25b6;</span> Local vars</a></li> 
     588              {% endif %} 
     589              {% if debugger_enabled %} 
     590                <li><a href="#" onclick="return debugToggle(this, '{{ frame.id }}')"><span>&#x25b6;</span> Debug</a></li> 
     591              {% endif %} 
     592            </ul> 
     593            {% if frame.vars %} 
     594              <table class="vars" id="v{{ frame.id }}"> 
     595                <thead> 
    419596                  <tr> 
    420                     <td>{{ var.0 }}</td
    421                     <td class="code"><div>{{ var.1|pprint|escape }}</div></td
     597                    <th>Variable</th
     598                    <th>Value</th
    422599                  </tr> 
    423                 {% endfor %} 
    424               </tbody> 
    425             </table> 
     600                </thead> 
     601                <tbody> 
     602                  {% for var in frame.vars|dictsort:"0" %} 
     603                    <tr> 
     604                      <td>{{ var.0 }}</td> 
     605                      <td class="code"><div>{{ var.1|pprint|escape }}</div></td> 
     606                    </tr> 
     607                  {% endfor %} 
     608                </tbody> 
     609              </table> 
     610            {% endif %} 
     611            {% if debugger_enabled %} 
     612              <form class="debug" id="d{{ frame.id }}" onsubmit="return submitCommand('{{ frame.id }}')"> 
     613                <pre id="do{{ frame.id }}"></pre> 
     614                <input type="text" id="di{{ frame.id }}"> 
     615              </form> 
     616            {% endif %} 
    426617          {% endif %} 
    427618        </li> 
    428619      {% endfor %} 
  • middleware/common.py

    old new  
    2525        settings.APPEND_SLASH and settings.PREPEND_WWW 
    2626        """ 
    2727 
     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 
    2839        # Check for denied User-Agents 
    2940        if 'HTTP_USER_AGENT' in request.META: 
    3041            for user_agent_regex in settings.DISALLOWED_USER_AGENTS: