pyglove 0.4.5.dev202410170809__py3-none-any.whl → 0.4.5.dev202410190807__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,8 +14,7 @@
14
14
  """HTML Tree View (The default view for PyGlove objects)."""
15
15
 
16
16
  import inspect
17
- import re
18
- from typing import Any, Callable, Dict, Iterable, Optional, Sequence, Union
17
+ from typing import Any, Callable, Dict, Iterable, Literal, Optional, Sequence, Tuple, Union
19
18
 
20
19
  from pyglove.core import object_utils
21
20
  from pyglove.core.views.html import base
@@ -38,93 +37,84 @@ class HtmlTreeView(HtmlView):
38
37
  class Extension(HtmlView.Extension):
39
38
  """The base class for extensions for HtmlTreeView."""
40
39
 
41
- #
42
- # Default extension-level rendering options overrides.
43
- #
40
+ def _html_tree_view_render(
41
+ self,
42
+ *,
43
+ view: 'HtmlTreeView',
44
+ name: Optional[str] = None,
45
+ parent: Any = None,
46
+ root_path: Optional[KeyPath] = None,
47
+ **kwargs,
48
+ ) -> Html:
49
+ """The entrypoint of rendering the subtree represented by this extension.
44
50
 
45
- def _html_tree_view_special_keys(self) -> Sequence[str]:
46
- """Returns the special keys to display (at the immediate child level)."""
47
- return []
51
+ Args:
52
+ view: The view to render the object.
53
+ name: The name of the object.
54
+ parent: The parent of the object.
55
+ root_path: The key path of the object relative to the root.
56
+ **kwargs: kwargs to pass to `view.render()` on this extension.
48
57
 
49
- def _html_tree_view_include_keys(self) -> Optional[Sequence[str]]:
50
- """Returns the keys to include (at the immediate child level)."""
51
- return None
58
+ Returns:
59
+ The rendered HTML.
60
+ """
61
+ return self._html_tree_view(
62
+ view=view,
63
+ name=name,
64
+ parent=parent,
65
+ root_path=root_path,
66
+ **view.get_kwargs(
67
+ kwargs, self._html_tree_view_config(), root_path or KeyPath()
68
+ )
69
+ ).add_style(
70
+ *self._html_tree_view_css_styles()
71
+ )
52
72
 
53
- def _html_tree_view_exclude_keys(self) -> Sequence[str]:
54
- """Returns the keys to include (at the immediate child level)."""
73
+ #
74
+ # Users could override this methods to customize the styles and
75
+ # rendering arguments for the subtree.
76
+ #
77
+
78
+ @classmethod
79
+ def _html_tree_view_css_styles(cls) -> list[str]:
80
+ """Returns the CSS styles for the subtree."""
81
+ del cls
55
82
  return []
56
83
 
57
- def _html_tree_view_uncollapse_level(self) -> Optional[int]:
58
- """Returns the level of the subtree to uncollapse.
84
+ @classmethod
85
+ def _html_tree_view_config(cls) -> Dict[str, Any]:
86
+ """Returns the config (rendering arguments) of current extension.
59
87
 
60
88
  Returns:
61
- The level of subtree to uncollapse. If None, the subtree will be fully
62
- expanded. Please note that the uncollapsed subtree will show only when
63
- current node is uncollapsed.
89
+ A dictionary of rendering arguments for the subtree. These arguments
90
+ will override the arguments passed to `view.render()`. See the
91
+ `render()` method for the full list of arguments.
64
92
  """
65
- return 1
66
-
67
- def _html_tree_view_uncollapse(self) -> KeyPathSet:
68
- """Returns the node paths (relative to current node) to uncollapse."""
69
- return KeyPathSet()
93
+ return {}
70
94
 
71
95
  #
72
- # Default behavior overrides.
96
+ # Users could override the methods below to customize rendering
97
+ # logics.
73
98
  #
74
99
 
75
- def _html_tree_view_render(
100
+ def _html_tree_view(
76
101
  self,
77
102
  *,
78
- view: 'HtmlView',
79
- name: Optional[str],
80
- parent: Any,
81
- root_path: KeyPath,
82
- title: Union[str, Html, None] = None,
83
- special_keys: Optional[Sequence[Union[int, str]]] = None,
84
- include_keys: Optional[Iterable[Union[int, str]]] = None,
85
- exclude_keys: Optional[Iterable[Union[int, str]]] = None,
86
- collapse_level: Optional[int] = HtmlView.PresetArgValue(1),
87
- uncollapse: Union[
88
- KeyPathSet, base.NodeFilter, None
89
- ] = HtmlView.PresetArgValue(None),
90
- filter: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None), # pylint: disable=redefined-builtin
91
- highlight: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None),
92
- lowlight: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None),
93
- enable_summary: Optional[bool] = HtmlView.PresetArgValue(None),
94
- max_summary_len_for_str: int = HtmlView.PresetArgValue(40),
95
- enable_summary_tooltip: bool = HtmlView.PresetArgValue(True),
96
- enable_key_tooltip: bool = HtmlView.PresetArgValue(True),
103
+ view: 'HtmlTreeView',
104
+ name: Optional[str] = None,
105
+ parent: Any = None,
106
+ root_path: Optional[KeyPath] = None,
97
107
  **kwargs,
98
108
  ) -> Html:
99
- """Returns the topmost HTML representation of the object.
109
+ """Returns the topmost HTML representation of this extension.
100
110
 
101
111
  Args:
102
112
  view: The view to render the object.
103
113
  name: The name of the object.
104
114
  parent: The parent of the object.
105
115
  root_path: The key path of the object relative to the root.
106
- title: The title of the summary.
107
- special_keys: The special keys to display (at the immediate child
108
- level).
109
- include_keys: The keys to include (at the immediate child level).
110
- exclude_keys: The keys to exclude (at the immediate child level).
111
- collapse_level: The level to collapse the tree (relative to this node).
112
- uncollapse: A key path set (relative to root_path) for the nodes to
113
- uncollapse. or a function with signature (path, value, parent) -> bool
114
- to filter nodes to uncollapse.
115
- filter: A function with signature (path, value, parent) -> include
116
- to determine whether to include a field (at all levels).
117
- highlight: A function with signature (path, value, parent) -> bool
118
- to determine whether to highlight a field.
119
- lowlight: A function with signature (path, value, parent) -> bool
120
- to determine whether to lowlight a field.
121
- enable_summary: Whether to enable the summary. If None, summary will
122
- be enabled for complex types or when string exceeds
123
- `max_summary_len_for_str`.
124
- max_summary_len_for_str: The maximum length of the string to display.
125
- enable_summary_tooltip: Whether to enable the tooltip for the summary.
126
- enable_key_tooltip: Whether to enable the tooltip for the key.
127
- **kwargs: Additional keyword arguments passed from `pg.to_html`.
116
+ **kwargs: kwargs to pass to the view. See `_html_tree_view_config` for
117
+ the builtin arguments.
128
118
 
129
119
  Returns:
130
120
  The rendered HTML.
@@ -134,65 +124,38 @@ class HtmlTreeView(HtmlView):
134
124
  name=name,
135
125
  parent=parent,
136
126
  root_path=root_path,
137
- title=title,
138
- special_keys=special_keys,
139
- include_keys=include_keys,
140
- exclude_keys=exclude_keys,
141
- filter=filter,
142
- highlight=highlight,
143
- lowlight=lowlight,
144
- enable_summary=enable_summary,
145
- max_summary_len_for_str=max_summary_len_for_str,
146
- enable_summary_tooltip=enable_summary_tooltip,
147
- enable_key_tooltip=enable_key_tooltip,
148
- collapse_level=collapse_level,
149
- uncollapse=uncollapse,
150
- **kwargs
127
+ **kwargs,
151
128
  )
152
129
 
153
130
  def _html_tree_view_summary(
154
131
  self,
155
132
  *,
156
133
  view: 'HtmlTreeView',
157
- name: Optional[str],
158
- parent: Any,
159
- root_path: KeyPath,
160
- title: Union[str, Html, None] = None,
161
- enable_summary: Optional[bool] = HtmlView.PresetArgValue(None),
162
- max_summary_len_for_str: int = HtmlView.PresetArgValue(40),
163
- enable_summary_tooltip: bool = HtmlView.PresetArgValue(True),
134
+ name: Optional[str] = None,
135
+ parent: Any = None,
136
+ root_path: Optional[KeyPath] = None,
164
137
  **kwargs,
165
138
  ) -> Optional[Html]:
166
- """Returns the HTML representation of the object.
139
+ """Returns the HTML summary for the object.
167
140
 
168
141
  Args:
169
142
  view: The view to render the object.
170
143
  name: The name of the object.
171
144
  parent: The parent of the object.
172
145
  root_path: The key path of the object relative to the root.
173
- title: The title of the summary.
174
- enable_summary: Whether to enable the summary. If None, summary will
175
- be enabled for complex types or when string exceeds
176
- `max_summary_len_for_str`.
177
- max_summary_len_for_str: The maximum length of the string to display.
178
- enable_summary_tooltip: Whether to enable the tooltip for the summary.
179
- **kwargs: Additional keyword arguments passed from `pg.to_html`. These
180
- arguments may be handled by the user logic but not the general
181
- HtmlTreeView.
146
+ **kwargs: kwargs to pass to the view. See `_html_tree_view_config` for
147
+ the builtin arguments.
182
148
 
183
149
  Returns:
184
150
  An optional HTML object representing the summary of the object. If None,
185
- the summary will be hidden.
151
+ the content will be returned directly instead of having a <details>
152
+ container.
186
153
  """
187
154
  return view.summary(
188
155
  self,
189
156
  name=name,
190
157
  parent=parent,
191
158
  root_path=root_path,
192
- title=title,
193
- enable_summary=enable_summary,
194
- max_summary_len_for_str=max_summary_len_for_str,
195
- enable_summary_tooltip=enable_summary_tooltip,
196
159
  **kwargs,
197
160
  )
198
161
 
@@ -200,22 +163,9 @@ class HtmlTreeView(HtmlView):
200
163
  self,
201
164
  *,
202
165
  view: 'HtmlTreeView',
203
- name: Optional[str],
204
- parent: Any,
205
- root_path: KeyPath,
206
- special_keys: Optional[Sequence[Union[int, str]]] = None,
207
- include_keys: Optional[Iterable[Union[int, str]]] = None,
208
- exclude_keys: Optional[Iterable[Union[int, str]]] = None,
209
- collapse_level: Optional[int] = HtmlView.PresetArgValue(1),
210
- uncollapse: Union[
211
- KeyPathSet, base.NodeFilter, None
212
- ] = HtmlView.PresetArgValue(None),
213
- filter: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None), # pylint: disable=redefined-builtin
214
- highlight: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None),
215
- lowlight: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None),
216
- max_summary_len_for_str: int = HtmlView.PresetArgValue(40),
217
- enable_summary_tooltip: bool = HtmlView.PresetArgValue(True),
218
- enable_key_tooltip: bool = HtmlView.PresetArgValue(True),
166
+ name: Optional[str] = None,
167
+ parent: Any = None,
168
+ root_path: Optional[KeyPath] = None,
219
169
  **kwargs,
220
170
  ) -> Html:
221
171
  """Returns the main content for the object.
@@ -225,26 +175,8 @@ class HtmlTreeView(HtmlView):
225
175
  name: The name of the object.
226
176
  parent: The parent of the object.
227
177
  root_path: The key path of the object relative to the root.
228
- special_keys: The special keys to display (at the immediate child
229
- level).
230
- include_keys: The keys to include (at the immediate child level).
231
- exclude_keys: The keys to exclude (at the immediate child level).
232
- collapse_level: The level to collapse the tree (relative to this node).
233
- uncollapse: A key path set (relative to root_path) for the nodes to
234
- uncollapse. or a function with signature (path, value, parent) -> bool
235
- to filter nodes to uncollapse.
236
- filter: A function with signature (path, value, parent) -> include
237
- to determine whether to include a field (at all levels).
238
- highlight: A function with signature (path, value, parent) -> bool
239
- to determine whether to highlight a field (at all levels).
240
- lowlight: A function with signature (path, value, parent) -> bool
241
- to determine whether to lowlight a field (at all levels).
242
- max_summary_len_for_str: The maximum length of the string to display.
243
- enable_summary_tooltip: Whether to enable the tooltip for the summary.
244
- enable_key_tooltip: Whether to enable the key tooltip.
245
- **kwargs: Additional keyword arguments passed from `pg.to_html`. These
246
- arguments may be handled by the user logic but not the general
247
- HtmlTreeView.
178
+ **kwargs: kwargs to pass to the view. See `_html_tree_view_config` for
179
+ the builtin arguments.
248
180
 
249
181
  Returns:
250
182
  The rendered HTML as the main content of the object.
@@ -254,51 +186,11 @@ class HtmlTreeView(HtmlView):
254
186
  name=name,
255
187
  parent=parent,
256
188
  root_path=root_path,
257
- special_keys=special_keys,
258
- include_keys=include_keys,
259
- exclude_keys=exclude_keys,
260
- filter=filter,
261
- highlight=highlight,
262
- lowlight=lowlight,
263
- max_summary_len_for_str=max_summary_len_for_str,
264
- enable_summary_tooltip=enable_summary_tooltip,
265
- enable_key_tooltip=enable_key_tooltip,
266
- collapse_level=collapse_level,
267
- uncollapse=uncollapse,
268
- **kwargs
269
- )
270
-
271
- def _html_tree_view_tooltip(
272
- self,
273
- *,
274
- view: 'HtmlTreeView',
275
- name: Optional[str],
276
- parent: Any,
277
- root_path: KeyPath,
278
- content: Union[str, Html, None] = None,
279
- **kwargs,
280
- ) -> Optional[Html]:
281
- """Returns the tooltip for the object.
282
-
283
- Args:
284
- view: The view to render the object.
285
- name: The referenced name of the object.
286
- parent: The parent of the object.
287
- root_path: The key path of the object relative to the root.
288
- content: Custom content to display in the tooltip.
289
- **kwargs: Additional keyword arguments passed from `pg.to_html`. These
290
- arguments may be handled by the user logic but not the general
291
- HtmlTreeView.
292
-
293
- Returns:
294
- An optional HTML object representing the tooltip of the object. If None,
295
- the tooltip will be hidden.
296
- """
297
- return view.tooltip(
298
- value=self, name=name, parent=parent, root_path=root_path,
299
- content=content, **kwargs
189
+ **kwargs,
300
190
  )
301
191
 
192
+ # NOTE(daiyip): update `get_kwargs()` and `get_passthrough_kwargs()` when new
193
+ # arguments are added.
302
194
  @HtmlView.extension_method('_html_tree_view_render')
303
195
  def render(
304
196
  self,
@@ -307,22 +199,47 @@ class HtmlTreeView(HtmlView):
307
199
  name: Optional[str] = None,
308
200
  parent: Any = None,
309
201
  root_path: Optional[KeyPath] = None,
202
+ css_classes: Optional[Sequence[str]] = None,
203
+ # Summary settings.
310
204
  title: Union[str, Html, None] = None,
311
- special_keys: Optional[Sequence[Union[int, str]]] = None,
312
- include_keys: Optional[Iterable[Union[int, str]]] = None,
313
- exclude_keys: Optional[Iterable[Union[int, str]]] = None,
314
- collapse_level: Optional[int] = HtmlView.PresetArgValue(1),
315
- uncollapse: Union[
316
- KeyPathSet, base.NodeFilter, None
317
- ] = HtmlView.PresetArgValue(None),
318
- filter: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None), # pylint: disable=redefined-builtin
319
- highlight: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None),
320
- lowlight: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None),
321
- enable_summary: Optional[bool] = HtmlView.PresetArgValue(None),
322
- max_summary_len_for_str: int = HtmlView.PresetArgValue(40),
323
- enable_summary_tooltip: bool = HtmlView.PresetArgValue(True),
324
- enable_key_tooltip: bool = HtmlView.PresetArgValue(True),
325
- **kwargs
205
+ enable_summary: Optional[bool] = None,
206
+ enable_summary_for_str: bool = True,
207
+ max_summary_len_for_str: int = 80,
208
+ enable_summary_tooltip: bool = True,
209
+ summary_color: Union[
210
+ Tuple[Optional[str], Optional[str]],
211
+ Callable[[KeyPath, Any, Any], Tuple[Optional[str], Optional[str]]]
212
+ ] = None,
213
+ # Content settings.
214
+ key_style: Union[
215
+ Literal['label', 'summary'],
216
+ Callable[[KeyPath, Any, Any], Literal['label', 'summary']]
217
+ ] = 'summary',
218
+ key_color: Union[
219
+ Tuple[Optional[str], Optional[str]],
220
+ Callable[[KeyPath, Any, Any], Tuple[Optional[str], Optional[str]]]
221
+ ] = None,
222
+ include_keys: Union[
223
+ Iterable[Union[int, str]],
224
+ Callable[[KeyPath, Any, Any], Iterable[Union[int, str]]],
225
+ None
226
+ ] = None,
227
+ exclude_keys: Union[
228
+ Iterable[Union[int, str]],
229
+ Callable[[KeyPath, Any, Any], Iterable[Union[int, str]]],
230
+ None
231
+ ] = None,
232
+ enable_key_tooltip: bool = True,
233
+ # Collapse settings.
234
+ collapse_level: Optional[int] = 1,
235
+ uncollapse: Union[KeyPathSet, base.NodeFilter, None] = None,
236
+ # Extension settings.
237
+ child_config: Optional[Dict[str, Any]] = None,
238
+ extra_flags: Optional[Dict[str, Any]] = None,
239
+ # Tree operations.
240
+ highlight: Optional[base.NodeFilter] = None,
241
+ lowlight: Optional[base.NodeFilter] = None,
242
+ debug: bool = False,
326
243
  ) -> Html:
327
244
  """Renders the entire HTML tree view for the value.
328
245
 
@@ -331,102 +248,177 @@ class HtmlTreeView(HtmlView):
331
248
  name: The name of the value.
332
249
  parent: The parent of the value.
333
250
  root_path: The root path of the value.
334
- title: The title of the summary.
335
- special_keys: The special keys to display (at the immediate child level).
336
- include_keys: The keys to include (at the immediate child level).
337
- exclude_keys: The keys to exclude (at the immediate child level).
338
- collapse_level: The level to collapse the tree (relative to this node).
339
- uncollapse: A key path set (relative to root_path) for the nodes to
340
- uncollapse. or a function with signature (path, value, parent) -> bool
341
- to filter nodes to uncollapse.
342
- filter: A function with signature (path, value, parent) -> include
343
- to determine whether to include a field (at all levels).
344
- highlight: A function with signature (path, value, parent) -> bool
345
- to determine whether to highlight a field.
346
- lowlight: A function with signature (path, value, parent) -> bool
347
- to determine whether to lowlight a field.
251
+ css_classes: CSS classes to add to the top-most element.
252
+ title: The title of the summary. If None, the default title will be used,
253
+ which is the type name of the value.
348
254
  enable_summary: Whether to enable the summary. If None, summary will
349
255
  be enabled for complex types or when string exceeds
350
256
  `max_summary_len_for_str`.
257
+ enable_summary_for_str: Whether to enable the summary for strings.
351
258
  max_summary_len_for_str: The maximum length of the string to display.
352
259
  enable_summary_tooltip: Whether to enable the tooltip for the summary.
353
- enable_key_tooltip: Whether to enable the key tooltip.
354
- **kwargs: Additional keyword arguments passed from `pg.to_html`.
260
+ summary_color: The color used for the summary for displaying the referred
261
+ field name of the object. It can be a tuple of (color, background-color)
262
+ or a function that takes (root_path, value, parent) and returns the
263
+ color tuple.
264
+ key_style: The style of the key. If 'label', the key will be rendered as a
265
+ label. If 'summary', it will be rendered as a summary in the <details>
266
+ tag. If a function, it will be called with (root_path, value, parent)
267
+ and return the style.
268
+ key_color: The color for label-style keys under this extension. It can be
269
+ a tuple of (color, background-color) or a function that takes
270
+ (root_path, value, parent) and returns the color tuple.
271
+ include_keys: A list of keys to include when displaying the sub-nodes of
272
+ the object. If None, all keys will be displayed. If a function, it will
273
+ be called with (root_path, value, parent) and return whether the key
274
+ should be included.
275
+ exclude_keys: A set of keys to exclude when displaying the sub-nodes of
276
+ the object. If None, all keys will be displayed. If a function, it will
277
+ be called with (root_path, value, parent) and return whether the key
278
+ should be excluded.
279
+ enable_key_tooltip: Whether to enable the tooltip for the object name.
280
+ collapse_level: The level of collapsing. If 0, the object will be
281
+ collapsed (without showing its sub-nodes). If 1, the immediate sub-nodes
282
+ will be shown in collapsed form. If None, all sub-tree will be shown.
283
+ uncollapse: Indivdual nodes to uncollapse. It can be a KeyPathSet or a
284
+ function that takes (root_path, value, parent) and returns a KeyPathSet.
285
+ child_config: The configs for the immediate child nodes of the object
286
+ being rendered. It's a dictionary of (key, child-config) where the key
287
+ is the name of the child node and the child-config is a dictionary of
288
+ (key, value) to override the default configs for the child node.
289
+ extra_flags: A dictionary of user-defined flags to control the rendering
290
+ behavior.
291
+ highlight: A function that takes (root_path, value, parent) and returns
292
+ whether the node should be highlighted.
293
+ lowlight: A function that takes (root_path, value, parent) and returns
294
+ whether the node should be lowlighted.
295
+ debug: Whether to show debug information for this rendering.
355
296
 
356
297
  Returns:
357
298
  The rendered HTML.
358
299
  """
359
300
  root_path = root_path or KeyPath()
301
+ child_config = child_config or {}
302
+ extra_flags = extra_flags or {}
360
303
  uncollapse = self.init_uncollapse(uncollapse)
361
304
 
362
- child_collapse_level = collapse_level
363
- if isinstance(value, HtmlTreeView.Extension):
364
- subtree_uncollapse_level = value._html_tree_view_uncollapse_level() # pylint: disable=protected-access
365
-
366
- # If the extension has child levels to uncollapse, honor them above the
367
- # collapse level passed from the root. However, we can see the
368
- # uncollapsed extension subtree only when the extension's parent node is
369
- # uncollapsed.
370
- child_collapse_level = self.max_collapse_level(
371
- collapse_level, subtree_uncollapse_level, root_path
372
- )
373
- if not callable(uncollapse):
374
- extension_uncollapse = value._html_tree_view_uncollapse().copy() # pylint: disable=protected-access
375
- if extension_uncollapse:
376
- extension_uncollapse.rebase(root_path)
377
- uncollapse = uncollapse.union(extension_uncollapse)
378
-
379
305
  summary = self.summary(
380
306
  value,
381
307
  name=name,
382
308
  parent=parent,
383
309
  root_path=root_path,
310
+ css_classes=css_classes,
384
311
  title=title,
312
+ summary_color=summary_color,
385
313
  enable_summary=enable_summary,
314
+ enable_summary_for_str=enable_summary_for_str,
386
315
  enable_summary_tooltip=enable_summary_tooltip,
316
+ enable_key_tooltip=enable_key_tooltip,
387
317
  max_summary_len_for_str=max_summary_len_for_str,
388
- **kwargs,
318
+ extra_flags=extra_flags,
389
319
  )
320
+
321
+ if debug:
322
+ debug_info = Html.element(
323
+ 'div',
324
+ [
325
+ Html.element(
326
+ 'span', ['DEBUG'], css_classes=['debug-info-trigger']
327
+ ),
328
+ self.tooltip(
329
+ dict(
330
+ # Most error-prone settings.
331
+ css_classes=css_classes,
332
+ collapse_level=collapse_level,
333
+ uncollapse=uncollapse,
334
+ extra_flags=extra_flags,
335
+ child_config=child_config,
336
+ # Relative obvious settings.
337
+ key_style=key_style,
338
+ key_color=key_color,
339
+ include_keys=include_keys,
340
+ exclude_keys=exclude_keys,
341
+ # More obvious settings.
342
+ summary_color=summary_color,
343
+ enable_summary=enable_summary,
344
+ enable_summary_for_str=enable_summary_for_str,
345
+ max_summary_len_for_str=max_summary_len_for_str,
346
+ enable_summary_tooltip=enable_summary_tooltip,
347
+ enable_key_tooltip=enable_key_tooltip,
348
+ ),
349
+ name='debug_info',
350
+ parent=parent,
351
+ root_path=root_path,
352
+ css_classes=['debug-info'],
353
+ ),
354
+ ],
355
+ ).add_style(
356
+ """
357
+ .debug-info-trigger {
358
+ display: inline-flex;
359
+ cursor: pointer;
360
+ font-size: 0.6em;
361
+ background-color: red;
362
+ color: white;
363
+ padding: 5px;
364
+ border-radius: 3px;
365
+ margin: 5px 0 5px 0;
366
+ }
367
+ .debug-info-trigger:hover + span.tooltip {
368
+ visibility: visible;
369
+ }
370
+ """
371
+ )
372
+ else:
373
+ debug_info = None
374
+
390
375
  content = self.content(
391
376
  value,
392
377
  name=name,
393
378
  parent=parent,
394
379
  root_path=root_path,
395
- filter=filter,
396
- special_keys=special_keys,
397
- include_keys=include_keys,
398
- exclude_keys=exclude_keys,
399
- collapse_level=child_collapse_level,
400
- uncollapse=uncollapse,
380
+ css_classes=css_classes if summary is None else None,
381
+ # Summary settings (child nodes).
382
+ enable_summary=enable_summary,
383
+ enable_summary_for_str=enable_summary_for_str,
401
384
  max_summary_len_for_str=max_summary_len_for_str,
402
385
  enable_summary_tooltip=enable_summary_tooltip,
386
+ # Content settings.
387
+ key_style=key_style,
388
+ key_color=key_color,
403
389
  enable_key_tooltip=enable_key_tooltip,
404
- **kwargs,
405
- )
406
- extension_style = (
407
- value._html_style() if isinstance(value, HtmlView.Extension) else [] # pylint: disable=protected-access
390
+ include_keys=include_keys,
391
+ exclude_keys=exclude_keys,
392
+ collapse_level=collapse_level,
393
+ uncollapse=uncollapse,
394
+ highlight=highlight,
395
+ lowlight=lowlight,
396
+ child_config=child_config,
397
+ extra_flags=extra_flags,
398
+ debug=debug,
408
399
  )
409
400
 
410
401
  if summary is None:
411
402
  content = Html.from_value(content)
412
403
  assert content is not None
413
- content.add_style(*extension_style)
414
- return content
404
+ return debug_info + content
415
405
 
416
- collapse_view = self.should_collapse(
417
- value, parent=parent, root_path=root_path,
406
+ collapse_details = self.should_collapse(
407
+ value, name=name, parent=parent, root_path=root_path,
418
408
  collapse_level=collapse_level, uncollapse=uncollapse,
419
409
  )
420
410
  return Html.element(
421
411
  'details',
422
412
  [
423
413
  summary,
414
+ debug_info,
424
415
  content,
425
416
  ],
426
- options=[None if collapse_view else 'open'],
427
- css_class=[
417
+ options=[None if collapse_details else 'open'],
418
+ css_classes=[
428
419
  'pyglove',
429
420
  self.css_class_name(value),
421
+ css_classes,
430
422
  ],
431
423
  ).add_style(
432
424
  """
@@ -435,10 +427,7 @@ class HtmlTreeView(HtmlView):
435
427
  border: 1px solid #aaa;
436
428
  border-radius: 4px;
437
429
  padding: 0.5em 0.5em 0;
438
- margin: 0.1em 0;
439
- }
440
- details.pyglove.special_value {
441
- margin-bottom: 0.75em;
430
+ margin: 0.25em 0;
442
431
  }
443
432
  details.pyglove[open] {
444
433
  padding: 0.5em 0.5em 0.5em;
@@ -450,38 +439,44 @@ class HtmlTreeView(HtmlView):
450
439
  opacity: 0.2;
451
440
  }
452
441
  """,
453
- *extension_style,
454
442
  )
455
443
 
456
- def init_uncollapse(
457
- self,
458
- uncollapse: Union[
459
- Iterable[Union[KeyPath, str]], base.NodeFilter, None
460
- ] = HtmlView.PresetArgValue(None),
461
- ) -> Union[KeyPathSet, base.NodeFilter]:
462
- """Normalize the uncollapse argument."""
463
- if uncollapse is None:
464
- return KeyPathSet()
465
- elif callable(uncollapse):
466
- return uncollapse
467
- else:
468
- return KeyPathSet.from_value(uncollapse, include_intermediate=True)
469
-
470
444
  def should_collapse(
471
445
  self,
472
446
  value: Any,
447
+ name: Optional[str],
473
448
  root_path: KeyPath,
474
449
  parent: Any,
475
- collapse_level: Optional[int] = 0,
450
+ collapse_level: Optional[int] = 1,
476
451
  uncollapse: Union[KeyPathSet, base.NodeFilter] = None,
477
452
  ) -> bool:
478
- """Returns whether the object should be collapsed."""
479
- if collapse_level is None or root_path.depth < collapse_level:
453
+ """Returns True if the object should be collapsed.
454
+
455
+ Args:
456
+ value: The value to render.
457
+ name: The referred field name of the value.
458
+ root_path: The root path of the value.
459
+ parent: The parent of the value.
460
+ collapse_level: The level of collapsing. If 0, the object will be
461
+ collapsed (without showing its sub-nodes). If 1, the immediate sub-nodes
462
+ will be shown in collapsed form. If None, all sub-tree will be shown.
463
+ uncollapse: Indivdual nodes to uncollapse. It can be a KeyPathSet or a
464
+ function that takes (root_path, value, parent) and returns a KeyPathSet.
465
+
466
+ Returns:
467
+ True if the object should be collapsed.
468
+ """
469
+ if collapse_level is None or collapse_level > 0:
480
470
  return False
481
471
  if callable(uncollapse):
482
472
  return not uncollapse(root_path, value, parent)
483
- else:
484
- return root_path not in uncollapse
473
+ if root_path in uncollapse:
474
+ return False
475
+ # Always uncollapse simple types.
476
+ if (name is not None
477
+ and isinstance(value, (bool, int, float, str, type(None)))):
478
+ return False
479
+ return True
485
480
 
486
481
  def needs_summary(
487
482
  self,
@@ -490,21 +485,38 @@ class HtmlTreeView(HtmlView):
490
485
  name: Optional[str] = None,
491
486
  parent: Any = None,
492
487
  title: Union[str, Html, None] = None,
493
- enable_summary: Optional[bool] = HtmlView.PresetArgValue(True),
494
- max_summary_len_for_str: int = HtmlView.PresetArgValue(40),
488
+ enable_summary: Optional[bool] = None,
489
+ enable_summary_for_str: bool = True,
490
+ max_summary_len_for_str: int = 80,
495
491
  ) -> bool:
496
- """Returns whether the object needs a summary."""
492
+ """Returns True if the object needs a summary.
493
+
494
+ Args:
495
+ value: The value to render.
496
+ name: The referred field name of the value.
497
+ parent: The parent of the value.
498
+ title: The title of the summary.
499
+ enable_summary: Whether to enable the summary. If None, summary will
500
+ be enabled for complex types or when string exceeds
501
+ `max_summary_len_for_str`.
502
+ enable_summary_for_str: Whether to enable the summary for strings.
503
+ max_summary_len_for_str: The maximum length of the string to display.
504
+
505
+ Returns:
506
+ True if the object needs a summary.
507
+ """
497
508
  del parent
498
- if enable_summary is None:
499
- if name is not None or title is not None or not (
500
- isinstance(value, (int, float, bool, type(None)))
501
- or (
502
- isinstance(value, str)
503
- and len(value) <= max_summary_len_for_str
504
- )
505
- ):
506
- return True
507
- return enable_summary
509
+ if isinstance(enable_summary, bool):
510
+ return enable_summary
511
+ assert enable_summary is None
512
+ if not enable_summary_for_str and isinstance(value, str):
513
+ return False
514
+ if name is None and title is None and (
515
+ isinstance(value, (int, float, bool, type(None)))
516
+ or (isinstance(value, str) and len(value) <= max_summary_len_for_str)
517
+ ):
518
+ return False
519
+ return True
508
520
 
509
521
  @HtmlView.extension_method('_html_tree_view_summary')
510
522
  def summary(
@@ -514,31 +526,52 @@ class HtmlTreeView(HtmlView):
514
526
  name: Optional[str] = None,
515
527
  parent: Any = None,
516
528
  root_path: Optional[KeyPath] = None,
529
+ css_classes: Optional[Sequence[str]] = None,
517
530
  title: Union[str, Html, None] = None,
518
- enable_summary: Optional[bool] = HtmlView.PresetArgValue(None),
519
- enable_summary_tooltip: bool = HtmlView.PresetArgValue(True),
520
- max_summary_len_for_str: int = HtmlView.PresetArgValue(40),
521
- **kwargs
531
+ enable_summary: Optional[bool] = None,
532
+ enable_summary_tooltip: bool = True,
533
+ summary_color: Union[
534
+ Tuple[Optional[str], Optional[str]],
535
+ Callable[[KeyPath, Any, Any], Tuple[Optional[str], Optional[str]]]
536
+ ] = None,
537
+ max_summary_len_for_str: int = 80,
538
+ enable_summary_for_str: bool = True,
539
+ enable_key_tooltip: bool = True,
540
+ summary_tooltip_fn: Optional[Callable[..., Html]] = None,
541
+ key_tooltip_fn: Optional[Callable[..., Html]] = None,
542
+ extra_flags: Optional[Dict[str, Any]] = None,
522
543
  ) -> Optional[Html]:
523
- """Renders a summary for the value.
544
+ """Renders the summary for an input value.
524
545
 
525
546
  Args:
526
547
  value: The value to render.
527
- name: The name of the value.
548
+ name: The referred field name of the value.
528
549
  parent: The parent of the value.
529
550
  root_path: The root path of the value.
551
+ css_classes: The CSS classes to add to the HTML element.
530
552
  title: The title of the summary.
531
553
  enable_summary: Whether to enable the summary. If None, summary will
532
554
  be enabled for complex types or when string exceeds
533
555
  `max_summary_len_for_str`.
534
- enable_summary_tooltip: Whether to enable the tooltip for the summary.
556
+ enable_summary_tooltip: Whether to enable the summary tooltip.
557
+ summary_color: The color of the summary. If None, the summary will be
558
+ rendered without a color. If a tuple, the first element is the text
559
+ color and the second element is the background color. If a function,
560
+ the function takes (root_path, value, parent) and returns a tuple of
561
+ (text_color, background_color).
535
562
  max_summary_len_for_str: The maximum length of the string to display.
536
- **kwargs: Additional keyword arguments passed from `pg.to_html`.
563
+ enable_summary_for_str: Whether to enable the summary for strings.
564
+ enable_key_tooltip: Whether to enable the key tooltip.
565
+ summary_tooltip_fn: The function to render the summary tooltip.
566
+ key_tooltip_fn: The function to render the key tooltip.
567
+ extra_flags: The extra flags to pass to the summary.
537
568
 
538
569
  Returns:
539
570
  An optional HTML object representing the summary of the value. If None,
540
- the summary will be hidden.
571
+ the summary will not be rendered.
541
572
  """
573
+ del extra_flags
574
+ root_path = root_path or KeyPath()
542
575
  if not self.needs_summary(
543
576
  value,
544
577
  name=name,
@@ -546,16 +579,27 @@ class HtmlTreeView(HtmlView):
546
579
  title=title,
547
580
  max_summary_len_for_str=max_summary_len_for_str,
548
581
  enable_summary=enable_summary,
582
+ enable_summary_for_str=enable_summary_for_str,
549
583
  ):
550
584
  return None
551
585
 
586
+ key_tooltip_fn = key_tooltip_fn or self.tooltip
587
+ summary_tooltip_fn = summary_tooltip_fn or self.tooltip
588
+
552
589
  def make_title(value: Any):
553
- if isinstance(value, str):
554
- if len(value) > max_summary_len_for_str:
555
- value = value[:max_summary_len_for_str] + '...'
556
- return Html.escape(repr(value))
590
+ if inspect.isclass(value):
591
+ return 'type'
592
+ elif isinstance(value, (int, float, bool, str)):
593
+ return type(value).__name__
557
594
  return f'{type(value).__name__}(...)'
558
595
 
596
+ if name is not None:
597
+ summary_color = self.get_color(
598
+ summary_color, root_path + name, value, parent
599
+ )
600
+ else:
601
+ summary_color = (None, None)
602
+
559
603
  return Html.element(
560
604
  'summary',
561
605
  [
@@ -564,8 +608,19 @@ class HtmlTreeView(HtmlView):
564
608
  'div',
565
609
  [
566
610
  name,
611
+ key_tooltip_fn( # pylint: disable=g-long-ternary
612
+ root_path,
613
+ name=name,
614
+ parent=parent,
615
+ root_path=root_path,
616
+ css_classes=css_classes,
617
+ ) if enable_key_tooltip else None,
567
618
  ],
568
- css_class=['summary_name']
619
+ css_classes=['summary-name', css_classes],
620
+ styles=dict(
621
+ color=summary_color[0],
622
+ background_color=summary_color[1],
623
+ ),
569
624
  ) if name else None,
570
625
 
571
626
  # Summary title
@@ -574,16 +629,15 @@ class HtmlTreeView(HtmlView):
574
629
  [
575
630
  title or make_title(value),
576
631
  ],
577
- css_class=['summary_title']
632
+ css_classes=['summary-title', css_classes],
578
633
  ),
579
634
 
580
- # Tooltip.
581
- lambda: self.tooltip( # pylint: disable=g-long-ternary
635
+ # Summary tooltip.
636
+ lambda: summary_tooltip_fn( # pylint: disable=g-long-ternary
582
637
  value,
583
- name=name,
584
638
  parent=parent,
585
639
  root_path=root_path,
586
- **kwargs,
640
+ css_classes=css_classes,
587
641
  ) if enable_summary_tooltip else None,
588
642
  ],
589
643
  ).add_style(
@@ -594,24 +648,25 @@ class HtmlTreeView(HtmlView):
594
648
  margin: -0.5em -0.5em 0;
595
649
  padding: 0.5em;
596
650
  }
597
- .summary_name {
651
+ .summary-name {
598
652
  display: inline;
599
- padding: 0 5px;
653
+ padding: 3px 5px 3px 5px;
654
+ margin: 0 5px;
655
+ border-radius: 3px;
600
656
  }
601
- .summary_title {
657
+ .summary-title {
602
658
  display: inline;
603
659
  }
604
- .summary_name + div.summary_title {
660
+ .summary-name + div.summary-title {
605
661
  display: inline;
606
662
  color: #aaa;
607
663
  }
608
- .summary_title:hover + span.tooltip {
664
+ .summary-title:hover + span.tooltip {
609
665
  visibility: visible;
610
666
  }
611
- /* Type-specific styles. */
612
- .pyglove.str .summary_title {
613
- color: darkred;
614
- font-style: italic;
667
+ .summary-name:hover > span.tooltip {
668
+ visibility: visible;
669
+ background-color: darkblue;
615
670
  }
616
671
  """
617
672
  )
@@ -621,80 +676,90 @@ class HtmlTreeView(HtmlView):
621
676
  # delegated to `HtmlTreeView.Extension`.
622
677
  def object_key(
623
678
  self,
624
- key: Union[str, int],
679
+ root_path: KeyPath,
625
680
  *,
626
- name: Optional[str] = None,
681
+ value: Any,
627
682
  parent: Any,
628
- root_path: Optional[KeyPath] = None,
629
- css_class: Union[str, Sequence[str], None] = None,
630
- key_color: Optional[str] = None,
631
- enable_tooltip: bool = HtmlView.PresetArgValue(True),
632
- **kwargs
683
+ css_classes: Optional[Sequence[str]] = None,
684
+ key_color: Union[
685
+ Tuple[Optional[str], Optional[str]],
686
+ Callable[[KeyPath, Any, Any], Tuple[Optional[str], Optional[str]]]
687
+ ] = None,
688
+ enable_key_tooltip: bool = True,
689
+ key_tooltip_fn: Optional[Callable[..., Html]] = None,
690
+ **kwargs,
633
691
  ) -> Html:
634
- """Renders the key for the value.
692
+ """Renders a label-style key for the value.
635
693
 
636
694
  Args:
637
- key: The key of the value.
638
- name: The name of the value.
639
- parent: The parent value of the key.
640
695
  root_path: The root path of the value.
641
- css_class: Additional CSS classes to add to the HTML element.
642
- key_color: The color of the key.
643
- enable_tooltip: Whether to enable the tooltip.
644
- **kwargs: Additional keyword arguments passed from `pg.to_html`.
696
+ value: The value to render.
697
+ parent: The parent of the value.
698
+ css_classes: The CSS classes to add to the HTML element.
699
+ key_color: The color of the key. If None, the key will be rendered
700
+ without a color. If a tuple, the first element is the text color and
701
+ the second element is the background color. If a function, the function
702
+ takes (root_path, value, parent) and returns a tuple of (text_color,
703
+ background_color).
704
+ enable_key_tooltip: Whether to enable the tooltip.
705
+ key_tooltip_fn: The function to render the key tooltip.
706
+ **kwargs: Additional arguments passed by the user that will be ignored.
645
707
 
646
708
  Returns:
647
709
  The rendered HTML as the key of the value.
648
710
  """
711
+ del kwargs
712
+ key_tooltip_fn = key_tooltip_fn or self.tooltip
713
+ key_color = self.get_color(key_color, root_path, value, parent)
649
714
  return (
650
715
  # Key span.
651
716
  Html.element(
652
717
  'span',
653
718
  [
654
- str(key),
719
+ str(root_path.key),
655
720
  ],
656
- css_class=[
657
- 'object_key',
658
- type(key).__name__,
659
- css_class,
721
+ css_classes=[
722
+ 'object-key',
723
+ type(root_path.key).__name__,
724
+ css_classes,
660
725
  ],
661
- style=dict(
662
- color=key_color,
726
+ styles=dict(
727
+ color=key_color[0],
728
+ background_color=key_color[1],
663
729
  )
664
730
  ) + (
665
731
  # Tooltip if enabled.
666
- lambda: self.tooltip( # pylint: disable=g-long-ternary
732
+ lambda: key_tooltip_fn( # pylint: disable=g-long-ternary
667
733
  value=root_path,
668
734
  root_path=root_path,
669
- name=name,
670
735
  parent=parent,
671
- **kwargs
672
- ) if enable_tooltip else None
736
+ ) if enable_key_tooltip else None
673
737
  )
674
738
  ).add_style(
675
739
  """
676
740
  /* Object key styles. */
677
- .object_key {
678
- margin-right: 0.25em;
741
+ .object-key {
742
+ margin: 0.15em 0.3em 0.15em 0;
743
+ display: block;
679
744
  }
680
- .object_key:hover + .tooltip {
745
+ .object-key:hover + .tooltip {
681
746
  visibility: visible;
682
747
  background-color: darkblue;
683
748
  }
684
- .object_key.str {
749
+ .object-key.str {
685
750
  color: gray;
686
751
  border: 1px solid lightgray;
687
752
  background-color: ButtonFace;
688
753
  border-radius: 0.2em;
689
754
  padding: 0.3em;
690
755
  }
691
- .object_key.int::before{
756
+ .object-key.int::before{
692
757
  content: '[';
693
758
  }
694
- .object_key.int::after{
759
+ .object-key.int::after{
695
760
  content: ']';
696
761
  }
697
- .object_key.int{
762
+ .object-key.int{
698
763
  border: 0;
699
764
  color: lightgray;
700
765
  background-color: transparent;
@@ -712,20 +777,41 @@ class HtmlTreeView(HtmlView):
712
777
  name: Optional[str] = None,
713
778
  parent: Any = None,
714
779
  root_path: Optional[KeyPath] = None,
715
- special_keys: Optional[Sequence[Union[int, str]]] = None,
716
- include_keys: Optional[Iterable[Union[int, str]]] = None,
717
- exclude_keys: Optional[Iterable[Union[int, str]]] = None,
718
- collapse_level: Optional[int] = HtmlView.PresetArgValue(1),
719
- uncollapse: Union[
720
- KeyPathSet, base.NodeFilter, None
721
- ] = HtmlView.PresetArgValue(None),
722
- filter: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None), # pylint: disable=redefined-builtin
723
- highlight: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None),
724
- lowlight: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None),
725
- max_summary_len_for_str: int = HtmlView.PresetArgValue(40),
726
- enable_summary_tooltip: bool = HtmlView.PresetArgValue(True),
727
- enable_key_tooltip: bool = HtmlView.PresetArgValue(True),
728
- **kwargs
780
+ css_classes: Optional[Sequence[str]] = None,
781
+ # Summary settings (for child nodes).
782
+ enable_summary: Optional[bool] = None,
783
+ enable_summary_for_str: bool = True,
784
+ max_summary_len_for_str: int = 80,
785
+ enable_summary_tooltip: bool = True,
786
+ # Content settings.
787
+ key_style: Union[
788
+ Literal['label', 'summary'],
789
+ Callable[[KeyPath, Any, Any], Literal['label', 'summary']]
790
+ ] = 'summary',
791
+ key_color: Union[
792
+ Tuple[Optional[str], Optional[str]],
793
+ Callable[[KeyPath, Any, Any], Tuple[Optional[str], Optional[str]]]
794
+ ] = None,
795
+ include_keys: Union[
796
+ Iterable[Union[int, str]],
797
+ Callable[[KeyPath, Any, Any], Iterable[Union[int, str]]],
798
+ None
799
+ ] = None,
800
+ exclude_keys: Union[
801
+ Iterable[Union[int, str]],
802
+ Callable[[KeyPath, Any, Any], Iterable[Union[int, str]]],
803
+ None
804
+ ] = None,
805
+ enable_key_tooltip: bool = True,
806
+ # Collapse settings.
807
+ collapse_level: Optional[int] = 1,
808
+ uncollapse: Union[KeyPathSet, base.NodeFilter, None] = None,
809
+ # Other settings.
810
+ highlight: Optional[base.NodeFilter] = None,
811
+ lowlight: Optional[base.NodeFilter] = None,
812
+ child_config: Optional[Dict[str, Any]] = None,
813
+ extra_flags: Optional[Dict[str, Any]] = None,
814
+ debug: bool = False,
729
815
  ) -> Html:
730
816
  """Renders the main content for the value.
731
817
 
@@ -734,27 +820,42 @@ class HtmlTreeView(HtmlView):
734
820
  name: The name of the value.
735
821
  parent: The parent of the value.
736
822
  root_path: The root path of the value.
737
- special_keys: The special keys to display (at the immediate child level).
738
- include_keys: The keys to include (at the immediate child level).
739
- exclude_keys: The keys to exclude (at the immediate child level).
740
- collapse_level: The level to collapse the tree (relative to this node).
823
+ css_classes: CSS classes to add to the HTML element.
824
+ enable_summary: Whether to enable the summary.
825
+ enable_summary_for_str: Whether to enable the summary for string.
826
+ max_summary_len_for_str: The maximum length of the string to display.
827
+ enable_summary_tooltip: Whether to enable the summary tooltip.
828
+ key_style: The style of the key. It can be either 'label' or 'summary'.
829
+ If it is a function, the function takes (root_path, value, parent) and
830
+ returns either 'label' or 'summary'.
831
+ key_color: The color of the key. If it is a tuple, the first element is
832
+ the text color and the second element is the background color. If it is
833
+ a function, the function takes (root_path, value, parent) and returns
834
+ a tuple of (text_color, background_color).
835
+ include_keys: The keys to include (at the immediate child level). If it is
836
+ a function, the function takes (root_path, value, parent) and returns
837
+ an iterable of keys to include.
838
+ exclude_keys: The keys to exclude (at the immediate child level). If it is
839
+ a function, the function takes (root_path, value, parent) and returns
840
+ an iterable of keys to exclude.
841
+ enable_key_tooltip: Whether to enable the key tooltip.
842
+ collapse_level: The level to collapse the tree.
741
843
  uncollapse: A key path set (relative to root_path) for the nodes to
742
844
  uncollapse. or a function with signature (path, value, parent) -> bool
743
845
  to filter nodes to uncollapse.
744
- filter: A function with signature (path, value, parent) -> include
745
- to determine whether to include a field (at all levels).
746
846
  highlight: A function with signature (path, value, parent) -> bool
747
- to determine whether to highlight a field (at all levels).
847
+ to determine whether to highlight.
748
848
  lowlight: A function with signature (path, value, parent) -> bool
749
- to determine whether to lowlight a field (at all levels).
750
- max_summary_len_for_str: The maximum length of the string to display.
751
- enable_summary_tooltip: Whether to enable the summary tooltip.
752
- enable_key_tooltip: Whether to enable the key tooltip.
753
- **kwargs: Additional keyword arguments passed from `pg.to_html`.
849
+ to determine whether to lowlight.
850
+ child_config: The configuration for rendering the child nodes.
851
+ extra_flags: Extra flags to pass to the child render.
852
+ debug: Whether to enable debug mode.
754
853
 
755
854
  Returns:
756
855
  The rendered HTML as the main content of the value.
757
856
  """
857
+ root_path = root_path or KeyPath()
858
+
758
859
  if isinstance(value, (tuple, list)):
759
860
  items = {i: v for i, v in enumerate(value)}
760
861
  elif isinstance(value, dict):
@@ -762,25 +863,31 @@ class HtmlTreeView(HtmlView):
762
863
  else:
763
864
  return self.simple_value(
764
865
  value, name=name, parent=parent, root_path=root_path,
866
+ css_classes=css_classes,
765
867
  max_summary_len_for_str=max_summary_len_for_str
766
868
  )
767
869
  return self.complex_value(
768
870
  items,
769
871
  name=name,
770
872
  parent=value,
771
- root_path=root_path or KeyPath(),
772
- special_keys=special_keys,
873
+ root_path=root_path,
874
+ css_classes=css_classes,
875
+ enable_summary=enable_summary,
876
+ enable_summary_for_str=enable_summary_for_str,
877
+ max_summary_len_for_str=max_summary_len_for_str,
878
+ enable_summary_tooltip=enable_summary_tooltip,
879
+ key_style=key_style,
880
+ key_color=key_color,
881
+ enable_key_tooltip=enable_key_tooltip,
773
882
  include_keys=include_keys,
774
883
  exclude_keys=exclude_keys,
775
884
  collapse_level=collapse_level,
776
885
  uncollapse=uncollapse,
777
- filter=filter,
886
+ child_config=child_config,
778
887
  highlight=highlight,
779
888
  lowlight=lowlight,
780
- max_summary_len_for_str=max_summary_len_for_str,
781
- enable_summary_tooltip=enable_summary_tooltip,
782
- enable_key_tooltip=enable_key_tooltip,
783
- **kwargs,
889
+ extra_flags=extra_flags,
890
+ debug=debug,
784
891
  )
785
892
 
786
893
  def simple_value(
@@ -790,8 +897,8 @@ class HtmlTreeView(HtmlView):
790
897
  name: Optional[str] = None,
791
898
  parent: Any = None,
792
899
  root_path: Optional[KeyPath] = None,
793
- css_class: Union[str, Sequence[str], None] = None,
794
- max_summary_len_for_str: int = HtmlView.PresetArgValue(40),
900
+ css_classes: Optional[Sequence[str]] = None,
901
+ max_summary_len_for_str: int = 80,
795
902
  ) -> Html:
796
903
  """Renders a simple value.
797
904
 
@@ -800,7 +907,7 @@ class HtmlTreeView(HtmlView):
800
907
  name: The name of the value.
801
908
  parent: The parent of the value.
802
909
  root_path: The root path of the value.
803
- css_class: Additional CSS classes to add to the HTML element.
910
+ css_classes: CSS classes to add to the HTML element.
804
911
  max_summary_len_for_str: The maximum length of the string to display.
805
912
 
806
913
  Returns:
@@ -824,26 +931,26 @@ class HtmlTreeView(HtmlView):
824
931
  [
825
932
  Html.escape(value_repr),
826
933
  ],
827
- css_class=[
828
- 'simple_value',
934
+ css_classes=[
935
+ 'simple-value',
829
936
  self.css_class_name(value),
830
- css_class,
937
+ css_classes,
831
938
  ],
832
939
  ).add_style(
833
940
  """
834
941
  /* Simple value styles. */
835
- .simple_value {
942
+ .simple-value {
836
943
  color: blue;
837
944
  display: inline-block;
838
945
  white-space: pre-wrap;
839
946
  padding: 0.2em;
840
947
  margin-top: 0.15em;
841
948
  }
842
- .simple_value.str {
949
+ .simple-value.str {
843
950
  color: darkred;
844
951
  font-style: italic;
845
952
  }
846
- .simple_value.int, .simple_value.float {
953
+ .simple-value.int, .simple-value.float {
847
954
  color: darkblue;
848
955
  }
849
956
  """
@@ -856,55 +963,86 @@ class HtmlTreeView(HtmlView):
856
963
  parent: Any,
857
964
  root_path: KeyPath,
858
965
  name: Optional[str] = None,
859
- css_class: Union[str, Sequence[str], None] = None,
966
+ css_classes: Optional[Sequence[str]] = None,
967
+ # Summary settings (for child nodes).
968
+ enable_summary: Optional[bool] = None,
969
+ enable_summary_for_str: bool = True,
970
+ max_summary_len_for_str: int = 80,
971
+ enable_summary_tooltip: bool = True,
972
+ # Content settings.
973
+ key_style: Union[
974
+ Literal['label', 'summary'],
975
+ Callable[..., Literal['label', 'summary']]
976
+ ] = 'summary',
977
+ key_color: Union[
978
+ Tuple[Optional[str], Optional[str]],
979
+ Callable[[KeyPath, Any, Any], Tuple[Optional[str], Optional[str]]]
980
+ ] = None,
981
+ include_keys: Union[
982
+ Iterable[Union[int, str]],
983
+ Callable[[KeyPath, Any, Any], Iterable[Union[int, str]]],
984
+ None
985
+ ] = None,
986
+ exclude_keys: Union[
987
+ Iterable[Union[int, str]],
988
+ Callable[[KeyPath, Any, Any], Iterable[Union[int, str]]],
989
+ None
990
+ ] = None,
991
+ enable_key_tooltip: bool = True,
992
+ # Collapse settings.
993
+ collapse_level: Optional[int] = 1,
994
+ uncollapse: Union[KeyPathSet, base.NodeFilter, None] = None,
995
+ # Other settings.
996
+ child_config: Optional[Dict[str, Any]] = None,
997
+ highlight: Optional[base.NodeFilter] = None,
998
+ lowlight: Optional[base.NodeFilter] = None,
999
+ # Custom render functions.
860
1000
  render_key_fn: Optional[Callable[..., Html]] = None,
861
1001
  render_value_fn: Optional[Callable[..., Html]] = None,
862
- special_keys: Optional[Sequence[Union[int, str]]] = None,
863
- include_keys: Optional[Iterable[Union[int, str]]] = None,
864
- exclude_keys: Optional[Iterable[Union[int, str]]] = None,
865
- collapse_level: Optional[int] = HtmlView.PresetArgValue(1),
866
- uncollapse: Union[
867
- KeyPathSet, base.NodeFilter, None
868
- ] = HtmlView.PresetArgValue(None),
869
- filter: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None), # pylint: disable=redefined-builtin
870
- highlight: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None),
871
- lowlight: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None),
872
- max_summary_len_for_str: int = HtmlView.PresetArgValue(40),
873
- enable_summary_tooltip: bool = HtmlView.PresetArgValue(True),
874
- enable_key_tooltip: bool = HtmlView.PresetArgValue(True),
875
- **kwargs,
1002
+ extra_flags: Optional[Dict[str, Any]] = None,
1003
+ debug: bool = False,
876
1004
  ) -> Html:
877
1005
  """Renders a list of key-value pairs.
878
1006
 
879
1007
  Args:
880
1008
  kv: The key-value pairs to render.
881
- parent: The parent value of the key-value pairs.
1009
+ parent: The parent of the value.
882
1010
  root_path: The root path of the value.
883
1011
  name: The name of the value.
884
- css_class: Additional CSS classes to add to the HTML element.
885
- render_key_fn: A function to render the key. The function has the
886
- same signature as `HtmlTreeView.object_key`.
887
- If None, `HtmlTreeView.object_key` will be used to render the key.
888
- render_value_fn: A function to render the value. The function has the
889
- same signature as `HtmlTreeView.render`.
890
- If None, `HtmlTreeView.render` will be used to render child value.
891
- special_keys: The special keys to display (at the immediate child level).
892
- include_keys: The keys to include (at the immediate child level).
893
- exclude_keys: The keys to exclude (at the immediate child level).
894
- collapse_level: The level to collapse the tree (relative to this node).
1012
+ css_classes: CSS classes to add to the HTML element.
1013
+ enable_summary: Whether to enable the summary. If None, the default is
1014
+ to enable the summary for non-string and disable the summary for
1015
+ string.
1016
+ enable_summary_for_str: Whether to enable the summary for string.
1017
+ max_summary_len_for_str: The maximum length of the string to display.
1018
+ enable_summary_tooltip: Whether to enable the summary tooltip.
1019
+ key_style: The style of the key. It can be either 'label' or 'summary'.
1020
+ If it is a function, the function takes (root_path, value, parent) and
1021
+ returns either 'label' or 'summary'.
1022
+ key_color: The color of the key. If it is a tuple, the first element is
1023
+ the text color and the second element is the background color. If it is
1024
+ a function, the function takes (root_path, value, parent) and returns
1025
+ a tuple of (text_color, background_color).
1026
+ include_keys: The keys to include (at the immediate child level). If it is
1027
+ a function, the function takes (root_path, value, parent) and returns
1028
+ an iterable of keys to include.
1029
+ exclude_keys: The keys to exclude (at the immediate child level). If it is
1030
+ a function, the function takes (root_path, value, parent) and returns
1031
+ an iterable of keys to exclude.
1032
+ enable_key_tooltip: Whether to enable the key tooltip.
1033
+ collapse_level: The level to collapse the tree.
895
1034
  uncollapse: A key path set (relative to root_path) for the nodes to
896
1035
  uncollapse. or a function with signature (path, value, parent) -> bool
897
1036
  to filter nodes to uncollapse.
898
- filter: A function with signature (path, value, parent) -> include
899
- to determine whether to include.
1037
+ child_config: The configuration for rendering the child nodes.
900
1038
  highlight: A function with signature (path, value, parent) -> bool
901
1039
  to determine whether to highlight.
902
1040
  lowlight: A function with signature (path, value, parent) -> bool
903
1041
  to determine whether to lowlight.
904
- max_summary_len_for_str: The maximum length of the string to display.
905
- enable_summary_tooltip: Whether to enable the summary tooltip.
906
- enable_key_tooltip: Whether to enable the key tooltip.
907
- **kwargs: Additional keyword arguments passed from `pg.to_html`.
1042
+ render_key_fn: A custom function to render the label-style key.
1043
+ render_value_fn: A custom function to render the child value.
1044
+ extra_flags: Extra flags to pass to the child render.
1045
+ debug: Whether to enable debug mode.
908
1046
 
909
1047
  Returns:
910
1048
  The rendered HTML as the key-value pairs.
@@ -912,142 +1050,156 @@ class HtmlTreeView(HtmlView):
912
1050
  del name
913
1051
  root_path = root_path or KeyPath()
914
1052
  uncollapse = self.init_uncollapse(uncollapse)
1053
+ extra_flags = extra_flags or {}
915
1054
 
916
- if isinstance(parent, HtmlTreeView.Extension):
917
- special_keys = special_keys or parent._html_tree_view_special_keys() # pylint: disable=protected-access
918
- include_keys = include_keys or parent._html_tree_view_include_keys() # pylint: disable=protected-access
919
- exclude_keys = exclude_keys or parent._html_tree_view_exclude_keys() # pylint: disable=protected-access
1055
+ inherited_kwargs = dict(
1056
+ # For child summary.
1057
+ enable_summary=enable_summary,
1058
+ enable_summary_for_str=enable_summary_for_str,
1059
+ max_summary_len_for_str=max_summary_len_for_str,
1060
+ enable_summary_tooltip=enable_summary_tooltip,
1061
+ # For child content.
1062
+ enable_key_tooltip=enable_key_tooltip,
1063
+ key_style=key_style,
1064
+ key_color=key_color,
1065
+ include_keys=include_keys if callable(include_keys) else None,
1066
+ exclude_keys=exclude_keys if callable(exclude_keys) else None,
1067
+ collapse_level=None if collapse_level is None else (collapse_level - 1),
1068
+ uncollapse=uncollapse,
1069
+ highlight=highlight,
1070
+ lowlight=lowlight,
1071
+ extra_flags=extra_flags,
1072
+ debug=debug,
1073
+ )
920
1074
 
921
- special_keys = special_keys or []
922
- include_keys = set(include_keys or [])
923
- exclude_keys = set(exclude_keys or [])
1075
+ render_key_fn = render_key_fn or HtmlTreeView.object_key
1076
+ render_value_fn = render_value_fn or HtmlTreeView.render
924
1077
 
925
- render_key_fn = render_key_fn or self.object_key
926
- render_value_fn = render_value_fn or self.render
1078
+ def render_child_key(child_path, value, parent, child_kwargs):
1079
+ render_child_key_fn = child_kwargs['extra_flags'].get(
1080
+ 'render_key_fn', render_key_fn
1081
+ )
1082
+ return render_child_key_fn(
1083
+ self,
1084
+ child_path,
1085
+ value=value,
1086
+ parent=parent,
1087
+ **child_kwargs
1088
+ )
927
1089
 
1090
+ def render_child_value(name, value, child_path, child_kwargs):
1091
+ render_child_value_fn = child_kwargs['extra_flags'].get(
1092
+ 'render_value_fn', render_value_fn
1093
+ )
1094
+ child_html = render_child_value_fn(
1095
+ self,
1096
+ value=value, name=child_kwargs.pop('name', name),
1097
+ parent=parent,
1098
+ root_path=child_path,
1099
+ **child_kwargs
1100
+ )
1101
+ should_highlight = highlight and highlight(child_path, value, parent)
1102
+ should_lowlight = lowlight and lowlight(child_path, value, parent)
1103
+ if should_highlight or should_lowlight:
1104
+ return Html.element(
1105
+ 'div', [child_html],
1106
+ css_classes=[
1107
+ 'highlight' if should_highlight else None,
1108
+ 'lowlight' if should_lowlight else None,
1109
+ ],
1110
+ )
1111
+ else:
1112
+ return child_html
1113
+
1114
+ has_child = False
928
1115
  s = Html()
929
1116
  if kv:
930
- include_keys = include_keys or set(kv.keys())
931
- if filter is not None:
932
- include_keys -= set(
933
- k for k, v in kv.items()
934
- if not filter(root_path + k, v, parent)
935
- )
936
- if exclude_keys:
937
- include_keys -= exclude_keys
938
-
939
- if special_keys:
940
- for k in special_keys:
941
- if k in include_keys and k in kv:
942
- child_path = root_path + k
943
- v = kv[k]
1117
+ # Compute included keys.
1118
+ if callable(include_keys):
1119
+ include_keys = [
1120
+ k for k, v in kv.items() if include_keys(root_path + k, v, parent)
1121
+ ]
1122
+ elif include_keys is not None:
1123
+ include_keys = list(k for k in include_keys if k in kv)
1124
+ else:
1125
+ include_keys = list(kv.keys())
1126
+
1127
+ # Filter with excluded keys.
1128
+ if callable(exclude_keys):
1129
+ include_keys = [
1130
+ k for k in include_keys if not exclude_keys(
1131
+ root_path + k, kv[k], parent
1132
+ )
1133
+ ]
1134
+ elif exclude_keys is not None:
1135
+ exclude_keys = set(exclude_keys)
1136
+ include_keys = [k for k in include_keys if k not in exclude_keys]
1137
+
1138
+ # Figure out keys of different styles.
1139
+ label_keys = []
1140
+ summary_keys = []
1141
+ if isinstance(parent, (tuple, list)) or key_style == 'label':
1142
+ label_keys = include_keys
1143
+ elif key_style == 'summary':
1144
+ summary_keys = include_keys
1145
+ else:
1146
+ assert callable(key_style), key_style
1147
+ for k in include_keys:
1148
+ ks = key_style(root_path + k, kv[k], parent)
1149
+ if ks == 'summary':
1150
+ summary_keys.append(k)
1151
+ elif ks == 'label':
1152
+ label_keys.append(k)
1153
+
1154
+ # Render child nodes with summary keys.
1155
+ if summary_keys:
1156
+ for k in summary_keys:
1157
+ child_path = root_path + k
1158
+ child_kwargs = self.get_child_kwargs(
1159
+ inherited_kwargs, child_config, k, root_path
1160
+ )
1161
+ s.write(render_child_value(k, kv[k], child_path, child_kwargs))
1162
+ has_child = True
1163
+
1164
+ # Render child nodes with label keys.
1165
+ if label_keys:
1166
+ s.write('<table>')
1167
+ for k in label_keys:
1168
+ v = kv[k]
1169
+ child_path = root_path + k
1170
+ child_kwargs = self.get_child_kwargs(
1171
+ inherited_kwargs, child_config, k, root_path
1172
+ )
1173
+ key_cell = render_child_key(child_path, v, parent, child_kwargs)
1174
+ value_cell = render_child_value(None, v, child_path, child_kwargs)
1175
+ if value_cell is not None:
944
1176
  s.write(
945
1177
  Html.element(
946
- 'div',
1178
+ 'tr',
947
1179
  [
948
- render_value_fn(
949
- value=v,
950
- name=k,
951
- parent=parent,
952
- root_path=child_path,
953
- filter=filter,
954
- special_keys=None,
955
- include_keys=None,
956
- exclude_keys=None,
957
- collapse_level=collapse_level,
958
- uncollapse=uncollapse,
959
- highlight=highlight,
960
- lowlight=lowlight,
961
- max_summary_len_for_str=max_summary_len_for_str,
962
- enable_summary_tooltip=enable_summary_tooltip,
963
- enable_key_tooltip=enable_key_tooltip,
964
- **kwargs
965
- )
966
- ],
967
- css_class=[
968
- 'special_value',
969
- (
970
- 'highlight' if highlight
971
- and highlight(child_path, v, parent) else None
972
- ),
973
- (
974
- 'lowlight' if lowlight
975
- and lowlight(child_path, v, parent) else None
976
- )
1180
+ '<td>', key_cell, '</td>',
1181
+ '<td>', value_cell, '</td>',
977
1182
  ],
978
1183
  )
979
1184
  )
980
- include_keys.remove(k)
981
-
982
- if include_keys:
983
- s.write('<table>')
984
- for k, v in kv.items():
985
- if k not in include_keys:
986
- continue
987
- child_path = root_path + k
988
- key_cell = render_key_fn(
989
- key=k,
990
- parent=parent,
991
- root_path=child_path,
992
- enable_tooltip=enable_key_tooltip,
993
- )
994
- value_cell = Html.element(
995
- 'div',
996
- [
997
- render_value_fn(
998
- value=v,
999
- name=None,
1000
- parent=parent,
1001
- root_path=child_path,
1002
- special_keys=None,
1003
- include_keys=None,
1004
- exclude_keys=None,
1005
- collapse_level=collapse_level,
1006
- uncollapse=uncollapse,
1007
- filter=filter,
1008
- highlight=highlight,
1009
- lowlight=lowlight,
1010
- max_summary_len_for_str=max_summary_len_for_str,
1011
- enable_summary_tooltip=enable_summary_tooltip,
1012
- enable_key_tooltip=enable_key_tooltip,
1013
- **kwargs,
1014
- )
1015
- ],
1016
- css_class=[
1017
- (
1018
- 'highlight' if highlight
1019
- and highlight(child_path, v, parent) else None
1020
- ),
1021
- (
1022
- 'lowlight' if lowlight
1023
- and lowlight(child_path, v, parent) else None
1024
- )
1025
- ],
1026
- )
1027
- s.write(
1028
- Html.element(
1029
- 'tr',
1030
- [
1031
- '<td>', key_cell, '</td>',
1032
- '<td>', value_cell, '</td>',
1033
- ],
1034
- )
1035
- )
1185
+ has_child = True
1036
1186
  s.write('</table>')
1037
- else:
1038
- s.write(Html.element('span', css_class=['empty_container']))
1187
+
1188
+ if not has_child:
1189
+ s.write(Html.element('span', css_classes=['empty-container']))
1190
+
1039
1191
  return Html.element(
1040
1192
  'div',
1041
1193
  [s],
1042
- css_class=[
1043
- 'complex_value',
1194
+ css_classes=[
1195
+ 'complex-value',
1044
1196
  self.css_class_name(parent),
1045
- css_class,
1197
+ css_classes,
1046
1198
  ]
1047
1199
  ).add_style(
1048
1200
  """
1049
1201
  /* Complex value styles. */
1050
- span.empty_container::before {
1202
+ span.empty-container::before {
1051
1203
  content: '(empty)';
1052
1204
  font-style: italic;
1053
1205
  margin-left: 0.5em;
@@ -1056,32 +1208,31 @@ class HtmlTreeView(HtmlView):
1056
1208
  """
1057
1209
  )
1058
1210
 
1059
- @HtmlView.extension_method('_html_tree_view_tooltip')
1060
1211
  def tooltip(
1061
1212
  self,
1062
1213
  value: Any,
1063
1214
  *,
1064
- name: Optional[str] = None,
1065
1215
  parent: Any = None,
1066
1216
  root_path: Optional[KeyPath] = None,
1067
- content: Union[str, Html, None] = HtmlView.PresetArgValue(None),
1068
- **kwargs
1217
+ css_classes: Optional[Sequence[str]] = None,
1218
+ content: Union[str, Html, None] = None,
1219
+ **kwargs,
1069
1220
  ) -> Html:
1070
1221
  """Renders a tooltip for the value.
1071
1222
 
1072
1223
  Args:
1073
1224
  value: The value to render.
1074
- name: The name of the value.
1075
- parent: The parent value of the key-value pairs.
1225
+ parent: The parent of the value.
1076
1226
  root_path: The root path of the value.
1077
- content: The content of the tooltip. If None, the formatted value will be
1078
- used as the content.
1079
- **kwargs: Additional keyword arguments passed from `pg.to_html`.
1227
+ css_classes: CSS classes to add to the HTML element.
1228
+ content: The content to render. If None, the value will be rendered.
1229
+ **kwargs: Additional keyword arguments passed from the user that
1230
+ will be ignored.
1080
1231
 
1081
1232
  Returns:
1082
1233
  The rendered HTML as the tooltip of the value.
1083
1234
  """
1084
- del name, parent
1235
+ del parent, kwargs
1085
1236
  if content is None:
1086
1237
  content = Html.escape(
1087
1238
  object_utils.format(
@@ -1092,13 +1243,15 @@ class HtmlTreeView(HtmlView):
1092
1243
  python_format=True,
1093
1244
  max_bytes_len=64,
1094
1245
  max_str_len=256,
1095
- **kwargs
1096
1246
  )
1097
1247
  )
1098
1248
  return Html.element(
1099
1249
  'span',
1100
1250
  [content],
1101
- css_class=['tooltip', self.css_class_name(value)],
1251
+ css_classes=[
1252
+ 'tooltip',
1253
+ css_classes,
1254
+ ],
1102
1255
  ).add_style(
1103
1256
  """
1104
1257
  /* Tooltip styles. */
@@ -1119,23 +1272,220 @@ class HtmlTreeView(HtmlView):
1119
1272
  @staticmethod
1120
1273
  def css_class_name(value: Any) -> Optional[str]:
1121
1274
  """Returns the CSS class name for the value."""
1122
- if isinstance(value, HtmlTreeView.Extension):
1123
- return Html.concate(value._html_element_class()) # pylint: disable=protected-access
1124
- value = value if inspect.isclass(value) else type(value)
1125
- return object_utils.camel_to_snake(value.__name__, '-')
1275
+ # if isinstance(value, HtmlTreeView.Extension):
1276
+ # return Html.concate(value._html_element_class()) # pylint: disable=protected-access
1277
+ if inspect.isclass(value):
1278
+ class_name = f'{value.__name__}-class'
1279
+ else:
1280
+ class_name = type(value).__name__
1281
+ return object_utils.camel_to_snake(class_name, '-')
1126
1282
 
1127
1283
  @staticmethod
1128
- def max_collapse_level(
1129
- original_level: int | None,
1130
- subtree_uncollapse_level: int | None,
1131
- root_path: KeyPath
1132
- ) -> int | None:
1133
- """Consolidates the collapse level."""
1134
- if original_level is None or subtree_uncollapse_level is None:
1135
- return None
1136
- return max(original_level, root_path.depth + subtree_uncollapse_level)
1284
+ def init_uncollapse(
1285
+ uncollapse: Union[Iterable[Union[KeyPath, str]], base.NodeFilter, None],
1286
+ ) -> Union[KeyPathSet, base.NodeFilter]:
1287
+ """Initializes the uncollapse argument."""
1288
+ if uncollapse is None:
1289
+ return KeyPathSet()
1290
+ elif callable(uncollapse):
1291
+ return uncollapse
1292
+ else:
1293
+ return KeyPathSet.from_value(uncollapse, include_intermediate=True)
1294
+
1295
+ @staticmethod
1296
+ def get_child_kwargs(
1297
+ call_kwargs: Dict[str, Any],
1298
+ child_config: Dict[str, Any],
1299
+ child_key: Optional[str],
1300
+ root_path: KeyPath,
1301
+ ) -> Dict[str, Any]:
1302
+ """Enter the child config for a child key."""
1303
+ if not child_config:
1304
+ return call_kwargs
1137
1305
 
1306
+ child_kwargs = child_config.get(
1307
+ child_key, child_config.get('__default__', None)
1308
+ )
1309
+ if not child_kwargs:
1310
+ return call_kwargs
1311
+
1312
+ return HtmlTreeView.get_kwargs(
1313
+ call_kwargs, child_kwargs, root_path + child_key,
1314
+ )
1138
1315
 
1139
- _REGEX_CAMEL_TO_SNAKE = re.compile(r'(?<!^)(?=[A-Z])')
1316
+ # pytype: disable=annotation-type-mismatch
1317
+ @staticmethod
1318
+ def get_passthrough_kwargs(
1319
+ *,
1320
+ enable_summary: Optional[bool] = object_utils.MISSING_VALUE,
1321
+ enable_summary_for_str: bool = object_utils.MISSING_VALUE,
1322
+ max_summary_len_for_str: int = object_utils.MISSING_VALUE,
1323
+ enable_summary_tooltip: bool = object_utils.MISSING_VALUE,
1324
+ key_style: Union[
1325
+ Literal['label', 'summary'],
1326
+ Callable[[KeyPath, Any, Any], Literal['label', 'summary']]
1327
+ ] = object_utils.MISSING_VALUE,
1328
+ key_color: Union[
1329
+ Tuple[Optional[str], Optional[str]],
1330
+ Callable[[KeyPath, Any, Any], Tuple[Optional[str], Optional[str]]]
1331
+ ] = object_utils.MISSING_VALUE,
1332
+ include_keys: Union[
1333
+ Iterable[Union[int, str]],
1334
+ Callable[[KeyPath, Any, Any], Iterable[Union[int, str]]],
1335
+ None
1336
+ ] = object_utils.MISSING_VALUE,
1337
+ exclude_keys: Union[
1338
+ Iterable[Union[int, str]],
1339
+ Callable[[KeyPath, Any, Any], Iterable[Union[int, str]]],
1340
+ None
1341
+ ] = object_utils.MISSING_VALUE,
1342
+ enable_key_tooltip: bool = object_utils.MISSING_VALUE,
1343
+ uncollapse: Union[
1344
+ KeyPathSet, base.NodeFilter, None
1345
+ ] = object_utils.MISSING_VALUE,
1346
+ extra_flags: Optional[Dict[str, Any]] = object_utils.MISSING_VALUE,
1347
+ highlight: Optional[base.NodeFilter] = object_utils.MISSING_VALUE,
1348
+ lowlight: Optional[base.NodeFilter] = object_utils.MISSING_VALUE,
1349
+ debug: bool = object_utils.MISSING_VALUE,
1350
+ remove: Optional[Iterable[str]] = None,
1351
+ **kwargs,
1352
+ ):
1353
+ # pytype: enable=annotation-type-mismatch
1354
+ """Gets the rendering arguments to pass through to the child nodes."""
1355
+ del kwargs
1356
+ passthrough_kwargs = dict(
1357
+ enable_summary=enable_summary,
1358
+ enable_summary_for_str=enable_summary_for_str,
1359
+ max_summary_len_for_str=max_summary_len_for_str,
1360
+ enable_summary_tooltip=enable_summary_tooltip,
1361
+ enable_key_tooltip=enable_key_tooltip,
1362
+ key_style=key_style,
1363
+ key_color=key_color,
1364
+ include_keys=(
1365
+ include_keys if callable(include_keys)
1366
+ else object_utils.MISSING_VALUE
1367
+ ),
1368
+ exclude_keys=(
1369
+ exclude_keys if callable(exclude_keys)
1370
+ else object_utils.MISSING_VALUE
1371
+ ),
1372
+ uncollapse=uncollapse,
1373
+ highlight=highlight,
1374
+ lowlight=lowlight,
1375
+ extra_flags=extra_flags,
1376
+ debug=debug
1377
+ )
1378
+ # Filter out missing values.
1379
+ passthrough_kwargs = {
1380
+ k: v for k, v in passthrough_kwargs.items()
1381
+ if v is not object_utils.MISSING_VALUE
1382
+ }
1383
+ if remove:
1384
+ return {
1385
+ k: v for k, v in passthrough_kwargs.items()
1386
+ if k not in remove
1387
+ }
1388
+ return passthrough_kwargs
1389
+
1390
+ @staticmethod
1391
+ def get_collapse_level(
1392
+ original_level: Union[None, int, Tuple[Optional[int], int]],
1393
+ overriden_level: Union[None, int, Tuple[Optional[int], int]],
1394
+ ) -> Optional[int]:
1395
+ """Gets the collapse level for a child node."""
1396
+ original_offset, overriden_offset = 0, 0
1397
+ if isinstance(original_level, tuple):
1398
+ original_level, original_offset = original_level
1399
+ if isinstance(overriden_level, tuple):
1400
+ overriden_level, overriden_offset = overriden_level
1401
+
1402
+ if original_level is None:
1403
+ return original_level
1404
+ elif overriden_level is None:
1405
+ return overriden_level
1406
+ else:
1407
+ return max(
1408
+ original_level + original_offset,
1409
+ overriden_level + overriden_offset
1410
+ )
1411
+
1412
+ @staticmethod
1413
+ def get_kwargs(
1414
+ call_kwargs: Dict[str, Any],
1415
+ overriden_kwargs: Dict[str, Any],
1416
+ root_path: Optional[KeyPath] = None,
1417
+ ) -> Dict[str, Any]:
1418
+ """Override render arguments."""
1419
+ # Select child config to override.
1420
+ if not overriden_kwargs:
1421
+ return call_kwargs
1422
+
1423
+ call_kwargs = call_kwargs.copy()
1424
+ overriden_kwargs = overriden_kwargs.copy()
1425
+
1426
+ # Override collapse_level.
1427
+ if 'collapse_level' in call_kwargs or 'collapse_level' in overriden_kwargs:
1428
+ call_kwargs['collapse_level'] = HtmlTreeView.get_collapse_level(
1429
+ call_kwargs.pop('collapse_level', 1),
1430
+ overriden_kwargs.pop('collapse_level', 0)
1431
+ )
1432
+
1433
+ # Override uncollapse.
1434
+ if 'uncollapse' in call_kwargs or 'uncollapse' in overriden_kwargs:
1435
+ uncollapse = KeyPathSet.from_value(
1436
+ call_kwargs.pop('uncollapse', None) or []
1437
+ )
1438
+ child_uncollapse = KeyPathSet.from_value(
1439
+ overriden_kwargs.pop('uncollapse', None) or []
1440
+ )
1441
+ call_kwargs['uncollapse'] = HtmlTreeView.merge_uncollapse(
1442
+ uncollapse, child_uncollapse, root_path
1443
+ )
1444
+
1445
+ # Deep hierarchy merge.
1446
+ return object_utils.merge_tree(call_kwargs, overriden_kwargs)
1447
+
1448
+ @staticmethod
1449
+ def merge_uncollapse(
1450
+ uncollapse: Union[KeyPathSet, base.NodeFilter, None],
1451
+ child_uncollapse: Optional[KeyPathSet],
1452
+ child_path: Optional[KeyPath] = None,
1453
+ ) -> Union[KeyPathSet, base.NodeFilter]:
1454
+ """Merge uncollapse paths."""
1455
+ if not uncollapse and not child_uncollapse:
1456
+ return KeyPathSet()
1457
+
1458
+ if callable(uncollapse) or not child_uncollapse:
1459
+ assert uncollapse is not None
1460
+ return uncollapse
1461
+
1462
+ assert isinstance(uncollapse, KeyPathSet), uncollapse
1463
+ assert isinstance(child_uncollapse, KeyPathSet), child_uncollapse
1464
+ if child_path:
1465
+ child_uncollapse = child_uncollapse.copy()
1466
+ child_uncollapse.rebase(child_path)
1467
+ uncollapse.update(child_uncollapse)
1468
+ return uncollapse
1469
+
1470
+ @staticmethod
1471
+ def get_color(
1472
+ color: Union[
1473
+ Tuple[str, str],
1474
+ Callable[
1475
+ [KeyPath, Any, Any],
1476
+ Tuple[Optional[str], Optional[str]]
1477
+ ],
1478
+ None
1479
+ ],
1480
+ root_path,
1481
+ value,
1482
+ parent
1483
+ ) -> Tuple[Optional[str], Optional[str]]:
1484
+ if callable(color):
1485
+ return color(root_path, value, parent)
1486
+ if color is None:
1487
+ return (None, None)
1488
+ assert isinstance(color, tuple) and len(color) == 2, color
1489
+ return color
1140
1490
 
1141
1491
  # pytype: enable=annotation-type-mismatch