Ticket #3527: django_debugger_r5620.diff

File django_debugger_r5620.diff, 13.4 KB (added by John Shaffer <jshaffer2112@…>, 8 years ago)

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

  • django/conf/global_settings.py

     
    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

     
    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

     
    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:
Back to Top