pyglove 0.4.5.dev202411030808__py3-none-any.whl → 0.4.5.dev202411050809__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.
Potentially problematic release.
This version of pyglove might be problematic. Click here for more details.
- pyglove/core/symbolic/base.py +2 -38
- pyglove/core/symbolic/diff.py +14 -13
- pyglove/core/symbolic/object_test.py +1 -0
- pyglove/core/symbolic/ref.py +7 -7
- pyglove/core/views/__init__.py +1 -0
- pyglove/core/views/base.py +14 -8
- pyglove/core/views/html/__init__.py +1 -0
- pyglove/core/views/html/base.py +52 -78
- pyglove/core/views/html/base_test.py +16 -3
- pyglove/core/views/html/tree_view.py +23 -5
- pyglove/core/views/html/tree_view_test.py +6 -0
- {pyglove-0.4.5.dev202411030808.dist-info → pyglove-0.4.5.dev202411050809.dist-info}/METADATA +1 -1
- {pyglove-0.4.5.dev202411030808.dist-info → pyglove-0.4.5.dev202411050809.dist-info}/RECORD +16 -16
- {pyglove-0.4.5.dev202411030808.dist-info → pyglove-0.4.5.dev202411050809.dist-info}/LICENSE +0 -0
- {pyglove-0.4.5.dev202411030808.dist-info → pyglove-0.4.5.dev202411050809.dist-info}/WHEEL +0 -0
- {pyglove-0.4.5.dev202411030808.dist-info → pyglove-0.4.5.dev202411050809.dist-info}/top_level.txt +0 -0
pyglove/core/symbolic/base.py
CHANGED
@@ -31,7 +31,7 @@ from pyglove.core.symbolic import flags
|
|
31
31
|
from pyglove.core.symbolic.origin import Origin
|
32
32
|
from pyglove.core.symbolic.pure_symbolic import NonDeterministic
|
33
33
|
from pyglove.core.symbolic.pure_symbolic import PureSymbolic
|
34
|
-
from pyglove.core.views import
|
34
|
+
from pyglove.core.views.html.base import HtmlConvertible
|
35
35
|
|
36
36
|
|
37
37
|
class WritePermissionError(Exception):
|
@@ -175,7 +175,7 @@ class Symbolic(
|
|
175
175
|
object_utils.Formattable,
|
176
176
|
object_utils.JSONConvertible,
|
177
177
|
object_utils.MaybePartial,
|
178
|
-
|
178
|
+
HtmlConvertible,
|
179
179
|
):
|
180
180
|
"""Base for all symbolic types.
|
181
181
|
|
@@ -949,42 +949,6 @@ class Symbolic(
|
|
949
949
|
"""Serializes current object into a JSON string."""
|
950
950
|
return to_json_str(self, json_indent=json_indent, **kwargs)
|
951
951
|
|
952
|
-
def _html_tree_view_content(
|
953
|
-
self,
|
954
|
-
*,
|
955
|
-
view: html.HtmlTreeView,
|
956
|
-
name: Optional[str] = None,
|
957
|
-
parent: Any = None,
|
958
|
-
root_path: Optional[object_utils.KeyPath] = None,
|
959
|
-
extra_flags: Optional[Dict[str, Any]],
|
960
|
-
**kwargs,
|
961
|
-
) -> html.Html:
|
962
|
-
"""Returns the content HTML for a symbolic object.."""
|
963
|
-
extra_flags = extra_flags or {}
|
964
|
-
hide_frozen = extra_flags.get('hide_frozen', True)
|
965
|
-
hide_default_values = extra_flags.get('hide_default_values', False)
|
966
|
-
use_inferred = extra_flags.get('use_inferred', False)
|
967
|
-
|
968
|
-
kv = {}
|
969
|
-
for k, v in self.sym_items():
|
970
|
-
# Apply frozen filter.
|
971
|
-
field = self.sym_attr_field(k)
|
972
|
-
if hide_frozen and field and field.frozen:
|
973
|
-
continue
|
974
|
-
|
975
|
-
# Apply inferred value.
|
976
|
-
if use_inferred and isinstance(v, Inferential):
|
977
|
-
v = self.sym_inferred(k, default=v)
|
978
|
-
|
979
|
-
# Apply default value filter.
|
980
|
-
if field and hide_default_values and eq(v, field.default_value):
|
981
|
-
continue
|
982
|
-
kv[k] = v
|
983
|
-
return view.complex_value(
|
984
|
-
kv, name=name, parent=self, root_path=root_path,
|
985
|
-
extra_flags=extra_flags, **kwargs
|
986
|
-
)
|
987
|
-
|
988
952
|
@classmethod
|
989
953
|
def load(cls, *args, **kwargs) -> Any:
|
990
954
|
"""Loads an instance of this type using the global load handler."""
|
pyglove/core/symbolic/diff.py
CHANGED
@@ -21,10 +21,10 @@ from pyglove.core.symbolic import base
|
|
21
21
|
from pyglove.core.symbolic import list as pg_list
|
22
22
|
from pyglove.core.symbolic import object as pg_object
|
23
23
|
from pyglove.core.symbolic.pure_symbolic import PureSymbolic
|
24
|
-
from pyglove.core.views import
|
24
|
+
from pyglove.core.views.html import tree_view
|
25
25
|
|
26
26
|
|
27
|
-
class Diff(PureSymbolic, pg_object.Object):
|
27
|
+
class Diff(PureSymbolic, pg_object.Object, tree_view.HtmlTreeView.Extension):
|
28
28
|
"""A value diff between two objects: a 'left' object and a 'right' object.
|
29
29
|
|
30
30
|
If one of them is missing, it may be represented by pg.Diff.MISSING
|
@@ -149,12 +149,12 @@ class Diff(PureSymbolic, pg_object.Object):
|
|
149
149
|
def _html_tree_view_summary(
|
150
150
|
self,
|
151
151
|
*,
|
152
|
-
view:
|
152
|
+
view: tree_view.HtmlTreeView,
|
153
153
|
css_classes: Optional[Sequence[str]] = None,
|
154
154
|
title: Optional[str] = None,
|
155
155
|
max_summary_len_for_str: int = 80,
|
156
156
|
**kwargs,
|
157
|
-
) -> Optional[
|
157
|
+
) -> Optional[tree_view.Html]:
|
158
158
|
# pytype: enable=annotation-type-mismatch
|
159
159
|
if not bool(self):
|
160
160
|
v = self.value
|
@@ -197,15 +197,16 @@ class Diff(PureSymbolic, pg_object.Object):
|
|
197
197
|
def _html_tree_view_content(
|
198
198
|
self,
|
199
199
|
*,
|
200
|
-
view:
|
201
|
-
parent: Any,
|
202
|
-
root_path: object_utils.KeyPath,
|
200
|
+
view: tree_view.HtmlTreeView,
|
201
|
+
parent: Any = None,
|
202
|
+
root_path: Optional[object_utils.KeyPath] = None,
|
203
203
|
css_classes: Optional[Sequence[str]] = None,
|
204
204
|
**kwargs
|
205
|
-
) ->
|
205
|
+
) -> tree_view.Html:
|
206
|
+
root_path = root_path or object_utils.KeyPath()
|
206
207
|
if not bool(self):
|
207
208
|
if self.value == Diff.MISSING:
|
208
|
-
root =
|
209
|
+
root = tree_view.Html.element(
|
209
210
|
'span',
|
210
211
|
# CSS class already defined in HtmlTreeView.
|
211
212
|
css_classes=['diff-empty']
|
@@ -219,7 +220,7 @@ class Diff(PureSymbolic, pg_object.Object):
|
|
219
220
|
**kwargs,
|
220
221
|
)
|
221
222
|
elif self.is_leaf:
|
222
|
-
root =
|
223
|
+
root = tree_view.Html.element(
|
223
224
|
'div',
|
224
225
|
[
|
225
226
|
view.render( # pylint: disable=g-long-ternary
|
@@ -242,7 +243,7 @@ class Diff(PureSymbolic, pg_object.Object):
|
|
242
243
|
else:
|
243
244
|
key_fn = lambda k: k
|
244
245
|
|
245
|
-
s =
|
246
|
+
s = tree_view.Html()
|
246
247
|
for k, v in self.children.items():
|
247
248
|
k = key_fn(k)
|
248
249
|
child_path = root_path + k
|
@@ -263,7 +264,7 @@ class Diff(PureSymbolic, pg_object.Object):
|
|
263
264
|
),
|
264
265
|
'</td></tr>'
|
265
266
|
)
|
266
|
-
root =
|
267
|
+
root = tree_view.Html.element(
|
267
268
|
'div',
|
268
269
|
[
|
269
270
|
'<table>', s, '</table>'
|
@@ -275,7 +276,7 @@ class Diff(PureSymbolic, pg_object.Object):
|
|
275
276
|
return root
|
276
277
|
|
277
278
|
def _html_tree_view_config(self) -> dict[str, Any]:
|
278
|
-
return
|
279
|
+
return tree_view.HtmlTreeView.get_kwargs(
|
279
280
|
super()._html_tree_view_config(),
|
280
281
|
dict(
|
281
282
|
css_classes=[
|
@@ -39,6 +39,7 @@ from pyglove.core.symbolic.object import use_init_args as pg_use_init_args
|
|
39
39
|
from pyglove.core.symbolic.origin import Origin
|
40
40
|
from pyglove.core.symbolic.pure_symbolic import NonDeterministic
|
41
41
|
from pyglove.core.symbolic.pure_symbolic import PureSymbolic
|
42
|
+
from pyglove.core.views.html import tree_view # pylint: disable=unused-import
|
42
43
|
|
43
44
|
|
44
45
|
MISSING_VALUE = object_utils.MISSING_VALUE
|
pyglove/core/symbolic/ref.py
CHANGED
@@ -20,10 +20,10 @@ from pyglove.core import object_utils
|
|
20
20
|
from pyglove.core import typing as pg_typing
|
21
21
|
from pyglove.core.symbolic import base
|
22
22
|
from pyglove.core.symbolic.object import Object
|
23
|
-
from pyglove.core.views import
|
23
|
+
from pyglove.core.views.html import tree_view
|
24
24
|
|
25
25
|
|
26
|
-
class Ref(Object, base.Inferential):
|
26
|
+
class Ref(Object, base.Inferential, tree_view.HtmlTreeView.Extension):
|
27
27
|
"""Symbolic reference.
|
28
28
|
|
29
29
|
When adding a symbolic node to a symbolic tree, it undergoes a copy operation
|
@@ -167,17 +167,17 @@ class Ref(Object, base.Inferential):
|
|
167
167
|
def _html_tree_view_content(
|
168
168
|
self,
|
169
169
|
*,
|
170
|
-
view:
|
171
|
-
**kwargs: Any) ->
|
170
|
+
view: tree_view.HtmlTreeView,
|
171
|
+
**kwargs: Any) -> tree_view.Html:
|
172
172
|
"""Overrides `_html_content` to render the referenced value."""
|
173
173
|
return view.content(self._value, **kwargs)
|
174
174
|
|
175
175
|
def _html_tree_view_summary(
|
176
176
|
self,
|
177
177
|
*,
|
178
|
-
view:
|
178
|
+
view: tree_view.HtmlTreeView,
|
179
179
|
title: Optional[str] = None,
|
180
|
-
**kwargs: Any) -> Optional[
|
180
|
+
**kwargs: Any) -> Optional[tree_view.Html]:
|
181
181
|
"""Overrides `_html_content` to render the referenced value."""
|
182
182
|
return view.summary(
|
183
183
|
self,
|
@@ -188,7 +188,7 @@ class Ref(Object, base.Inferential):
|
|
188
188
|
@classmethod
|
189
189
|
@functools.cache
|
190
190
|
def _html_tree_view_config(cls) -> dict[str, Any]:
|
191
|
-
return
|
191
|
+
return tree_view.HtmlTreeView.get_kwargs(
|
192
192
|
super()._html_tree_view_config(),
|
193
193
|
dict(
|
194
194
|
css_classes=['ref'],
|
pyglove/core/views/__init__.py
CHANGED
pyglove/core/views/base.py
CHANGED
@@ -272,11 +272,10 @@ class Content(object_utils.Formattable, metaclass=abc.ABCMeta):
|
|
272
272
|
self._shared_parts = shared_parts
|
273
273
|
|
274
274
|
for c in content:
|
275
|
+
c = self._to_content(c)
|
275
276
|
if c is None:
|
276
277
|
continue
|
277
|
-
|
278
|
-
c = c()
|
279
|
-
if isinstance(c, str):
|
278
|
+
elif isinstance(c, str):
|
280
279
|
self._content_stream.write(c)
|
281
280
|
else:
|
282
281
|
self.write(c)
|
@@ -306,9 +305,7 @@ class Content(object_utils.Formattable, metaclass=abc.ABCMeta):
|
|
306
305
|
"""
|
307
306
|
content_updated = False
|
308
307
|
for p in parts:
|
309
|
-
|
310
|
-
p = p()
|
311
|
-
|
308
|
+
p = self._to_content(p)
|
312
309
|
if p is None:
|
313
310
|
continue
|
314
311
|
|
@@ -342,8 +339,7 @@ class Content(object_utils.Formattable, metaclass=abc.ABCMeta):
|
|
342
339
|
|
343
340
|
def __add__(self, other: WritableTypes) -> 'Content':
|
344
341
|
"""Operator +: Concatenates two Content objects."""
|
345
|
-
|
346
|
-
other = other()
|
342
|
+
other = self._to_content(other)
|
347
343
|
if not other:
|
348
344
|
return self
|
349
345
|
s = copy_lib.deepcopy(self)
|
@@ -416,6 +412,16 @@ class Content(object_utils.Formattable, metaclass=abc.ABCMeta):
|
|
416
412
|
return value
|
417
413
|
return cls(value) # pytype: disable=not-instantiable
|
418
414
|
|
415
|
+
@classmethod
|
416
|
+
def _to_content(cls, value: WritableTypes) -> Union['Content', str, None]:
|
417
|
+
"""Returns a Content object or None from a writable type."""
|
418
|
+
if callable(value):
|
419
|
+
value = value()
|
420
|
+
if value is None:
|
421
|
+
return None
|
422
|
+
assert isinstance(value, (str, cls)), value
|
423
|
+
return value
|
424
|
+
|
419
425
|
|
420
426
|
def view(
|
421
427
|
value: Any,
|
@@ -17,6 +17,7 @@
|
|
17
17
|
# pylint: disable=g-bad-import-order
|
18
18
|
|
19
19
|
from pyglove.core.views.html.base import Html
|
20
|
+
from pyglove.core.views.html.base import HtmlConvertible
|
20
21
|
from pyglove.core.views.html.base import HtmlView
|
21
22
|
from pyglove.core.views.html.base import to_html
|
22
23
|
from pyglove.core.views.html.base import to_html_str
|
pyglove/core/views/html/base.py
CHANGED
@@ -86,7 +86,8 @@ class Html(base.Content):
|
|
86
86
|
WritableTypes = Union[ # pylint: disable=invalid-name
|
87
87
|
str,
|
88
88
|
'Html',
|
89
|
-
|
89
|
+
'HtmlConvertible',
|
90
|
+
Callable[[], Union[str, 'Html', 'HtmlConvertible', None]],
|
90
91
|
None
|
91
92
|
]
|
92
93
|
|
@@ -274,6 +275,20 @@ class Html(base.Content):
|
|
274
275
|
Html, super().from_value(value, copy=copy)
|
275
276
|
)
|
276
277
|
|
278
|
+
@classmethod
|
279
|
+
def _to_content(
|
280
|
+
cls, value: WritableTypes
|
281
|
+
) -> Union['Html', str, None]:
|
282
|
+
if callable(value):
|
283
|
+
value = value()
|
284
|
+
if value is None:
|
285
|
+
return None
|
286
|
+
elif isinstance(value, HtmlConvertible):
|
287
|
+
value = value.to_html()
|
288
|
+
elif not isinstance(value, (str, cls)):
|
289
|
+
raise TypeError(f'Not a writable value for `{cls.__name__}`: {value!r}')
|
290
|
+
return value
|
291
|
+
|
277
292
|
#
|
278
293
|
# Helper methods for creating templated Html objects.
|
279
294
|
#
|
@@ -337,13 +352,15 @@ class Html(base.Content):
|
|
337
352
|
cls,
|
338
353
|
s: WritableTypes,
|
339
354
|
javascript_str: bool = False
|
340
|
-
) ->
|
355
|
+
) -> Union[str, 'Html', None]:
|
341
356
|
"""Escapes an HTML writable object."""
|
342
357
|
if s is None:
|
343
358
|
return None
|
344
359
|
|
345
360
|
if callable(s):
|
346
361
|
s = s()
|
362
|
+
if isinstance(s, HtmlConvertible):
|
363
|
+
s = s.to_html()
|
347
364
|
|
348
365
|
def _escape(s: str) -> str:
|
349
366
|
if javascript_str:
|
@@ -413,87 +430,27 @@ class Html(base.Content):
|
|
413
430
|
pg_typing.register_converter(str, Html, convert_fn=Html.from_value)
|
414
431
|
|
415
432
|
|
416
|
-
class
|
417
|
-
"""Base class for HTML
|
433
|
+
class HtmlConvertible:
|
434
|
+
"""Base class for HTML convertible objects."""
|
418
435
|
|
419
|
-
|
420
|
-
"""
|
436
|
+
def to_html(self, **kwargs) -> Html:
|
437
|
+
"""Returns the HTML representation of the object."""
|
438
|
+
return to_html(self, **kwargs)
|
421
439
|
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
Subclasses can override this method to add additional CSS style to the
|
426
|
-
rendered HTML.
|
427
|
-
"""
|
428
|
-
return []
|
429
|
-
|
430
|
-
def _html_element_class(self) -> List[str]:
|
431
|
-
"""Returns the CSS classes for the rendered element for this node.
|
432
|
-
|
433
|
-
Subclasses can override this method to add CSS classes to the
|
434
|
-
rendered element of this object.
|
435
|
-
"""
|
436
|
-
return [
|
437
|
-
object_utils.camel_to_snake(self.__class__.__name__, '-')
|
438
|
-
]
|
439
|
-
|
440
|
-
def to_html(
|
441
|
-
self,
|
442
|
-
*,
|
443
|
-
name: Optional[str] = None,
|
444
|
-
root_path: Optional[object_utils.KeyPath] = None,
|
445
|
-
view_id: str = 'html-tree-view',
|
446
|
-
**kwargs
|
447
|
-
) -> Html:
|
448
|
-
"""Returns the HTML representation of the object.
|
449
|
-
|
450
|
-
Args:
|
451
|
-
name: The name of the object.
|
452
|
-
root_path: The root path of the object.
|
453
|
-
view_id: The ID of the view to render the value.
|
454
|
-
See `pg.views.HtmlView.dir()` for all available HTML view IDs.
|
455
|
-
**kwargs: View-specific keyword arguments passed to `pg.to_html`, wich
|
456
|
-
will be used to construct/override `HtmlView` settings.
|
457
|
-
|
458
|
-
Returns:
|
459
|
-
An rendered HTML.
|
460
|
-
"""
|
461
|
-
return to_html(
|
462
|
-
self, name=name, root_path=root_path, view_id=view_id, **kwargs
|
463
|
-
)
|
440
|
+
def to_html_str(self, *, content_only: bool = False, **kwargs) -> str:
|
441
|
+
"""Returns the HTML str of the object."""
|
442
|
+
return self.to_html(**kwargs).to_str(content_only=content_only)
|
464
443
|
|
465
|
-
|
466
|
-
|
467
|
-
*,
|
468
|
-
name: Optional[str] = None,
|
469
|
-
root_path: Optional[object_utils.KeyPath] = None,
|
470
|
-
view_id: str = 'html-tree-view',
|
471
|
-
content_only: bool = False,
|
472
|
-
**kwargs
|
473
|
-
) -> str:
|
474
|
-
"""Returns the HTML str of the object.
|
475
|
-
|
476
|
-
Args:
|
477
|
-
name: The name of the object.
|
478
|
-
root_path: The root path of the object.
|
479
|
-
view_id: The ID of the view to render the value.
|
480
|
-
See `pg.views.HtmlView.dir()` for all available HTML view IDs.
|
481
|
-
content_only: If True, only the content will be returned.
|
482
|
-
**kwargs: View-specific keyword arguments passed to `pg.to_html`, wich
|
483
|
-
will be used to construct/override `HtmlView` settings.
|
484
|
-
|
485
|
-
Returns:
|
486
|
-
An rendered HTML str.
|
487
|
-
"""
|
488
|
-
return to_html_str(
|
489
|
-
self, name=name, root_path=root_path,
|
490
|
-
view_id=view_id, content_only=content_only, **kwargs
|
491
|
-
)
|
444
|
+
def _repr_html_(self) -> str:
|
445
|
+
return self.to_html_str()
|
492
446
|
|
493
|
-
def _repr_html_(self) -> str:
|
494
|
-
return self.to_html_str()
|
495
447
|
|
496
|
-
|
448
|
+
class HtmlView(base.View):
|
449
|
+
"""Base class for HTML views."""
|
450
|
+
|
451
|
+
class Extension(base.View.Extension, HtmlConvertible):
|
452
|
+
"""Base class for HtmlView extensions."""
|
453
|
+
|
497
454
|
def render(
|
498
455
|
self,
|
499
456
|
value: Any,
|
@@ -503,6 +460,23 @@ class HtmlView(base.View):
|
|
503
460
|
**kwargs
|
504
461
|
) -> Html:
|
505
462
|
"""Renders the input value into an HTML object."""
|
463
|
+
# For customized HtmlConvertible objects, call their `to_html()` method.
|
464
|
+
if (isinstance(value, HtmlConvertible)
|
465
|
+
and not isinstance(value, self.__class__.Extension)
|
466
|
+
and value.__class__.to_html is not HtmlConvertible.to_html):
|
467
|
+
return value.to_html(name=name, root_path=root_path, **kwargs)
|
468
|
+
return self._render(value, name=name, root_path=root_path, **kwargs)
|
469
|
+
|
470
|
+
@abc.abstractmethod
|
471
|
+
def _render(
|
472
|
+
self,
|
473
|
+
value: Any,
|
474
|
+
*,
|
475
|
+
name: Optional[str] = None,
|
476
|
+
root_path: Optional[object_utils.KeyPath] = None,
|
477
|
+
**kwargs
|
478
|
+
) -> Html:
|
479
|
+
"""View's implementation of HTML rendering."""
|
506
480
|
|
507
481
|
|
508
482
|
def to_html(
|
@@ -422,6 +422,10 @@ class SharedPartTest(TestCase):
|
|
422
422
|
|
423
423
|
class HtmlTest(TestCase):
|
424
424
|
|
425
|
+
class Foo(base.HtmlConvertible):
|
426
|
+
def to_html(self, **kwargs):
|
427
|
+
return base.Html('<h1>foo</h1>')
|
428
|
+
|
425
429
|
def test_content_init(self):
|
426
430
|
html = Html()
|
427
431
|
self.assertEqual(html, Html())
|
@@ -446,6 +450,9 @@ class HtmlTest(TestCase):
|
|
446
450
|
)
|
447
451
|
self.assertEqual(html, Html('abcdefghi'))
|
448
452
|
|
453
|
+
html = Html(HtmlTest.Foo())
|
454
|
+
self.assertEqual(html, Html('<h1>foo</h1>'))
|
455
|
+
|
449
456
|
def test_basics(self):
|
450
457
|
html = Html(
|
451
458
|
'<h1>foo</h1>',
|
@@ -583,7 +590,7 @@ class HtmlTest(TestCase):
|
|
583
590
|
html2.write('<div class="c">bar</div>')
|
584
591
|
html2.write('\n<script>\nconsole.log("bar");\n</script>')
|
585
592
|
|
586
|
-
html1.write(
|
593
|
+
html1.write(HtmlTest.Foo())
|
587
594
|
html1.write('\n<script>\nconsole.log("foo");\n</script>\n')
|
588
595
|
html1.write(html2)
|
589
596
|
|
@@ -603,7 +610,7 @@ class HtmlTest(TestCase):
|
|
603
610
|
</script>
|
604
611
|
</head>
|
605
612
|
<body>
|
606
|
-
<
|
613
|
+
<h1>foo</h1>
|
607
614
|
<script>
|
608
615
|
console.log("foo");
|
609
616
|
</script>
|
@@ -618,7 +625,7 @@ class HtmlTest(TestCase):
|
|
618
625
|
self.assert_html(
|
619
626
|
html1.to_str(content_only=True),
|
620
627
|
"""
|
621
|
-
<
|
628
|
+
<h1>foo</h1>
|
622
629
|
<script>
|
623
630
|
console.log("foo");
|
624
631
|
</script>
|
@@ -641,6 +648,8 @@ class HtmlTest(TestCase):
|
|
641
648
|
html2 = Html.from_value(html, copy=True)
|
642
649
|
self.assertIsNot(html2, html)
|
643
650
|
self.assertEqual(html2, html)
|
651
|
+
html3 = Html.from_value(HtmlTest.Foo())
|
652
|
+
self.assertEqual(html3, Html('<h1>foo</h1>'))
|
644
653
|
|
645
654
|
with self.assertRaises(TypeError):
|
646
655
|
Html.from_value(1)
|
@@ -694,9 +703,13 @@ class HtmlTest(TestCase):
|
|
694
703
|
self.assertEqual(Html.escape('foo'), 'foo')
|
695
704
|
self.assertEqual(Html.escape('foo"bar'), 'foo"bar')
|
696
705
|
self.assertEqual(Html.escape(Html('foo"bar')), Html('foo"bar'))
|
706
|
+
self.assertEqual(Html.escape(HtmlTest.Foo()), Html('<h1>foo</h1>'))
|
697
707
|
self.assertEqual(Html.escape(lambda: 'foo"bar'), 'foo"bar')
|
698
708
|
self.assertEqual(Html.escape('"x=y"', javascript_str=True), '\\"x=y\\"')
|
699
709
|
self.assertEqual(Html.escape('x\n"', javascript_str=True), 'x\\n\\"')
|
710
|
+
self.assertEqual(
|
711
|
+
Html.escape(HtmlTest.Foo(), javascript_str=True), Html('<h1>foo</h1>')
|
712
|
+
)
|
700
713
|
|
701
714
|
def test_concate(self):
|
702
715
|
self.assertIsNone(Html.concate(None))
|
@@ -17,6 +17,7 @@ import inspect
|
|
17
17
|
from typing import Any, Callable, Dict, Iterable, Literal, Optional, Sequence, Tuple, Union
|
18
18
|
|
19
19
|
from pyglove.core import object_utils
|
20
|
+
from pyglove.core.symbolic import base as pg_symbolic
|
20
21
|
from pyglove.core.views.html import base
|
21
22
|
|
22
23
|
|
@@ -192,7 +193,7 @@ class HtmlTreeView(HtmlView):
|
|
192
193
|
# NOTE(daiyip): update `get_kwargs()` and `get_passthrough_kwargs()` when new
|
193
194
|
# arguments are added.
|
194
195
|
@HtmlView.extension_method('_html_tree_view_render')
|
195
|
-
def
|
196
|
+
def _render(
|
196
197
|
self,
|
197
198
|
value: Any,
|
198
199
|
*,
|
@@ -855,8 +856,27 @@ class HtmlTreeView(HtmlView):
|
|
855
856
|
The rendered HTML as the main content of the value.
|
856
857
|
"""
|
857
858
|
root_path = root_path or KeyPath()
|
858
|
-
|
859
|
-
|
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)):
|
860
880
|
items = {i: v for i, v in enumerate(value)}
|
861
881
|
elif isinstance(value, dict):
|
862
882
|
items = value
|
@@ -1278,8 +1298,6 @@ class HtmlTreeView(HtmlView):
|
|
1278
1298
|
@staticmethod
|
1279
1299
|
def css_class_name(value: Any) -> Optional[str]:
|
1280
1300
|
"""Returns the CSS class name for the value."""
|
1281
|
-
# if isinstance(value, HtmlTreeView.Extension):
|
1282
|
-
# return Html.concate(value._html_element_class()) # pylint: disable=protected-access
|
1283
1301
|
if inspect.isclass(value):
|
1284
1302
|
class_name = f'{value.__name__}-class'
|
1285
1303
|
else:
|
@@ -852,6 +852,12 @@ class ContentTest(TestCase):
|
|
852
852
|
|
853
853
|
class RenderTest(TestCase):
|
854
854
|
|
855
|
+
def test_render_html_convertible(self):
|
856
|
+
class Foo(base.HtmlConvertible):
|
857
|
+
def to_html(self, **kwargs):
|
858
|
+
return base.Html('<span>foo</span>')
|
859
|
+
self.assert_content(base.to_html(Foo()), '<span>foo</span>')
|
860
|
+
|
855
861
|
def test_render(self):
|
856
862
|
class Foo:
|
857
863
|
def __str__(self):
|
@@ -78,7 +78,7 @@ pyglove/core/patching/pattern_based_test.py,sha256=PW1EcVfsFPB6wtgwg3s4dzvigWn3b
|
|
78
78
|
pyglove/core/patching/rule_based.py,sha256=QJsDsYVpyfUoaTZeXZtAdFuxpvB1eniY0qYdZvVqzk0,17056
|
79
79
|
pyglove/core/patching/rule_based_test.py,sha256=qfy0ILmczV_LMHWEnwo2y079OrJsGYO0nKxSZdmIUcI,18782
|
80
80
|
pyglove/core/symbolic/__init__.py,sha256=jYq-LwR1Ql3iMChjz9lN-0heDKiocmxR0ZJFEJ8RHHQ,5787
|
81
|
-
pyglove/core/symbolic/base.py,sha256=
|
81
|
+
pyglove/core/symbolic/base.py,sha256=4xgxmMtmNe70Rhc7acC3mXO36WRxqG6YrDubb1f2TII,77473
|
82
82
|
pyglove/core/symbolic/base_test.py,sha256=YLMlQGF4TbxAB2PcN1-ckI70gRIVDxabv-goh43CV7A,7333
|
83
83
|
pyglove/core/symbolic/boilerplate.py,sha256=YO8ZTZJ3VfAqeHqJpIY_ORVDxc1XIzxdplNlpexWEyc,6000
|
84
84
|
pyglove/core/symbolic/boilerplate_test.py,sha256=1CZ1W6kq3l-3tpaknhGFa04V18bO7vPzis5qzWnxHEs,5252
|
@@ -88,7 +88,7 @@ pyglove/core/symbolic/compounding.py,sha256=zg6x09rtaSEb5v5pYJWWWpI5Cezy2Oy1iuWw
|
|
88
88
|
pyglove/core/symbolic/compounding_test.py,sha256=PSnk_ds0q6pRJrpN9teMwZtMrbUp5_P-D7ea7wRTK6k,8372
|
89
89
|
pyglove/core/symbolic/dict.py,sha256=gp2sn9nf7movorKWDQsbCQmj0PBdqFul0FYJLm5_kac,36839
|
90
90
|
pyglove/core/symbolic/dict_test.py,sha256=09kAEy_Kiwr8O59CIKK9a9KG-xxJJEIKbfEcZhs5KfU,70809
|
91
|
-
pyglove/core/symbolic/diff.py,sha256=
|
91
|
+
pyglove/core/symbolic/diff.py,sha256=UFjC6GnH_yzutuJHSao-di9qVeWL1amBYUJrEKmol3U,16351
|
92
92
|
pyglove/core/symbolic/diff_test.py,sha256=EDiGHqqKhi-NeMxr-bgjBEqlquee_4l_0IM6hgAb9Mg,29400
|
93
93
|
pyglove/core/symbolic/flags.py,sha256=GAF_QrthtDytO3DP61AgxWUgjBc89nnI4TJS4nZ_ng0,12097
|
94
94
|
pyglove/core/symbolic/flags_test.py,sha256=JDJcm6dYTlnktFsdNFjQszmHXf9bZnhrXMxi_jUiKUA,5483
|
@@ -99,11 +99,11 @@ pyglove/core/symbolic/inferred_test.py,sha256=G6uPykONcChvs6vZujXHSWaYfjewLTVBsc
|
|
99
99
|
pyglove/core/symbolic/list.py,sha256=63v4Ph0FdkoCDj1FjwcmjUHGZSJLBLxaTKcGg7PdghE,30345
|
100
100
|
pyglove/core/symbolic/list_test.py,sha256=yHYAJhe_EYwtU9p8eDztSXNBjnAGKe0UDN5U6S-xDr8,60627
|
101
101
|
pyglove/core/symbolic/object.py,sha256=1NdVMGoZiyuuxXIbQiW-n4NhUdHSuiedB-chx1n0BrQ,42349
|
102
|
-
pyglove/core/symbolic/object_test.py,sha256=
|
102
|
+
pyglove/core/symbolic/object_test.py,sha256=IRoeBr8j1SJVohDkEaat0KlW5YQfco6zARTh3qOW0Jc,93092
|
103
103
|
pyglove/core/symbolic/origin.py,sha256=5bH1jZvFHY5jwku32vDm8Bj2i-buv-YNuzOOsA5GlSA,6177
|
104
104
|
pyglove/core/symbolic/origin_test.py,sha256=dU_ZGrGDetM_lYVMn3wQO0d367_t_t8eESe3NrKPBNE,3159
|
105
105
|
pyglove/core/symbolic/pure_symbolic.py,sha256=FVq-5Cg5uZe3ybTIrTqTHIEJIpje0oxzV2kKL6UKlsU,3244
|
106
|
-
pyglove/core/symbolic/ref.py,sha256=
|
106
|
+
pyglove/core/symbolic/ref.py,sha256=6I2f_jbsmkXZzpGRk60Z0l_vlSdj7WJ39WmvsOyv5xg,8068
|
107
107
|
pyglove/core/symbolic/ref_test.py,sha256=AjzvXoBtR8lOp8BgQOejnSnLcvgM3Yxq9GIMaNb1i6Y,5786
|
108
108
|
pyglove/core/symbolic/symbolize.py,sha256=ohID9-V8QiFe7OMpPlRomiqUnKBVMpypd8ZuMuHaa4s,6582
|
109
109
|
pyglove/core/symbolic/symbolize_test.py,sha256=o7bRfMhGc6uw2FIH8arE99-bPb3i0YixcHYyiP-QqeQ,6487
|
@@ -139,14 +139,14 @@ pyglove/core/typing/typed_missing.py,sha256=5lzkrd-DqJDT8eoW1d8p6mxV3mvy5X78zFon
|
|
139
139
|
pyglove/core/typing/typed_missing_test.py,sha256=3k_s0JBYBJ_6xoOuPSMrQzqas6QHD0PHux0T9NlTBZc,2381
|
140
140
|
pyglove/core/typing/value_specs.py,sha256=V6m4RhCc4yi6H8X1LoptSk5gsa7ko8HgLYDSfVsMBtQ,99874
|
141
141
|
pyglove/core/typing/value_specs_test.py,sha256=kKwoEmGIXtM5JikhtbhcE4Ds-wSyYAsMTozHVa2Lpbc,122707
|
142
|
-
pyglove/core/views/__init__.py,sha256=
|
143
|
-
pyglove/core/views/base.py,sha256=
|
142
|
+
pyglove/core/views/__init__.py,sha256=gll9ZBRYz4p_-LWOdzSR2a6UTWcJ8nR430trrP0yLCU,967
|
143
|
+
pyglove/core/views/base.py,sha256=Eq94AM5lryQ1IKuQsTSb7ZzX-lp2nhuOnS4ztMnCPIM,26447
|
144
144
|
pyglove/core/views/base_test.py,sha256=F6nou7reS_Kmr2H57MUgcnc1bIp9Z3BWCvHOvMqjGkQ,16642
|
145
|
-
pyglove/core/views/html/__init__.py,sha256=
|
146
|
-
pyglove/core/views/html/base.py,sha256=
|
147
|
-
pyglove/core/views/html/base_test.py,sha256=
|
148
|
-
pyglove/core/views/html/tree_view.py,sha256=
|
149
|
-
pyglove/core/views/html/tree_view_test.py,sha256=
|
145
|
+
pyglove/core/views/html/__init__.py,sha256=Rkd8IU6-988A8RPcuHBqibQxcL3u2RB54NZr0qoGNUU,1086
|
146
|
+
pyglove/core/views/html/base.py,sha256=FhJvcKpVZaYWxBbJ-XuXkoANfxL7_yVVTXitRlU9OP4,15083
|
147
|
+
pyglove/core/views/html/base_test.py,sha256=ly9icRdzL4FddWdlFy3b2qsOe-mxT4iK6xiTgQLoqcw,22782
|
148
|
+
pyglove/core/views/html/tree_view.py,sha256=TqNqGpBrEG52kVCJoLGeF_mZA52V_n6YyzzPfq2ojdw,52155
|
149
|
+
pyglove/core/views/html/tree_view_test.py,sha256=vkjkxKfBU1QhyNLUZ0Gi1btJRFZC9B4EMrCQAKnBqNI,74708
|
150
150
|
pyglove/ext/__init__.py,sha256=3jp8cJvKW6PENOZlmVAbT0w-GBRn_kjhc0wDX3XjpOE,755
|
151
151
|
pyglove/ext/early_stopping/__init__.py,sha256=_xkT3K_MycFuF3k2N74sfr9FPFcXMD3pfv5vuc4Tx5M,1116
|
152
152
|
pyglove/ext/early_stopping/base.py,sha256=KolexwKDAXhI9iY7hoaDO1mbc3TA4gDn0UifxURwbk0,2358
|
@@ -186,8 +186,8 @@ pyglove/ext/scalars/randoms.py,sha256=LkMIIx7lOq_lvJvVS3BrgWGuWl7Pi91-lA-O8x_gZs
|
|
186
186
|
pyglove/ext/scalars/randoms_test.py,sha256=nEhiqarg8l_5EOucp59CYrpO2uKxS1pe0hmBdZUzRNM,2000
|
187
187
|
pyglove/ext/scalars/step_wise.py,sha256=IDw3tuTpv0KVh7AN44W43zqm1-E0HWPUlytWOQC9w3Y,3789
|
188
188
|
pyglove/ext/scalars/step_wise_test.py,sha256=TL1vJ19xVx2t5HKuyIzGoogF7N3Rm8YhLE6JF7i0iy8,2540
|
189
|
-
pyglove-0.4.5.
|
190
|
-
pyglove-0.4.5.
|
191
|
-
pyglove-0.4.5.
|
192
|
-
pyglove-0.4.5.
|
193
|
-
pyglove-0.4.5.
|
189
|
+
pyglove-0.4.5.dev202411050809.dist-info/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
190
|
+
pyglove-0.4.5.dev202411050809.dist-info/METADATA,sha256=Awku-gpCb395dqtLTubEg0bd1j8QCrCgqedotNKk6QM,6666
|
191
|
+
pyglove-0.4.5.dev202411050809.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
192
|
+
pyglove-0.4.5.dev202411050809.dist-info/top_level.txt,sha256=wITzJSKcj8GZUkbq-MvUQnFadkiuAv_qv5qQMw0fIow,8
|
193
|
+
pyglove-0.4.5.dev202411050809.dist-info/RECORD,,
|
File without changes
|
File without changes
|
{pyglove-0.4.5.dev202411030808.dist-info → pyglove-0.4.5.dev202411050809.dist-info}/top_level.txt
RENAMED
File without changes
|