[debug] add in each html snippet from where it has been generated in the code
Closes #17219704
--- a/cubicweb/pyramid/pyramidctl.py Thu Nov 28 11:48:03 2019 +0100
+++ b/cubicweb/pyramid/pyramidctl.py Wed Jul 24 16:11:22 2019 +0200
@@ -37,6 +37,7 @@
from cubicweb.cwctl import CWCTL, InstanceCommand, init_cmdline_log_threshold
from cubicweb.pyramid import wsgi_application_from_cwconfig
from cubicweb.pyramid.config import get_random_secret_key
+from cubicweb.view import inject_html_generating_call_on_w
from cubicweb.server import serverctl
from cubicweb.web.webctl import WebCreateHandler
from cubicweb.toolsutils import fill_templated_file
@@ -271,6 +272,11 @@
"https://docs.pylonsproject.org/projects/pyramid_debugtoolbar/en/latest/")
sys.exit(1)
+ if self['debug']:
+ # this is for injecting those into generated html:
+ # > cubicweb-generated-by="module.Class" cubicweb-from-source="/path/to/file.py:42"
+ inject_html_generating_call_on_w()
+
app = wsgi_application_from_cwconfig(
cwconfig, profile=self['profile'],
profile_output=self['profile-output'],
--- a/cubicweb/view.py Thu Nov 28 11:48:03 2019 +0100
+++ b/cubicweb/view.py Wed Jul 24 16:11:22 2019 +0200
@@ -20,9 +20,11 @@
from cubicweb import _
+import re
from io import BytesIO
from warnings import warn
from functools import partial
+from inspect import getframeinfo, stack
from logilab.common.registry import yes
from logilab.mtconverter import xml_escape
@@ -46,6 +48,11 @@
STRICT_DOCTYPE_NOEXT = u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n'
STRICT_DOCTYPE = STRICT_DOCTYPE_NOEXT # bw compat
+
+def inject_html_generating_call_on_w():
+ View.debug_html_rendering = True
+
+
# base view object ############################################################
class View(AppObject):
@@ -86,17 +93,50 @@
add_to_breadcrumbs = True
category = 'view'
paginable = True
+ debug_html_rendering = False
def __init__(self, req=None, rset=None, **kwargs):
super(View, self).__init__(req, rset=rset, **kwargs)
- self.w = None
+ self._w = None
@property
def content_type(self):
return self._cw.html_content_type()
+ def w(self, text):
+ if self._w is None:
+ raise Exception('Error: call to %s.w before it has been set' % self)
+
+ if self.debug_html_rendering:
+ caller = getframeinfo(stack()[1][0])
+
+ if isinstance(text, str) and re.match(r"^ *<[a-zA-Z0-9]+( .*>|>)", text):
+ to_add = 'cubicweb-generated-by="%s.%s" cubicweb-from-source="%s:%s"' % (
+ self.__module__, self.__class__.__name__,
+ caller.filename, caller.lineno,
+ )
+
+ before_space, beginning_of_html = text.split("<", 1)
+
+ # when it's a tag without attribues like "<b>"
+ if re.match(r"^ *<[a-zA-Z0-9]+>", text):
+ tag_name, rest = beginning_of_html.split(">", 1)
+ rest = ">" + rest
+ else:
+ tag_name, rest = beginning_of_html.split(" ", 1)
+ rest = " " + rest
+
+ text = "%(before_space)s<%(tag_name)s %(to_add)s%(rest)s" % {
+ "before_space": before_space,
+ "tag_name": tag_name,
+ "to_add": to_add,
+ "rest": rest,
+ }
+
+ return self._w(text)
+
def set_stream(self, w=None):
- if self.w is not None:
+ if self._w is not None:
return
if w is None:
if self.binary:
@@ -106,7 +146,7 @@
w = stream.write
else:
stream = None
- self.w = w
+ self._w = w
return stream
# main view interface #####################################################
@@ -238,7 +278,7 @@
def wview(self, __vid, rset=None, __fallback_vid=None, **kwargs):
"""shortcut to self.view method automatically passing self.w as argument
"""
- self._cw.view(__vid, rset, __fallback_vid, w=self.w, **kwargs)
+ self._cw.view(__vid, rset, __fallback_vid, w=self._w, **kwargs)
def whead(self, data):
self._cw.html_headers.write(data)
@@ -466,7 +506,7 @@
doctype = '<!DOCTYPE html>'
def set_stream(self, w=None):
- if self.w is not None:
+ if self._w is not None:
return
if w is None:
if self.binary:
@@ -476,7 +516,7 @@
w = stream.write
else:
stream = None
- self.w = w
+ self._w = w
return stream
def write_doctype(self, xmldecl=True):
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cubicweb/web/test/unittest_views_debug_html.py Wed Jul 24 16:11:22 2019 +0200
@@ -0,0 +1,49 @@
+# copyright 2019 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
+
+import inspect
+
+from cubicweb.view import View
+from cubicweb.devtools.testlib import CubicWebTC
+
+
+class DebugHtmlRenderingTC(CubicWebTC):
+ def setUp(self):
+ super().setUp()
+ View.debug_html_rendering = True
+
+ def tearDown(self):
+ super().tearDown()
+ View.debug_html_rendering = False
+
+ def test_debug_html_rendering_inject_tags(self):
+ with self.admin_access.web_request() as req:
+ view = self.vreg['views'].select("index", req)
+ view_class = view.__class__
+ page = view.render()
+
+ self.assertIn('cubicweb-generated-by="%s.%s"' % (view_class.__module__,
+ view_class.__name__),
+ page)
+ source_file = inspect.getsourcefile(view_class)
+ self.assertIn('cubicweb-from-source="%s' % (source_file), page)
+
+
+if __name__ == '__main__':
+ from logilab.common.testlib import unittest_main
+ unittest_main()