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