Django

Code

Ticket #3527: django_debugger_r5620.diff

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

Patch against [5620]. This one doesn't cause any visual changes.

  • django/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 
  • django/views/debug.py

    old new  
    33from django.utils.html import escape 
    44from django.http import HttpResponseServerError, HttpResponseNotFound 
    55from django.utils.encoding import smart_unicode 
    6 import os, re, sys 
     6import os, re, sys, code, threading 
    77 
    88HIDDEN_SETTINGS = re.compile('SECRET|PASSWORD|PROFANITIES_LIST') 
    99 
     10def new_debugger_id(): 
     11    import time 
     12    import md5 
     13    return md5.new(str(time.time())).hexdigest() 
     14 
    1015def linebreak_iter(template_source): 
    1116    yield 0 
    1217    p = template_source.find('\n') 
     
    6772    Create a technical server error response. The last three arguments are 
    6873    the values returned from sys.exc_info() and friends. 
    6974    """ 
     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 
    7082    template_info = None 
    7183    template_does_not_exist = False 
    7284    loader_debug_info = None 
     
    96108        if tb.tb_frame.f_locals.get('__traceback_hide__'): 
    97109            tb = tb.tb_next 
    98110            continue 
     111        if debugger_enabled: 
     112            frame_storage[str(id(tb))] = tb.tb_frame 
    99113        filename = tb.tb_frame.f_code.co_filename 
    100114        function = tb.tb_frame.f_code.co_name 
    101115        lineno = tb.tb_lineno - 1 
     
    125139        }] 
    126140    t = Template(TECHNICAL_500_TEMPLATE, name='Technical 500 template') 
    127141    c = Context({ 
     142        'debugger_id': debugger_id, 
     143        'debugger_enabled': debugger_enabled, 
    128144        'exception_type': exc_type.__name__, 
    129145        'exception_value': smart_unicode(exc_value, errors='replace'), 
    130146        'frames': frames, 
     
    211227 
    212228    return lower_bound, pre_context, context_line, post_context 
    213229 
     230class 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 
     269class 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 
     308class 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 
     332console = AjaxDebugger() 
     333 
    214334# 
    215335# Templates are embedded in the file so that we know the error handler will 
    216336# always work even if the template loader is broken. 
     
    244364    table td.code div { overflow:hidden; } 
    245365    table.source th { color:#666; } 
    246366    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%; } 
    247369    ul.traceback { list-style-type:none; } 
    248370    ul.traceback li.frame { margin-bottom:1em; } 
    249371    div.context { margin: 10px 0; } 
     
    253375    div.context ol.context-line li span { float: right; } 
    254376    div.commands { margin-left: 40px; } 
    255377    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; } 
    256380    #summary { background: #ffc; } 
    257381    #summary h2 { font-weight: normal; color: #666; } 
    258382    #explanation { background:#eee; } 
     
    268392  </style> 
    269393  <script type="text/javascript"> 
    270394  //<!-- 
    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 : 
    274400        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); 
    284409        } 
    285         return (arrReturnElements) 
     410      } 
     411      return arrReturnElements; 
    286412    } 
    287413    function hideAll(elems) { 
    288414      for (var e = 0; e < elems.length; e++) { 
     
    291417    } 
    292418    window.onload = function() { 
    293419      hideAll(getElementsByClassName(document, 'table', 'vars')); 
     420      hideAll(getElementsByClassName(document, 'form', 'debug')); 
    294421      hideAll(getElementsByClassName(document, 'ol', 'pre-context')); 
    295422      hideAll(getElementsByClassName(document, 'ol', 'post-context')); 
    296423      hideAll(getElementsByClassName(document, 'div', 'pastebin')); 
     
    312439      s.innerHTML = s.innerHTML == uarr ? darr : uarr; 
    313440      return false; 
    314441    } 
     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    } 
    315450    function switchPastebinFriendly(link) { 
    316451      s1 = "Switch to copy-and-paste view"; 
    317452      s2 = "Switch back to interactive view"; 
     
    319454      toggle('browserTraceback', 'pastebinTraceback'); 
    320455      return false; 
    321456    } 
     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    } 
    322501    //--> 
    323502  </script> 
    324503</head> 
     
    415594            </div> 
    416595          {% endif %} 
    417596 
    418           {% if frame.vars %} 
    419             <div class="commands"> 
    420                 <a href="#" onclick="return varToggle(this, '{{ frame.id }}')"><span>&#x25b6;</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>&#x25b6;</span> Local vars</a></li> 
     601              {% endif %} 
     602              {% if debugger_enabled %} 
     603                <li><a href="#" onclick="return debugToggle(this, '{{ frame.id }}')"><span>&#x25b6;</span> Debug</a></li> 
     604              {% endif %} 
     605            </ul> 
     606            {% if frame.vars %} 
     607              <table class="vars" id="v{{ frame.id }}"> 
     608                <thead> 
    431609                  <tr> 
    432                     <td>{{ var.0 }}</td
    433                     <td class="code"><div>{{ var.1|pprint|escape }}</div></td
     610                    <th>Variable</th
     611                    <th>Value</th
    434612                  </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 %} 
    438630          {% endif %} 
    439631        </li> 
    440632      {% endfor %} 
  • django/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: