[debug-toolbar] add registry decisions debug panel

Closes #17219866

authorLaurent Peuch <cortex@worlddomination.be>
changeset7d2c61d40fe9
branchdefault
phasepublic
hiddenno
parent revision#d50661367401 Increase needed version of logilab-common to >= 1.5.0
child revision#c15db18d832f [html] Add tags ol, ul, li, i, q, #a61e0fe17a69 [debug/fix] ensure that not syntax highlighted code is a string
files modified by this revision
cubicweb/cwvreg.py
cubicweb/debug.py
cubicweb/pyramid/debug_toolbar_templates/registry_decisions.dbtmako
cubicweb/pyramid/debugtoolbar_panels.py
# HG changeset patch
# User Laurent Peuch <cortex@worlddomination.be>
# Date 1568260750 -7200
# Thu Sep 12 05:59:10 2019 +0200
# Node ID 7d2c61d40fe9ab107fc10111c46b785e8304e17a
# Parent d506613674011d6ea61b48891744abbc8b18d9c6
[debug-toolbar] add registry decisions debug panel

Closes #17219866

diff --git a/cubicweb/cwvreg.py b/cubicweb/cwvreg.py
@@ -31,10 +31,11 @@
1 
2  from rql import RQLHelper
3  from yams.constraints import BASE_CONVERTERS
4 
5  from cubicweb import _
6 +from cubicweb.debug import emit_to_debug_channel
7  from cubicweb import (CW_SOFTWARE_ROOT, ETYPE_NAME_MAP, CW_EVENT_MANAGER,
8                        onevent, Binary, UnknownProperty, UnknownEid)
9  from cubicweb.predicates import appobject_selectable, _reset_is_instance_cache
10 
11 
@@ -70,10 +71,20 @@
12          :param vreg: the :py:class:`CWRegistryStore` managing this registry.
13          """
14          super(CWRegistry, self).__init__(True)
15          self.vreg = vreg
16 
17 +    def _select_best(self, objects, *args, **kwargs):
18 +        """
19 +        Overwrite version of Registry._select_best to emit debug information.
20 +        """
21 +        def emit_registry_debug_information(debug_registry_select_best):
22 +            emit_to_debug_channel("registry_decisions", debug_registry_select_best)
23 +
24 +        kwargs["debug_callback"] = emit_registry_debug_information
25 +        return super()._select_best(objects, *args, **kwargs)
26 +
27      @property
28      def schema(self):
29          """The :py:class:`cubicweb.schema.CubicWebSchema`
30          """
31          return self.vreg.schema
diff --git a/cubicweb/debug.py b/cubicweb/debug.py
@@ -24,10 +24,11 @@
32  SUBSCRIBERS = {
33      "controller": [],
34      "rql": [],
35      "sql": [],
36      "vreg": [],
37 +    "registry_decisions": [],
38  }
39 
40 
41  def subscribe_to_debug_channel(channel, subscriber):
42      if channel not in SUBSCRIBERS.keys():
diff --git a/cubicweb/pyramid/debug_toolbar_templates/registry_decisions.dbtmako b/cubicweb/pyramid/debug_toolbar_templates/registry_decisions.dbtmako
@@ -0,0 +1,65 @@
43 +<%def name="render_object(obj)">
44 +% if hasattr(obj, "__name__"):
45 +    ${obj.__module__}.${obj.__name__}
46 +% else:
47 +    ${obj}
48 +% endif
49 +</%def>
50 +
51 +<table class="table table-bordered table-striped">
52 +    <tr>
53 +        <th>Result</th>
54 +        <th>Decision</th>
55 +    </tr>
56 +    <tr></tr>
57 +% for registry_decision in registry_decisions:
58 +    <tr>
59 +        <td colspan="2"><b>${repr(registry_decision["key"])} -&gt; ${render_object(registry_decision["winner"])}</b></td>
60 +    </tr>
61 +    <tr>
62 +        <td>
63 +            <p>End score: ${registry_decision["end_score"]}</p>
64 +            <div class="highlight-inline">args: ${highlight(registry_decision["args"], "html") | n}</div>
65 +            <div>kwargs:
66 +                <ul>
67 +                % for key, value in registry_decision["kwargs"].items():
68 +                    <li>${repr(key)}: ${repr(value)}</li>
69 +                % endfor
70 +                </ul>
71 +            </div>
72 +        </td>
73 +
74 +        <td>
75 +            <ul>
76 +                % for obj in registry_decision["all_objects"]:
77 +                <li>
78 +                    ${obj["score"]}: ${render_object(obj["object"])}
79 +                </li>
80 +                % endfor
81 +            </ul>
82 +        </td>
83 +
84 +    </tr>
85 +
86 +% endfor
87 +</table>
88 +<style>
89 +${generate_css() | n}
90 +
91 +.highlight-inline {
92 +    margin: 0 0 10px; /* like <p> */
93 +}
94 +
95 +.highlight-inline > .highlight {
96 +    display: inline;
97 +}
98 +
99 +.highlight > pre {
100 +    word-break: unset;
101 +    border: none;
102 +    margin: 0;
103 +    padding: 0;
104 +    background-color: unset;
105 +    display: inline;
106 +}
107 +</style>
diff --git a/cubicweb/pyramid/debugtoolbar_panels.py b/cubicweb/pyramid/debugtoolbar_panels.py
@@ -52,10 +52,63 @@
108 
109      def process_response(self, response):
110          unsubscribe_to_debug_channel("controller", self.collect_controller)
111 
112 
113 +class RegistryDecisionsDebugPanel(DebugPanel):
114 +    """
115 +    CubicWeb registry decisions debug panel
116 +    """
117 +
118 +    name = 'RegistryDecisions'
119 +    title = 'Registry Decisions'
120 +    nav_title = 'Registry Decisions'
121 +
122 +    has_content = True
123 +    template = 'cubicweb.pyramid:debug_toolbar_templates/registry_decisions.dbtmako'
124 +
125 +    def __init__(self, request):
126 +        # clear on every new response
127 +        self.data = {
128 +            'registry_decisions': [],
129 +            'vreg': None,
130 +            'highlight': highlight_html,
131 +            'generate_css': generate_css,
132 +        }
133 +
134 +        subscribe_to_debug_channel("vreg", self.collect_vreg)
135 +        subscribe_to_debug_channel("registry_decisions", self.collect_registry_decisions)
136 +
137 +    def collect_vreg(self, message):
138 +        self.data["vreg"] = message["vreg"]
139 +
140 +    def collect_registry_decisions(self, decision):
141 +        # decision = {
142 +        #     "all_objects": [],
143 +        #     "end_score": int,
144 +        #     "winners": [],
145 +        #     "registry": obj,
146 +        #     "args": args,
147 +        #     "kwargs": kwargs,
148 +        # }
149 +        decision["key"] = None
150 +        self.data["registry_decisions"].append(decision)
151 +
152 +    def link_registry_to_their_key(self):
153 +        if self.data["vreg"]:
154 +            # use "id" here to be hashable
155 +            registry_to_key = {id(registry): key for key, registry in self.data["vreg"].items()}
156 +            for decision in self.data["registry_decisions"]:
157 +                decision["key"] = registry_to_key.get(id(decision["self"]))
158 +
159 +    def process_response(self, response):
160 +        unsubscribe_to_debug_channel("registry_decisions", self.collect_registry_decisions)
161 +        unsubscribe_to_debug_channel("vreg", self.collect_vreg)
162 +
163 +        self.link_registry_to_their_key()
164 +
165 +
166  class RegistryDebugPanel(DebugPanel):
167      """
168      CubicWeb registry content and decisions debug panel
169      """
170 
@@ -188,8 +241,9 @@
171          unsubscribe_to_debug_channel("sql", self.collect_sql_queries)
172 
173 
174  def includeme(config):
175      config.add_debugtoolbar_panel(CubicWebDebugPanel)
176 +    config.add_debugtoolbar_panel(RegistryDecisionsDebugPanel)
177      config.add_debugtoolbar_panel(RegistryDebugPanel)
178      config.add_debugtoolbar_panel(RQLDebugPanel)
179      config.add_debugtoolbar_panel(SQLDebugPanel)