# HG changeset patch # User RĂ©mi Cardona # Date 1416395612 -3600 # Node ID 0aebb1c0f849b469fe986c4e018b754ca489805a # Parent 6346f53c85f10a417b8bcfff9eb09c1294c0c95a [utils] Add a '_cwtracehtml' GET parameter to trace self._cw.w() calls (closes #4601327) The core of this patch is in UStringIO.write(). When tracing is enabled, write() doesn't just append the 'value' argument to the underlying list. Instead, a stack trace is recorded and a special HTML "source" is formatted. The output with tracing enabled is an HTML page, with the original HTML escaped, and made clickable to show the stack trace when the write() call was done. This allows answering the recurring question: "who wrote this tag here?!" diff -r 6346f53c85f1 -r 0aebb1c0f849 utils.py --- a/utils.py Wed Nov 19 11:57:55 2014 +0100 +++ b/utils.py Wed Nov 19 12:13:32 2014 +0100 @@ -206,12 +206,23 @@ specifed in the constructor """ + def __init__(self, tracewrites=False, *args, **kwargs): + self.tracewrites = tracewrites + super(UStringIO, self).__init__(*args, **kwargs) + def __nonzero__(self): return True def write(self, value): assert isinstance(value, unicode), u"unicode required not %s : %s"\ % (type(value).__name__, repr(value)) + if self.tracewrites: + from traceback import format_stack + stack = format_stack(None)[:-1] + escaped_stack = xml_escape(json_dumps(u'\n'.join(stack))) + escaped_html = xml_escape(value).replace('\n', '
\n') + tpl = u'%s' + value = tpl % (escaped_stack, escaped_html) self.append(value) def getvalue(self): @@ -234,8 +245,8 @@ script_opening = u'' - def __init__(self, req): - super(HTMLHead, self).__init__() + def __init__(self, req, *args, **kwargs): + super(HTMLHead, self).__init__(*args, **kwargs) self.jsvars = [] self.jsfiles = [] self.cssfiles = [] @@ -399,10 +410,15 @@ w(self.script_opening) w(u'\n\n'.join(self.post_inlined_scripts)) w(self.script_closing) - header = super(HTMLHead, self).getvalue() - if skiphead: - return header - return u'\n%s\n' % header + # at the start of this function, the parent UStringIO may already have + # data in it, so we can't w(u'\n') at the top. Instead, we create + # a temporary UStringIO to get the same debugging output formatting + # if debugging is enabled. + headtag = UStringIO(tracewrites=self.tracewrites) + if not skiphead: + headtag.write(u'\n') + w(u'\n') + return headtag.getvalue() + super(HTMLHead, self).getvalue() class HTMLStream(object): @@ -416,10 +432,11 @@ """ def __init__(self, req): + self.tracehtml = req.tracehtml # stream for self.head = req.html_headers # main stream - self.body = UStringIO() + self.body = UStringIO(tracewrites=req.tracehtml) # this method will be assigned to self.w in views self.write = self.body.write self.doctype = u'' @@ -457,6 +474,26 @@ def getvalue(self): """writes HTML headers, closes tag and writes HTML body""" + if self.tracehtml: + css = u'\n'.join((u'span {', + u' font-family: monospace;', + u' word-break: break-all;', + u' word-wrap: break-word;', + u'}', + u'span:hover {', + u' color: red;', + u' text-decoration: underline;', + u'}')) + style = u'\n' % css + return (u'\n' + + u'\n\n%s\n\n' % style + + u'\n' + + u'' + xml_escape(self.doctype) + u'
' + + u'' + xml_escape(self.htmltag) + u'
' + + self.head.getvalue() + + self.body.getvalue() + + u'' + xml_escape(u'') + u'' + + u'\n') return u'%s\n%s\n%s\n%s\n' % (self.doctype, self.htmltag, self.head.getvalue(), diff -r 6346f53c85f1 -r 0aebb1c0f849 web/request.py --- a/web/request.py Wed Nov 19 11:57:55 2014 +0100 +++ b/web/request.py Wed Nov 19 12:13:32 2014 +0100 @@ -128,8 +128,12 @@ self.datadir_url = vreg.config.https_datadir_url else: self.datadir_url = vreg.config.datadir_url + #: enable UStringIO's write tracing + self.tracehtml = False + if vreg.config.debugmode: + self.tracehtml = bool(form.pop('_cwtracehtml', False)) #: raw html headers that can be added from any view - self.html_headers = HTMLHead(self) + self.html_headers = HTMLHead(self, tracewrites=self.tracehtml) #: received headers self._headers_in = Headers() if headers is not None: