pyglove 0.4.5.dev202410020809__py3-none-any.whl → 0.4.5.dev202410100808__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 +9 -0
- pyglove/core/object_utils/__init__.py +1 -0
- pyglove/core/object_utils/value_location.py +10 -0
- pyglove/core/object_utils/value_location_test.py +22 -0
- pyglove/core/symbolic/base.py +40 -2
- pyglove/core/symbolic/base_test.py +67 -0
- pyglove/core/symbolic/diff.py +190 -1
- pyglove/core/symbolic/diff_test.py +290 -0
- pyglove/core/symbolic/object_test.py +3 -8
- pyglove/core/symbolic/ref.py +29 -0
- pyglove/core/symbolic/ref_test.py +143 -0
- pyglove/core/typing/__init__.py +4 -0
- pyglove/core/typing/callable_ext.py +240 -1
- pyglove/core/typing/callable_ext_test.py +255 -0
- pyglove/core/typing/inspect.py +63 -0
- pyglove/core/typing/inspect_test.py +39 -0
- pyglove/core/views/__init__.py +30 -0
- pyglove/core/views/base.py +906 -0
- pyglove/core/views/base_test.py +615 -0
- pyglove/core/views/html/__init__.py +27 -0
- pyglove/core/views/html/base.py +529 -0
- pyglove/core/views/html/base_test.py +804 -0
- pyglove/core/views/html/tree_view.py +1052 -0
- pyglove/core/views/html/tree_view_test.py +748 -0
- {pyglove-0.4.5.dev202410020809.dist-info → pyglove-0.4.5.dev202410100808.dist-info}/METADATA +1 -1
- {pyglove-0.4.5.dev202410020809.dist-info → pyglove-0.4.5.dev202410100808.dist-info}/RECORD +29 -21
- {pyglove-0.4.5.dev202410020809.dist-info → pyglove-0.4.5.dev202410100808.dist-info}/LICENSE +0 -0
- {pyglove-0.4.5.dev202410020809.dist-info → pyglove-0.4.5.dev202410100808.dist-info}/WHEEL +0 -0
- {pyglove-0.4.5.dev202410020809.dist-info → pyglove-0.4.5.dev202410100808.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1052 @@
|
|
1
|
+
# Copyright 2024 The Langfun Authors
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
"""HTML Tree View (The default view for PyGlove objects)."""
|
15
|
+
|
16
|
+
import inspect
|
17
|
+
import re
|
18
|
+
from typing import Any, Dict, Iterable, Optional, Sequence, Set, Union
|
19
|
+
|
20
|
+
from pyglove.core import object_utils
|
21
|
+
from pyglove.core.views.html import base
|
22
|
+
|
23
|
+
|
24
|
+
KeyPath = object_utils.KeyPath
|
25
|
+
Html = base.Html
|
26
|
+
HtmlView = base.HtmlView
|
27
|
+
|
28
|
+
|
29
|
+
# pytype: disable=annotation-type-mismatch
|
30
|
+
|
31
|
+
|
32
|
+
class HtmlTreeView(HtmlView):
|
33
|
+
"""HTML Tree View."""
|
34
|
+
|
35
|
+
VIEW_ID = 'html-tree-view'
|
36
|
+
|
37
|
+
class Extension(HtmlView.Extension):
|
38
|
+
"""The base class for extensions for HtmlTreeView."""
|
39
|
+
|
40
|
+
def _html_tree_view_render(
|
41
|
+
self,
|
42
|
+
*,
|
43
|
+
view: 'HtmlView',
|
44
|
+
name: Optional[str],
|
45
|
+
parent: Any,
|
46
|
+
root_path: KeyPath,
|
47
|
+
title: Union[str, Html, None] = None,
|
48
|
+
special_keys: Optional[Sequence[Union[int, str]]] = None,
|
49
|
+
include_keys: Optional[Iterable[Union[int, str]]] = None,
|
50
|
+
exclude_keys: Optional[Iterable[Union[int, str]]] = None,
|
51
|
+
filter: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None), # pylint: disable=redefined-builtin
|
52
|
+
highlight: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None),
|
53
|
+
lowlight: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None),
|
54
|
+
enable_summary: Optional[bool] = HtmlView.PresetArgValue(None),
|
55
|
+
max_summary_len_for_str: int = HtmlView.PresetArgValue(40),
|
56
|
+
enable_summary_tooltip: bool = HtmlView.PresetArgValue(True),
|
57
|
+
enable_key_tooltip: bool = HtmlView.PresetArgValue(True),
|
58
|
+
collapse_level: Optional[int] = HtmlView.PresetArgValue(1),
|
59
|
+
uncollapse: Union[
|
60
|
+
Iterable[Union[KeyPath, str]], base.NodeFilter, None
|
61
|
+
] = HtmlView.PresetArgValue(None),
|
62
|
+
**kwargs,
|
63
|
+
) -> Html:
|
64
|
+
"""Returns the topmost HTML representation of the object.
|
65
|
+
|
66
|
+
Args:
|
67
|
+
view: The view to render the object.
|
68
|
+
name: The name of the object.
|
69
|
+
parent: The parent of the object.
|
70
|
+
root_path: The key path of the object relative to the root.
|
71
|
+
title: The title of the summary.
|
72
|
+
special_keys: The special keys to display (at the immediate child
|
73
|
+
level).
|
74
|
+
include_keys: The keys to include (at the immediate child level).
|
75
|
+
exclude_keys: The keys to exclude (at the immediate child level).
|
76
|
+
filter: A function with signature (path, value, parent) -> include
|
77
|
+
to determine whether to include a field (at all levels).
|
78
|
+
highlight: A function with signature (path, value, parent) -> bool
|
79
|
+
to determine whether to highlight a field.
|
80
|
+
lowlight: A function with signature (path, value, parent) -> bool
|
81
|
+
to determine whether to lowlight a field.
|
82
|
+
enable_summary: Whether to enable the summary. If None, summary will
|
83
|
+
be enabled for complex types or when string exceeds
|
84
|
+
`max_summary_len_for_str`.
|
85
|
+
max_summary_len_for_str: The maximum length of the string to display.
|
86
|
+
enable_summary_tooltip: Whether to enable the tooltip for the summary.
|
87
|
+
enable_key_tooltip: Whether to enable the tooltip for the key.
|
88
|
+
collapse_level: The level to collapse the tree
|
89
|
+
uncollapse: A set of key paths for the nodes to uncollapse. or a
|
90
|
+
function with signature (path, value, parent) -> bool to determine
|
91
|
+
whether to uncollapse a node.
|
92
|
+
**kwargs: Additional keyword arguments passed from `pg.to_html`.
|
93
|
+
|
94
|
+
Returns:
|
95
|
+
The rendered HTML.
|
96
|
+
"""
|
97
|
+
return view.render(
|
98
|
+
self,
|
99
|
+
name=name,
|
100
|
+
parent=parent,
|
101
|
+
root_path=root_path,
|
102
|
+
title=title,
|
103
|
+
special_keys=special_keys,
|
104
|
+
include_keys=include_keys,
|
105
|
+
exclude_keys=exclude_keys,
|
106
|
+
filter=filter,
|
107
|
+
highlight=highlight,
|
108
|
+
lowlight=lowlight,
|
109
|
+
enable_summary=enable_summary,
|
110
|
+
max_summary_len_for_str=max_summary_len_for_str,
|
111
|
+
enable_summary_tooltip=enable_summary_tooltip,
|
112
|
+
enable_key_tooltip=enable_key_tooltip,
|
113
|
+
collapse_level=collapse_level,
|
114
|
+
uncollapse=uncollapse,
|
115
|
+
**kwargs
|
116
|
+
)
|
117
|
+
|
118
|
+
def _html_tree_view_summary(
|
119
|
+
self,
|
120
|
+
*,
|
121
|
+
view: 'HtmlTreeView',
|
122
|
+
name: Optional[str],
|
123
|
+
parent: Any,
|
124
|
+
root_path: KeyPath,
|
125
|
+
css_class: Optional[Sequence[str]] = None,
|
126
|
+
title: Union[str, Html, None] = None,
|
127
|
+
enable_summary: Optional[bool] = HtmlView.PresetArgValue(None),
|
128
|
+
max_summary_len_for_str: int = HtmlView.PresetArgValue(40),
|
129
|
+
enable_summary_tooltip: bool = HtmlView.PresetArgValue(True),
|
130
|
+
**kwargs,
|
131
|
+
) -> Optional[Html]:
|
132
|
+
"""Returns the HTML representation of the object.
|
133
|
+
|
134
|
+
Args:
|
135
|
+
view: The view to render the object.
|
136
|
+
name: The name of the object.
|
137
|
+
parent: The parent of the object.
|
138
|
+
root_path: The key path of the object relative to the root.
|
139
|
+
css_class: The CSS classes to add to the root element
|
140
|
+
title: The title of the summary.
|
141
|
+
enable_summary: Whether to enable the summary. If None, summary will
|
142
|
+
be enabled for complex types or when string exceeds
|
143
|
+
`max_summary_len_for_str`.
|
144
|
+
max_summary_len_for_str: The maximum length of the string to display.
|
145
|
+
enable_summary_tooltip: Whether to enable the tooltip for the summary.
|
146
|
+
**kwargs: Additional keyword arguments passed from `pg.to_html`. These
|
147
|
+
arguments may be handled by the user logic but not the general
|
148
|
+
HtmlTreeView.
|
149
|
+
|
150
|
+
Returns:
|
151
|
+
An optional HTML object representing the summary of the object. If None,
|
152
|
+
the summary will be hidden.
|
153
|
+
"""
|
154
|
+
return view.summary(
|
155
|
+
self,
|
156
|
+
name=name,
|
157
|
+
parent=parent,
|
158
|
+
root_path=root_path,
|
159
|
+
css_class=css_class,
|
160
|
+
title=title,
|
161
|
+
enable_summary=enable_summary,
|
162
|
+
max_summary_len_for_str=max_summary_len_for_str,
|
163
|
+
enable_summary_tooltip=enable_summary_tooltip,
|
164
|
+
**kwargs,
|
165
|
+
)
|
166
|
+
|
167
|
+
def _html_tree_view_content(
|
168
|
+
self,
|
169
|
+
*,
|
170
|
+
view: 'HtmlTreeView',
|
171
|
+
name: Optional[str],
|
172
|
+
parent: Any,
|
173
|
+
root_path: KeyPath,
|
174
|
+
special_keys: Optional[Sequence[Union[int, str]]] = None,
|
175
|
+
include_keys: Optional[Iterable[Union[int, str]]] = None,
|
176
|
+
exclude_keys: Optional[Iterable[Union[int, str]]] = None,
|
177
|
+
filter: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None), # pylint: disable=redefined-builtin
|
178
|
+
highlight: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None),
|
179
|
+
lowlight: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None),
|
180
|
+
max_summary_len_for_str: int = HtmlView.PresetArgValue(40),
|
181
|
+
enable_summary_tooltip: bool = HtmlView.PresetArgValue(True),
|
182
|
+
enable_key_tooltip: bool = HtmlView.PresetArgValue(True),
|
183
|
+
collapse_level: Optional[int] = HtmlView.PresetArgValue(1),
|
184
|
+
uncollapse: Union[
|
185
|
+
Iterable[Union[KeyPath, str]], base.NodeFilter, None
|
186
|
+
] = HtmlView.PresetArgValue(None),
|
187
|
+
**kwargs,
|
188
|
+
) -> Html:
|
189
|
+
"""Returns the main content for the object.
|
190
|
+
|
191
|
+
Args:
|
192
|
+
view: The view to render the object.
|
193
|
+
name: The name of the object.
|
194
|
+
parent: The parent of the object.
|
195
|
+
root_path: The key path of the object relative to the root.
|
196
|
+
special_keys: The special keys to display (at the immediate child
|
197
|
+
level).
|
198
|
+
include_keys: The keys to include (at the immediate child level).
|
199
|
+
exclude_keys: The keys to exclude (at the immediate child level).
|
200
|
+
filter: A function with signature (path, value, parent) -> include
|
201
|
+
to determine whether to include a field (at all levels).
|
202
|
+
highlight: A function with signature (path, value, parent) -> bool
|
203
|
+
to determine whether to highlight a field (at all levels).
|
204
|
+
lowlight: A function with signature (path, value, parent) -> bool
|
205
|
+
to determine whether to lowlight a field (at all levels).
|
206
|
+
max_summary_len_for_str: The maximum length of the string to display.
|
207
|
+
enable_summary_tooltip: Whether to enable the tooltip for the summary.
|
208
|
+
enable_key_tooltip: Whether to enable the key tooltip.
|
209
|
+
collapse_level: The level to collapse the tree
|
210
|
+
uncollapse: The paths to uncollapse.
|
211
|
+
**kwargs: Additional keyword arguments passed from `pg.to_html`. These
|
212
|
+
arguments may be handled by the user logic but not the general
|
213
|
+
HtmlTreeView.
|
214
|
+
|
215
|
+
Returns:
|
216
|
+
The rendered HTML as the main content of the object.
|
217
|
+
"""
|
218
|
+
return view.content(
|
219
|
+
self,
|
220
|
+
name=name,
|
221
|
+
parent=parent,
|
222
|
+
root_path=root_path,
|
223
|
+
special_keys=special_keys,
|
224
|
+
include_keys=include_keys,
|
225
|
+
exclude_keys=exclude_keys,
|
226
|
+
filter=filter,
|
227
|
+
highlight=highlight,
|
228
|
+
lowlight=lowlight,
|
229
|
+
max_summary_len_for_str=max_summary_len_for_str,
|
230
|
+
enable_summary_tooltip=enable_summary_tooltip,
|
231
|
+
enable_key_tooltip=enable_key_tooltip,
|
232
|
+
collapse_level=collapse_level,
|
233
|
+
uncollapse=uncollapse,
|
234
|
+
**kwargs
|
235
|
+
)
|
236
|
+
|
237
|
+
def _html_tree_view_tooltip(
|
238
|
+
self,
|
239
|
+
*,
|
240
|
+
view: 'HtmlTreeView',
|
241
|
+
name: Optional[str],
|
242
|
+
parent: Any,
|
243
|
+
root_path: KeyPath,
|
244
|
+
content: Union[str, Html, None] = None,
|
245
|
+
**kwargs,
|
246
|
+
) -> Optional[Html]:
|
247
|
+
"""Returns the tooltip for the object.
|
248
|
+
|
249
|
+
Args:
|
250
|
+
view: The view to render the object.
|
251
|
+
name: The referenced name of the object.
|
252
|
+
parent: The parent of the object.
|
253
|
+
root_path: The key path of the object relative to the root.
|
254
|
+
content: Custom content to display in the tooltip.
|
255
|
+
**kwargs: Additional keyword arguments passed from `pg.to_html`. These
|
256
|
+
arguments may be handled by the user logic but not the general
|
257
|
+
HtmlTreeView.
|
258
|
+
|
259
|
+
Returns:
|
260
|
+
An optional HTML object representing the tooltip of the object. If None,
|
261
|
+
the tooltip will be hidden.
|
262
|
+
"""
|
263
|
+
return view.tooltip(
|
264
|
+
value=self, name=name, parent=parent, root_path=root_path,
|
265
|
+
content=content, **kwargs
|
266
|
+
)
|
267
|
+
|
268
|
+
@HtmlView.extension_method('_html_tree_view_render')
|
269
|
+
def render(
|
270
|
+
self,
|
271
|
+
value: Any,
|
272
|
+
*,
|
273
|
+
name: Optional[str] = None,
|
274
|
+
parent: Any = None,
|
275
|
+
root_path: Optional[KeyPath] = None,
|
276
|
+
css_class: Optional[Sequence[str]] = None,
|
277
|
+
title: Union[str, Html, None] = None,
|
278
|
+
special_keys: Optional[Sequence[Union[int, str]]] = None,
|
279
|
+
include_keys: Optional[Iterable[Union[int, str]]] = None,
|
280
|
+
exclude_keys: Optional[Iterable[Union[int, str]]] = None,
|
281
|
+
filter: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None), # pylint: disable=redefined-builtin
|
282
|
+
highlight: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None),
|
283
|
+
lowlight: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None),
|
284
|
+
enable_summary: Optional[bool] = HtmlView.PresetArgValue(None),
|
285
|
+
max_summary_len_for_str: int = HtmlView.PresetArgValue(40),
|
286
|
+
enable_summary_tooltip: bool = HtmlView.PresetArgValue(True),
|
287
|
+
enable_key_tooltip: bool = HtmlView.PresetArgValue(True),
|
288
|
+
collapse_level: Optional[int] = HtmlView.PresetArgValue(1),
|
289
|
+
uncollapse: Union[
|
290
|
+
Iterable[Union[KeyPath, str]], base.NodeFilter, None
|
291
|
+
] = HtmlView.PresetArgValue(None),
|
292
|
+
**kwargs
|
293
|
+
) -> Html:
|
294
|
+
"""Renders the entire HTML tree view for the value.
|
295
|
+
|
296
|
+
Args:
|
297
|
+
value: The value to render.
|
298
|
+
name: The name of the value.
|
299
|
+
parent: The parent of the value.
|
300
|
+
root_path: The root path of the value.
|
301
|
+
css_class: The CSS classes to add to the root element
|
302
|
+
title: The title of the summary.
|
303
|
+
special_keys: The special keys to display (at the immediate child level).
|
304
|
+
include_keys: The keys to include (at the immediate child level).
|
305
|
+
exclude_keys: The keys to exclude (at the immediate child level).
|
306
|
+
filter: A function with signature (path, value, parent) -> include
|
307
|
+
to determine whether to include a field (at all levels).
|
308
|
+
highlight: A function with signature (path, value, parent) -> bool
|
309
|
+
to determine whether to highlight a field.
|
310
|
+
lowlight: A function with signature (path, value, parent) -> bool
|
311
|
+
to determine whether to lowlight a field.
|
312
|
+
enable_summary: Whether to enable the summary. If None, summary will
|
313
|
+
be enabled for complex types or when string exceeds
|
314
|
+
`max_summary_len_for_str`.
|
315
|
+
max_summary_len_for_str: The maximum length of the string to display.
|
316
|
+
enable_summary_tooltip: Whether to enable the tooltip for the summary.
|
317
|
+
enable_key_tooltip: Whether to enable the key tooltip.
|
318
|
+
collapse_level: The level to collapse the tree.
|
319
|
+
uncollapse: The paths to uncollapse.
|
320
|
+
**kwargs: Additional keyword arguments passed from `pg.to_html`.
|
321
|
+
|
322
|
+
Returns:
|
323
|
+
The rendered HTML.
|
324
|
+
"""
|
325
|
+
root_path = root_path or KeyPath()
|
326
|
+
uncollapse = self.normalize_uncollapse(uncollapse)
|
327
|
+
summary = self.summary(
|
328
|
+
value,
|
329
|
+
name=name,
|
330
|
+
parent=parent,
|
331
|
+
root_path=root_path,
|
332
|
+
title=title,
|
333
|
+
enable_summary=enable_summary,
|
334
|
+
enable_summary_tooltip=enable_summary_tooltip,
|
335
|
+
max_summary_len_for_str=max_summary_len_for_str,
|
336
|
+
**kwargs,
|
337
|
+
)
|
338
|
+
content = self.content(
|
339
|
+
value,
|
340
|
+
name=name,
|
341
|
+
parent=parent,
|
342
|
+
root_path=root_path,
|
343
|
+
css_class=css_class if summary is None else None,
|
344
|
+
filter=filter,
|
345
|
+
special_keys=special_keys,
|
346
|
+
include_keys=include_keys,
|
347
|
+
exclude_keys=exclude_keys,
|
348
|
+
max_summary_len_for_str=max_summary_len_for_str,
|
349
|
+
enable_summary_tooltip=enable_summary_tooltip,
|
350
|
+
enable_key_tooltip=enable_key_tooltip,
|
351
|
+
collapse_level=collapse_level,
|
352
|
+
uncollapse=uncollapse,
|
353
|
+
**kwargs,
|
354
|
+
)
|
355
|
+
if summary is None:
|
356
|
+
content = Html.from_value(content)
|
357
|
+
assert content is not None
|
358
|
+
return content
|
359
|
+
|
360
|
+
collapse_view = self.should_collapse(
|
361
|
+
value, parent=parent, root_path=root_path,
|
362
|
+
collapse_level=collapse_level, uncollapse=uncollapse,
|
363
|
+
)
|
364
|
+
return Html.element(
|
365
|
+
'details',
|
366
|
+
[
|
367
|
+
summary,
|
368
|
+
content,
|
369
|
+
],
|
370
|
+
options=[None if collapse_view else 'open'],
|
371
|
+
css_class=[
|
372
|
+
'pyglove',
|
373
|
+
self.css_class_name(value),
|
374
|
+
] + (css_class or []),
|
375
|
+
).add_style(
|
376
|
+
"""
|
377
|
+
/* Value details styles. */
|
378
|
+
details.pyglove {
|
379
|
+
border: 1px solid #aaa;
|
380
|
+
border-radius: 4px;
|
381
|
+
padding: 0.5em 0.5em 0;
|
382
|
+
margin: 0.1em 0;
|
383
|
+
}
|
384
|
+
details.pyglove.special_value {
|
385
|
+
margin-bottom: 0.75em;
|
386
|
+
}
|
387
|
+
details.pyglove[open] {
|
388
|
+
padding: 0.5em 0.5em 0.5em;
|
389
|
+
}
|
390
|
+
.highlight {
|
391
|
+
background-color: Mark;
|
392
|
+
}
|
393
|
+
.lowlight {
|
394
|
+
opacity: 0.2;
|
395
|
+
}
|
396
|
+
"""
|
397
|
+
)
|
398
|
+
|
399
|
+
def normalize_uncollapse(
|
400
|
+
self,
|
401
|
+
uncollapse: Union[
|
402
|
+
Iterable[Union[KeyPath, str]], base.NodeFilter, None
|
403
|
+
] = HtmlView.PresetArgValue(None),
|
404
|
+
) -> Union[None, Set[KeyPath], base.NodeFilter]:
|
405
|
+
"""Normalize the uncollapse argument."""
|
406
|
+
if uncollapse is None:
|
407
|
+
return None
|
408
|
+
elif isinstance(uncollapse, set) or callable(uncollapse):
|
409
|
+
return uncollapse
|
410
|
+
else:
|
411
|
+
expanded = set()
|
412
|
+
for path in uncollapse:
|
413
|
+
path = object_utils.KeyPath.from_value(path)
|
414
|
+
expanded.add(path)
|
415
|
+
while path:
|
416
|
+
expanded.add(path.parent)
|
417
|
+
path = path.parent
|
418
|
+
return expanded
|
419
|
+
|
420
|
+
def should_collapse(
|
421
|
+
self,
|
422
|
+
value: Any,
|
423
|
+
root_path: KeyPath,
|
424
|
+
parent: Any,
|
425
|
+
collapse_level: Optional[int] = 0,
|
426
|
+
uncollapse: Union[
|
427
|
+
Set[Union[KeyPath, str]], base.NodeFilter, None
|
428
|
+
] = None,
|
429
|
+
) -> bool:
|
430
|
+
"""Returns whether the object should be collapsed."""
|
431
|
+
if collapse_level is None or root_path.depth < collapse_level:
|
432
|
+
return False
|
433
|
+
if uncollapse is None:
|
434
|
+
return True
|
435
|
+
elif callable(uncollapse):
|
436
|
+
return not uncollapse(root_path, value, parent)
|
437
|
+
else:
|
438
|
+
return root_path not in uncollapse
|
439
|
+
|
440
|
+
def needs_summary(
|
441
|
+
self,
|
442
|
+
value: Any,
|
443
|
+
*,
|
444
|
+
name: Optional[str] = None,
|
445
|
+
parent: Any = None,
|
446
|
+
title: Union[str, Html, None] = None,
|
447
|
+
enable_summary: Optional[bool] = HtmlView.PresetArgValue(True),
|
448
|
+
max_summary_len_for_str: int = HtmlView.PresetArgValue(40),
|
449
|
+
) -> bool:
|
450
|
+
"""Returns whether the object needs a summary."""
|
451
|
+
del parent
|
452
|
+
if enable_summary is None:
|
453
|
+
if name is not None or title is not None or not (
|
454
|
+
isinstance(value, (int, float, bool, type(None)))
|
455
|
+
or (
|
456
|
+
isinstance(value, str)
|
457
|
+
and len(value) <= max_summary_len_for_str
|
458
|
+
)
|
459
|
+
):
|
460
|
+
return True
|
461
|
+
return enable_summary
|
462
|
+
|
463
|
+
@HtmlView.extension_method('_html_tree_view_summary')
|
464
|
+
def summary(
|
465
|
+
self,
|
466
|
+
value: Any,
|
467
|
+
*,
|
468
|
+
name: Optional[str] = None,
|
469
|
+
parent: Any = None,
|
470
|
+
root_path: Optional[KeyPath] = None,
|
471
|
+
css_class: Optional[Sequence[str]] = None,
|
472
|
+
title: Union[str, Html, None] = None,
|
473
|
+
enable_summary: Optional[bool] = HtmlView.PresetArgValue(None),
|
474
|
+
enable_summary_tooltip: bool = HtmlView.PresetArgValue(True),
|
475
|
+
max_summary_len_for_str: int = HtmlView.PresetArgValue(40),
|
476
|
+
**kwargs
|
477
|
+
) -> Optional[Html]:
|
478
|
+
"""Renders a summary for the value.
|
479
|
+
|
480
|
+
Args:
|
481
|
+
value: The value to render.
|
482
|
+
name: The name of the value.
|
483
|
+
parent: The parent of the value.
|
484
|
+
root_path: The root path of the value.
|
485
|
+
css_class: The CSS classes to add to the root element
|
486
|
+
title: The title of the summary.
|
487
|
+
enable_summary: Whether to enable the summary. If None, summary will
|
488
|
+
be enabled for complex types or when string exceeds
|
489
|
+
`max_summary_len_for_str`.
|
490
|
+
enable_summary_tooltip: Whether to enable the tooltip for the summary.
|
491
|
+
max_summary_len_for_str: The maximum length of the string to display.
|
492
|
+
**kwargs: Additional keyword arguments passed from `pg.to_html`.
|
493
|
+
|
494
|
+
Returns:
|
495
|
+
An optional HTML object representing the summary of the value. If None,
|
496
|
+
the summary will be hidden.
|
497
|
+
"""
|
498
|
+
if not self.needs_summary(
|
499
|
+
value,
|
500
|
+
name=name,
|
501
|
+
parent=parent,
|
502
|
+
title=title,
|
503
|
+
max_summary_len_for_str=max_summary_len_for_str,
|
504
|
+
enable_summary=enable_summary,
|
505
|
+
):
|
506
|
+
return None
|
507
|
+
|
508
|
+
def make_title(value: Any):
|
509
|
+
if isinstance(value, str):
|
510
|
+
if len(value) > max_summary_len_for_str:
|
511
|
+
value = value[:max_summary_len_for_str] + '...'
|
512
|
+
return Html.escape(repr(value))
|
513
|
+
return f'{type(value).__name__}(...)'
|
514
|
+
|
515
|
+
return Html.element(
|
516
|
+
'summary',
|
517
|
+
[
|
518
|
+
# Summary name.
|
519
|
+
lambda: Html.element( # pylint: disable=g-long-ternary
|
520
|
+
'div',
|
521
|
+
[
|
522
|
+
name,
|
523
|
+
],
|
524
|
+
css_class=['summary_name']
|
525
|
+
) if name else None,
|
526
|
+
|
527
|
+
# Summary title
|
528
|
+
Html.element(
|
529
|
+
'div',
|
530
|
+
[
|
531
|
+
title or make_title(value),
|
532
|
+
],
|
533
|
+
css_class=['summary_title']
|
534
|
+
),
|
535
|
+
|
536
|
+
# Tooltip.
|
537
|
+
lambda: self.tooltip( # pylint: disable=g-long-ternary
|
538
|
+
value,
|
539
|
+
name=name,
|
540
|
+
parent=parent,
|
541
|
+
root_path=root_path,
|
542
|
+
**kwargs,
|
543
|
+
) if enable_summary_tooltip else None,
|
544
|
+
],
|
545
|
+
css_class=css_class,
|
546
|
+
).add_style(
|
547
|
+
"""
|
548
|
+
/* Summary styles. */
|
549
|
+
details.pyglove summary {
|
550
|
+
font-weight: bold;
|
551
|
+
margin: -0.5em -0.5em 0;
|
552
|
+
padding: 0.5em;
|
553
|
+
}
|
554
|
+
.summary_name {
|
555
|
+
display: inline;
|
556
|
+
padding: 0 5px;
|
557
|
+
}
|
558
|
+
.summary_title {
|
559
|
+
display: inline;
|
560
|
+
}
|
561
|
+
.summary_name + div.summary_title {
|
562
|
+
display: inline;
|
563
|
+
color: #aaa;
|
564
|
+
}
|
565
|
+
.summary_title:hover + span.tooltip {
|
566
|
+
visibility: visible;
|
567
|
+
}
|
568
|
+
/* Type-specific styles. */
|
569
|
+
.pyglove.str .summary_title {
|
570
|
+
color: darkred;
|
571
|
+
font-style: italic;
|
572
|
+
}
|
573
|
+
"""
|
574
|
+
)
|
575
|
+
|
576
|
+
# NOTE(daiyip)" `object_key`` does not have a corresponding extension
|
577
|
+
# method in `HtmlTreeView.Extension`, because the rendering of the key is not
|
578
|
+
# delegated to `HtmlTreeView.Extension`.
|
579
|
+
def object_key(
|
580
|
+
self,
|
581
|
+
key: Union[str, int],
|
582
|
+
*,
|
583
|
+
name: Optional[str] = None,
|
584
|
+
parent: Any,
|
585
|
+
root_path: Optional[KeyPath] = None,
|
586
|
+
css_class: Optional[Sequence[str]] = None,
|
587
|
+
key_color: Optional[str] = None,
|
588
|
+
enable_tooltip: bool = HtmlView.PresetArgValue(True),
|
589
|
+
**kwargs
|
590
|
+
) -> Html:
|
591
|
+
"""Renders the key for the value.
|
592
|
+
|
593
|
+
Args:
|
594
|
+
key: The key of the value.
|
595
|
+
name: The name of the value.
|
596
|
+
parent: The parent value of the key.
|
597
|
+
root_path: The root path of the value.
|
598
|
+
css_class: The CSS classes to add to the root element.
|
599
|
+
key_color: The color of the key.
|
600
|
+
enable_tooltip: Whether to enable the tooltip.
|
601
|
+
**kwargs: Additional keyword arguments passed from `pg.to_html`.
|
602
|
+
|
603
|
+
Returns:
|
604
|
+
The rendered HTML as the key of the value.
|
605
|
+
"""
|
606
|
+
return (
|
607
|
+
# Key span.
|
608
|
+
Html.element(
|
609
|
+
'span',
|
610
|
+
[
|
611
|
+
str(key),
|
612
|
+
],
|
613
|
+
css_class=['object_key'] + (css_class or []),
|
614
|
+
style=dict(
|
615
|
+
color=key_color,
|
616
|
+
)
|
617
|
+
) + (
|
618
|
+
# Tooltip if enabled.
|
619
|
+
lambda: self.tooltip( # pylint: disable=g-long-ternary
|
620
|
+
value=root_path,
|
621
|
+
root_path=root_path,
|
622
|
+
name=name,
|
623
|
+
parent=parent,
|
624
|
+
**kwargs
|
625
|
+
) if enable_tooltip else None
|
626
|
+
)
|
627
|
+
).add_style(
|
628
|
+
"""
|
629
|
+
/* Object key styles. */
|
630
|
+
.object_key {
|
631
|
+
margin-right: 0.25em;
|
632
|
+
}
|
633
|
+
.object_key:hover + .tooltip {
|
634
|
+
visibility: visible;
|
635
|
+
background-color: darkblue;
|
636
|
+
}
|
637
|
+
.complex_value .object_key{
|
638
|
+
color: gray;
|
639
|
+
border: 1px solid lightgray;
|
640
|
+
background-color: ButtonFace;
|
641
|
+
border-radius: 0.2em;
|
642
|
+
padding: 0.3em;
|
643
|
+
}
|
644
|
+
.complex_value.list .object_key{
|
645
|
+
border: 0;
|
646
|
+
color: lightgray;
|
647
|
+
background-color: transparent;
|
648
|
+
border-radius: 0;
|
649
|
+
padding: 0;
|
650
|
+
}
|
651
|
+
.complex_value.list .object_key::before{
|
652
|
+
content: '[';
|
653
|
+
}
|
654
|
+
.complex_value.list .object_key::after{
|
655
|
+
content: ']';
|
656
|
+
}
|
657
|
+
"""
|
658
|
+
)
|
659
|
+
|
660
|
+
@HtmlView.extension_method('_html_tree_view_content')
|
661
|
+
def content(
|
662
|
+
self,
|
663
|
+
value: Any,
|
664
|
+
*,
|
665
|
+
name: Optional[str] = None,
|
666
|
+
parent: Any = None,
|
667
|
+
root_path: Optional[KeyPath] = None,
|
668
|
+
css_class: Optional[Sequence[str]] = None,
|
669
|
+
special_keys: Optional[Sequence[Union[int, str]]] = None,
|
670
|
+
include_keys: Optional[Iterable[Union[int, str]]] = None,
|
671
|
+
exclude_keys: Optional[Iterable[Union[int, str]]] = None,
|
672
|
+
filter: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None), # pylint: disable=redefined-builtin
|
673
|
+
highlight: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None),
|
674
|
+
lowlight: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None),
|
675
|
+
max_summary_len_for_str: int = HtmlView.PresetArgValue(40),
|
676
|
+
enable_summary_tooltip: bool = HtmlView.PresetArgValue(True),
|
677
|
+
enable_key_tooltip: bool = HtmlView.PresetArgValue(True),
|
678
|
+
collapse_level: Optional[int] = HtmlView.PresetArgValue(1),
|
679
|
+
uncollapse: Union[
|
680
|
+
Iterable[Union[KeyPath, str]], base.NodeFilter, None
|
681
|
+
] = HtmlView.PresetArgValue(None),
|
682
|
+
**kwargs
|
683
|
+
) -> Html:
|
684
|
+
"""Renders the main content for the value.
|
685
|
+
|
686
|
+
Args:
|
687
|
+
value: The value to render.
|
688
|
+
name: The name of the value.
|
689
|
+
parent: The parent of the value.
|
690
|
+
root_path: The root path of the value.
|
691
|
+
css_class: Additional CSS classes for root element.
|
692
|
+
special_keys: The special keys to display (at the immediate child level).
|
693
|
+
include_keys: The keys to include (at the immediate child level).
|
694
|
+
exclude_keys: The keys to exclude (at the immediate child level).
|
695
|
+
filter: A function with signature (path, value, parent) -> include
|
696
|
+
to determine whether to include a field (at all levels).
|
697
|
+
highlight: A function with signature (path, value, parent) -> bool
|
698
|
+
to determine whether to highlight a field (at all levels).
|
699
|
+
lowlight: A function with signature (path, value, parent) -> bool
|
700
|
+
to determine whether to lowlight a field (at all levels).
|
701
|
+
max_summary_len_for_str: The maximum length of the string to display.
|
702
|
+
enable_summary_tooltip: Whether to enable the summary tooltip.
|
703
|
+
enable_key_tooltip: Whether to enable the key tooltip.
|
704
|
+
collapse_level: The level of the tree to collapse.
|
705
|
+
uncollapse: The keys to uncollapse.
|
706
|
+
**kwargs: Additional keyword arguments passed from `pg.to_html`.
|
707
|
+
|
708
|
+
Returns:
|
709
|
+
The rendered HTML as the main content of the value.
|
710
|
+
"""
|
711
|
+
if isinstance(value, (tuple, list)):
|
712
|
+
items = {i: v for i, v in enumerate(value)}
|
713
|
+
elif isinstance(value, dict):
|
714
|
+
items = value
|
715
|
+
else:
|
716
|
+
return self.simple_value(
|
717
|
+
value, name=name, parent=parent, root_path=root_path,
|
718
|
+
css_class=css_class, max_summary_len_for_str=max_summary_len_for_str
|
719
|
+
)
|
720
|
+
return self.complex_value(
|
721
|
+
items,
|
722
|
+
name=name,
|
723
|
+
parent=value,
|
724
|
+
root_path=root_path or KeyPath(),
|
725
|
+
css_class=css_class,
|
726
|
+
special_keys=special_keys,
|
727
|
+
include_keys=include_keys,
|
728
|
+
exclude_keys=exclude_keys,
|
729
|
+
filter=filter,
|
730
|
+
highlight=highlight,
|
731
|
+
lowlight=lowlight,
|
732
|
+
max_summary_len_for_str=max_summary_len_for_str,
|
733
|
+
enable_summary_tooltip=enable_summary_tooltip,
|
734
|
+
enable_key_tooltip=enable_key_tooltip,
|
735
|
+
collapse_level=collapse_level,
|
736
|
+
uncollapse=uncollapse,
|
737
|
+
**kwargs,
|
738
|
+
)
|
739
|
+
|
740
|
+
def simple_value(
|
741
|
+
self,
|
742
|
+
value: Any,
|
743
|
+
*,
|
744
|
+
name: Optional[str] = None,
|
745
|
+
parent: Any = None,
|
746
|
+
root_path: Optional[KeyPath] = None,
|
747
|
+
css_class: Optional[Sequence[str]] = None,
|
748
|
+
max_summary_len_for_str: int = HtmlView.PresetArgValue(40),
|
749
|
+
) -> Html:
|
750
|
+
"""Renders a simple value.
|
751
|
+
|
752
|
+
Args:
|
753
|
+
value: The value to render.
|
754
|
+
name: The name of the value.
|
755
|
+
parent: The parent of the value.
|
756
|
+
root_path: The root path of the value.
|
757
|
+
css_class: Additional CSS classes for root span.
|
758
|
+
max_summary_len_for_str: The maximum length of the string to display.
|
759
|
+
|
760
|
+
Returns:
|
761
|
+
The rendered HTML as the simple value.
|
762
|
+
"""
|
763
|
+
del name, parent, root_path
|
764
|
+
def value_repr() -> str:
|
765
|
+
if isinstance(value, str):
|
766
|
+
if len(value) < max_summary_len_for_str:
|
767
|
+
return repr(value)
|
768
|
+
else:
|
769
|
+
return value
|
770
|
+
return object_utils.format(
|
771
|
+
value,
|
772
|
+
compact=False, verbose=False, hide_default_values=True,
|
773
|
+
python_format=True, use_inferred=True,
|
774
|
+
max_bytes_len=64,
|
775
|
+
)
|
776
|
+
return Html.element(
|
777
|
+
'span',
|
778
|
+
[
|
779
|
+
Html.escape(value_repr),
|
780
|
+
],
|
781
|
+
css_class=['simple_value', self.css_class_name(value)] + (
|
782
|
+
css_class or []
|
783
|
+
),
|
784
|
+
).add_style(
|
785
|
+
"""
|
786
|
+
/* Simple value styles. */
|
787
|
+
.simple_value {
|
788
|
+
color: blue;
|
789
|
+
display: inline-block;
|
790
|
+
white-space: pre-wrap;
|
791
|
+
padding: 0.2em;
|
792
|
+
margin-top: 0.15em;
|
793
|
+
}
|
794
|
+
.simple_value.str {
|
795
|
+
color: darkred;
|
796
|
+
font-style: italic;
|
797
|
+
}
|
798
|
+
.simple_value.int, .simple_value.float {
|
799
|
+
color: darkblue;
|
800
|
+
}
|
801
|
+
"""
|
802
|
+
)
|
803
|
+
|
804
|
+
def complex_value(
|
805
|
+
self,
|
806
|
+
kv: Dict[Union[int, str], Any],
|
807
|
+
*,
|
808
|
+
name: Optional[str] = None,
|
809
|
+
parent: Any = None,
|
810
|
+
root_path: Optional[KeyPath] = None,
|
811
|
+
css_class: Optional[Sequence[str]] = None,
|
812
|
+
special_keys: Optional[Sequence[Union[int, str]]] = None,
|
813
|
+
include_keys: Optional[Iterable[Union[int, str]]] = None,
|
814
|
+
exclude_keys: Optional[Iterable[Union[int, str]]] = None,
|
815
|
+
filter: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None), # pylint: disable=redefined-builtin
|
816
|
+
highlight: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None),
|
817
|
+
lowlight: Optional[base.NodeFilter] = HtmlView.PresetArgValue(None),
|
818
|
+
max_summary_len_for_str: int = HtmlView.PresetArgValue(40),
|
819
|
+
enable_summary_tooltip: bool = HtmlView.PresetArgValue(True),
|
820
|
+
enable_key_tooltip: bool = HtmlView.PresetArgValue(True),
|
821
|
+
collapse_level: Optional[int] = HtmlView.PresetArgValue(1),
|
822
|
+
uncollapse: Union[
|
823
|
+
Iterable[Union[KeyPath, str]], base.NodeFilter, None
|
824
|
+
] = HtmlView.PresetArgValue(None),
|
825
|
+
**kwargs,
|
826
|
+
) -> Html:
|
827
|
+
"""Renders a list of key-value pairs.
|
828
|
+
|
829
|
+
Args:
|
830
|
+
kv: The key-value pairs to render.
|
831
|
+
name: The name of the value.
|
832
|
+
parent: The parent value of the key-value pairs.
|
833
|
+
root_path: The root path of the value.
|
834
|
+
css_class: Additional CSS classes for root div.
|
835
|
+
special_keys: The special keys to display (at the immediate child level).
|
836
|
+
include_keys: The keys to include (at the immediate child level).
|
837
|
+
exclude_keys: The keys to exclude (at the immediate child level).
|
838
|
+
filter: A function with signature (path, value, parent) -> include
|
839
|
+
to determine whether to include.
|
840
|
+
highlight: A function with signature (path, value, parent) -> bool
|
841
|
+
to determine whether to highlight.
|
842
|
+
lowlight: A function with signature (path, value, parent) -> bool
|
843
|
+
to determine whether to lowlight.
|
844
|
+
max_summary_len_for_str: The maximum length of the string to display.
|
845
|
+
enable_summary_tooltip: Whether to enable the summary tooltip.
|
846
|
+
enable_key_tooltip: Whether to enable the key tooltip.
|
847
|
+
collapse_level: The level of the tree to collapse.
|
848
|
+
uncollapse: The keys to uncollapse.
|
849
|
+
**kwargs: Additional keyword arguments passed from `pg.to_html`.
|
850
|
+
|
851
|
+
Returns:
|
852
|
+
The rendered HTML as the key-value pairs.
|
853
|
+
"""
|
854
|
+
del name
|
855
|
+
special_keys = special_keys or []
|
856
|
+
include_keys = set(include_keys or [])
|
857
|
+
exclude_keys = set(exclude_keys or [])
|
858
|
+
uncollapse = self.normalize_uncollapse(uncollapse)
|
859
|
+
|
860
|
+
s = Html()
|
861
|
+
if kv:
|
862
|
+
include_keys = include_keys or set(kv.keys())
|
863
|
+
if filter is not None:
|
864
|
+
include_keys -= set(
|
865
|
+
k for k, v in kv.items()
|
866
|
+
if not filter(root_path + k, v, parent)
|
867
|
+
)
|
868
|
+
if exclude_keys:
|
869
|
+
include_keys -= exclude_keys
|
870
|
+
|
871
|
+
if special_keys:
|
872
|
+
for k in special_keys:
|
873
|
+
if k in include_keys and k in kv:
|
874
|
+
child_path = root_path + k
|
875
|
+
v = kv[k]
|
876
|
+
|
877
|
+
child_css_class = [
|
878
|
+
'special_value',
|
879
|
+
(
|
880
|
+
'highlight' if highlight
|
881
|
+
and highlight(child_path, v, parent) else None
|
882
|
+
),
|
883
|
+
(
|
884
|
+
'lowlight' if lowlight
|
885
|
+
and lowlight(child_path, v, parent) else None
|
886
|
+
)
|
887
|
+
]
|
888
|
+
s.write(
|
889
|
+
self.render(
|
890
|
+
value=v,
|
891
|
+
name=k,
|
892
|
+
parent=v,
|
893
|
+
root_path=child_path,
|
894
|
+
css_class=child_css_class,
|
895
|
+
filter=filter,
|
896
|
+
special_keys=None,
|
897
|
+
include_keys=None,
|
898
|
+
exclude_keys=None,
|
899
|
+
highlight=highlight,
|
900
|
+
lowlight=lowlight,
|
901
|
+
max_summary_len_for_str=max_summary_len_for_str,
|
902
|
+
enable_summary_tooltip=enable_summary_tooltip,
|
903
|
+
enable_key_tooltip=enable_key_tooltip,
|
904
|
+
collapse_level=collapse_level,
|
905
|
+
uncollapse=uncollapse,
|
906
|
+
**kwargs
|
907
|
+
)
|
908
|
+
)
|
909
|
+
include_keys.remove(k)
|
910
|
+
|
911
|
+
if include_keys:
|
912
|
+
s.write('<table>')
|
913
|
+
for k, v in kv.items():
|
914
|
+
if k not in include_keys:
|
915
|
+
continue
|
916
|
+
child_path = root_path + k
|
917
|
+
key = self.object_key(
|
918
|
+
key=k, parent=v, root_path=child_path,
|
919
|
+
enable_tooltip=enable_key_tooltip,
|
920
|
+
)
|
921
|
+
child_css_class = [
|
922
|
+
(
|
923
|
+
'highlight' if highlight and highlight(child_path, v, parent)
|
924
|
+
else None
|
925
|
+
),
|
926
|
+
(
|
927
|
+
'lowlight' if lowlight and lowlight(child_path, v, parent)
|
928
|
+
else None
|
929
|
+
)
|
930
|
+
]
|
931
|
+
value = self.render(
|
932
|
+
value=v,
|
933
|
+
name=None,
|
934
|
+
parent=v,
|
935
|
+
root_path=child_path,
|
936
|
+
css_class=child_css_class,
|
937
|
+
special_keys=None,
|
938
|
+
include_keys=None,
|
939
|
+
exclude_keys=None,
|
940
|
+
filter=filter,
|
941
|
+
highlight=highlight,
|
942
|
+
lowlight=lowlight,
|
943
|
+
max_summary_len_for_str=max_summary_len_for_str,
|
944
|
+
enable_summary_tooltip=enable_summary_tooltip,
|
945
|
+
enable_key_tooltip=enable_key_tooltip,
|
946
|
+
collapse_level=collapse_level,
|
947
|
+
uncollapse=uncollapse,
|
948
|
+
**kwargs,
|
949
|
+
)
|
950
|
+
s.write(
|
951
|
+
Html.element(
|
952
|
+
'tr',
|
953
|
+
[
|
954
|
+
'<td>', key, '</td>',
|
955
|
+
'<td>', value, '</td>',
|
956
|
+
],
|
957
|
+
)
|
958
|
+
)
|
959
|
+
s.write('</table>')
|
960
|
+
else:
|
961
|
+
s.write(Html.element('span', css_class=['empty_container']))
|
962
|
+
return Html.element(
|
963
|
+
'div',
|
964
|
+
[s],
|
965
|
+
css_class=['complex_value', self.css_class_name(parent)] + (
|
966
|
+
css_class or []
|
967
|
+
),
|
968
|
+
).add_style(
|
969
|
+
"""
|
970
|
+
/* Complex value styles. */
|
971
|
+
span.empty_container::before {
|
972
|
+
content: '(empty)';
|
973
|
+
font-style: italic;
|
974
|
+
margin-left: 0.5em;
|
975
|
+
color: #aaa;
|
976
|
+
}
|
977
|
+
"""
|
978
|
+
)
|
979
|
+
|
980
|
+
@HtmlView.extension_method('_html_tree_view_tooltip')
|
981
|
+
def tooltip(
|
982
|
+
self,
|
983
|
+
value: Any,
|
984
|
+
*,
|
985
|
+
name: Optional[str] = None,
|
986
|
+
parent: Any = None,
|
987
|
+
root_path: Optional[KeyPath] = None,
|
988
|
+
css_class: Optional[Sequence[str]] = None,
|
989
|
+
content: Union[str, Html, None] = HtmlView.PresetArgValue(None),
|
990
|
+
**kwargs
|
991
|
+
) -> Html:
|
992
|
+
"""Renders a tooltip for the value.
|
993
|
+
|
994
|
+
Args:
|
995
|
+
value: The value to render.
|
996
|
+
name: The name of the value.
|
997
|
+
parent: The parent value of the key-value pairs.
|
998
|
+
root_path: The root path of the value.
|
999
|
+
css_class: Additional CSS classes for the tooltip span.
|
1000
|
+
content: The content of the tooltip. If None, the formatted value will be
|
1001
|
+
used as the content.
|
1002
|
+
**kwargs: Additional keyword arguments passed from `pg.to_html`.
|
1003
|
+
|
1004
|
+
Returns:
|
1005
|
+
The rendered HTML as the tooltip of the value.
|
1006
|
+
"""
|
1007
|
+
del name, parent
|
1008
|
+
if content is None:
|
1009
|
+
content = Html.escape(
|
1010
|
+
object_utils.format(
|
1011
|
+
value,
|
1012
|
+
root_path=root_path,
|
1013
|
+
compact=False,
|
1014
|
+
verbose=False,
|
1015
|
+
python_format=True,
|
1016
|
+
max_bytes_len=64,
|
1017
|
+
max_str_len=256,
|
1018
|
+
**kwargs
|
1019
|
+
)
|
1020
|
+
)
|
1021
|
+
return Html.element(
|
1022
|
+
'span',
|
1023
|
+
[content],
|
1024
|
+
css_class=['tooltip'] + (css_class or []),
|
1025
|
+
).add_style(
|
1026
|
+
"""
|
1027
|
+
/* Tooltip styles. */
|
1028
|
+
span.tooltip {
|
1029
|
+
visibility: hidden;
|
1030
|
+
white-space: pre-wrap;
|
1031
|
+
font-weight: normal;
|
1032
|
+
background-color: #484848;
|
1033
|
+
color: #fff;
|
1034
|
+
padding: 10px;
|
1035
|
+
border-radius: 6px;
|
1036
|
+
position: absolute;
|
1037
|
+
z-index: 1;
|
1038
|
+
}
|
1039
|
+
"""
|
1040
|
+
)
|
1041
|
+
|
1042
|
+
@staticmethod
|
1043
|
+
def css_class_name(value: Any) -> str:
|
1044
|
+
"""Returns the CSS class name for the value."""
|
1045
|
+
value = value if inspect.isclass(value) else type(value)
|
1046
|
+
class_name = value.__name__
|
1047
|
+
return _REGEX_CAMEL_TO_SNAKE.sub('-', class_name).lower()
|
1048
|
+
|
1049
|
+
|
1050
|
+
_REGEX_CAMEL_TO_SNAKE = re.compile(r'(?<!^)(?=[A-Z])')
|
1051
|
+
|
1052
|
+
# pytype: enable=annotation-type-mismatch
|