pyglove 0.4.5.dev202410170809__py3-none-any.whl → 0.4.5.dev202410180808__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.
- pyglove/core/__init__.py +1 -0
- pyglove/core/symbolic/base.py +10 -10
- pyglove/core/symbolic/base_test.py +47 -8
- pyglove/core/symbolic/diff.py +60 -66
- pyglove/core/symbolic/diff_test.py +33 -98
- pyglove/core/symbolic/ref.py +17 -3
- pyglove/core/symbolic/ref_test.py +9 -110
- pyglove/core/views/__init__.py +1 -0
- pyglove/core/views/base.py +45 -151
- pyglove/core/views/base_test.py +14 -49
- pyglove/core/views/html/base.py +19 -18
- pyglove/core/views/html/base_test.py +10 -8
- pyglove/core/views/html/tree_view.py +911 -561
- pyglove/core/views/html/tree_view_test.py +743 -164
- {pyglove-0.4.5.dev202410170809.dist-info → pyglove-0.4.5.dev202410180808.dist-info}/METADATA +1 -1
- {pyglove-0.4.5.dev202410170809.dist-info → pyglove-0.4.5.dev202410180808.dist-info}/RECORD +19 -19
- {pyglove-0.4.5.dev202410170809.dist-info → pyglove-0.4.5.dev202410180808.dist-info}/LICENSE +0 -0
- {pyglove-0.4.5.dev202410170809.dist-info → pyglove-0.4.5.dev202410180808.dist-info}/WHEEL +0 -0
- {pyglove-0.4.5.dev202410170809.dist-info → pyglove-0.4.5.dev202410180808.dist-info}/top_level.txt +0 -0
@@ -14,8 +14,7 @@
|
|
14
14
|
"""HTML Tree View (The default view for PyGlove objects)."""
|
15
15
|
|
16
16
|
import inspect
|
17
|
-
import
|
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
|
-
|
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
|
-
|
46
|
-
|
47
|
-
|
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
|
-
|
50
|
-
|
51
|
-
|
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
|
-
|
54
|
-
|
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
|
-
|
58
|
-
|
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
|
-
|
62
|
-
|
63
|
-
|
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
|
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
|
-
#
|
96
|
+
# Users could override the methods below to customize rendering
|
97
|
+
# logics.
|
73
98
|
#
|
74
99
|
|
75
|
-
def
|
100
|
+
def _html_tree_view(
|
76
101
|
self,
|
77
102
|
*,
|
78
|
-
view: '
|
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
|
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
|
-
|
107
|
-
|
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
|
-
|
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
|
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
|
-
|
174
|
-
|
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
|
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
|
-
|
229
|
-
|
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
|
-
|
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
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
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
|
-
|
335
|
-
|
336
|
-
|
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
|
-
|
354
|
-
|
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
|
-
|
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
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
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
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
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
|
414
|
-
return content
|
404
|
+
return debug_info + content
|
415
405
|
|
416
|
-
|
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
|
427
|
-
|
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.
|
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] =
|
450
|
+
collapse_level: Optional[int] = 1,
|
476
451
|
uncollapse: Union[KeyPathSet, base.NodeFilter] = None,
|
477
452
|
) -> bool:
|
478
|
-
"""Returns
|
479
|
-
|
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
|
-
|
484
|
-
return
|
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] =
|
494
|
-
|
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
|
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
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
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] =
|
519
|
-
enable_summary_tooltip: bool =
|
520
|
-
|
521
|
-
|
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
|
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
|
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
|
-
|
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
|
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
|
554
|
-
|
555
|
-
|
556
|
-
return
|
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
|
-
|
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
|
-
|
632
|
+
css_classes=['summary-title', css_classes],
|
578
633
|
),
|
579
634
|
|
580
|
-
#
|
581
|
-
lambda:
|
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
|
-
|
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
|
-
.
|
651
|
+
.summary-name {
|
598
652
|
display: inline;
|
599
|
-
padding:
|
653
|
+
padding: 3px 5px 3px 5px;
|
654
|
+
margin: 0 5px;
|
655
|
+
border-radius: 3px;
|
600
656
|
}
|
601
|
-
.
|
657
|
+
.summary-title {
|
602
658
|
display: inline;
|
603
659
|
}
|
604
|
-
.
|
660
|
+
.summary-name + div.summary-title {
|
605
661
|
display: inline;
|
606
662
|
color: #aaa;
|
607
663
|
}
|
608
|
-
.
|
664
|
+
.summary-title:hover + span.tooltip {
|
609
665
|
visibility: visible;
|
610
666
|
}
|
611
|
-
|
612
|
-
|
613
|
-
color:
|
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
|
-
|
679
|
+
root_path: KeyPath,
|
625
680
|
*,
|
626
|
-
|
681
|
+
value: Any,
|
627
682
|
parent: Any,
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
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
|
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
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
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
|
-
|
657
|
-
'
|
658
|
-
type(key).__name__,
|
659
|
-
|
721
|
+
css_classes=[
|
722
|
+
'object-key',
|
723
|
+
type(root_path.key).__name__,
|
724
|
+
css_classes,
|
660
725
|
],
|
661
|
-
|
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:
|
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
|
-
|
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
|
-
.
|
678
|
-
margin
|
741
|
+
.object-key {
|
742
|
+
margin: 0.15em 0.3em 0.15em 0;
|
743
|
+
display: block;
|
679
744
|
}
|
680
|
-
.
|
745
|
+
.object-key:hover + .tooltip {
|
681
746
|
visibility: visible;
|
682
747
|
background-color: darkblue;
|
683
748
|
}
|
684
|
-
.
|
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
|
-
.
|
756
|
+
.object-key.int::before{
|
692
757
|
content: '[';
|
693
758
|
}
|
694
|
-
.
|
759
|
+
.object-key.int::after{
|
695
760
|
content: ']';
|
696
761
|
}
|
697
|
-
.
|
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
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
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
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
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
|
847
|
+
to determine whether to highlight.
|
748
848
|
lowlight: A function with signature (path, value, parent) -> bool
|
749
|
-
to determine whether to lowlight
|
750
|
-
|
751
|
-
|
752
|
-
|
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
|
772
|
-
|
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
|
-
|
886
|
+
child_config=child_config,
|
778
887
|
highlight=highlight,
|
779
888
|
lowlight=lowlight,
|
780
|
-
|
781
|
-
|
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
|
-
|
794
|
-
max_summary_len_for_str: int =
|
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
|
-
|
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
|
-
|
828
|
-
'
|
934
|
+
css_classes=[
|
935
|
+
'simple-value',
|
829
936
|
self.css_class_name(value),
|
830
|
-
|
937
|
+
css_classes,
|
831
938
|
],
|
832
939
|
).add_style(
|
833
940
|
"""
|
834
941
|
/* Simple value styles. */
|
835
|
-
.
|
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
|
-
.
|
949
|
+
.simple-value.str {
|
843
950
|
color: darkred;
|
844
951
|
font-style: italic;
|
845
952
|
}
|
846
|
-
.
|
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
|
-
|
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
|
-
|
863
|
-
|
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
|
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
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
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
|
-
|
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
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
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
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
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
|
-
|
922
|
-
|
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
|
-
|
926
|
-
|
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
|
-
|
931
|
-
if
|
932
|
-
include_keys
|
933
|
-
k for k, v in kv.items()
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
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
|
-
'
|
1178
|
+
'tr',
|
947
1179
|
[
|
948
|
-
|
949
|
-
|
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
|
-
|
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
|
-
|
1038
|
-
|
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
|
-
|
1043
|
-
'
|
1194
|
+
css_classes=[
|
1195
|
+
'complex-value',
|
1044
1196
|
self.css_class_name(parent),
|
1045
|
-
|
1197
|
+
css_classes,
|
1046
1198
|
]
|
1047
1199
|
).add_style(
|
1048
1200
|
"""
|
1049
1201
|
/* Complex value styles. */
|
1050
|
-
span.
|
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
|
-
|
1068
|
-
|
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
|
-
|
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
|
-
|
1078
|
-
|
1079
|
-
**kwargs: Additional keyword arguments passed from
|
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
|
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
|
-
|
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
|
-
|
1124
|
-
|
1125
|
-
|
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
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
return
|
1136
|
-
|
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
|
-
|
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
|