pyglove 0.4.5.dev202410020809__py3-none-any.whl → 0.4.5.dev202410100808__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. pyglove/core/__init__.py +9 -0
  2. pyglove/core/object_utils/__init__.py +1 -0
  3. pyglove/core/object_utils/value_location.py +10 -0
  4. pyglove/core/object_utils/value_location_test.py +22 -0
  5. pyglove/core/symbolic/base.py +40 -2
  6. pyglove/core/symbolic/base_test.py +67 -0
  7. pyglove/core/symbolic/diff.py +190 -1
  8. pyglove/core/symbolic/diff_test.py +290 -0
  9. pyglove/core/symbolic/object_test.py +3 -8
  10. pyglove/core/symbolic/ref.py +29 -0
  11. pyglove/core/symbolic/ref_test.py +143 -0
  12. pyglove/core/typing/__init__.py +4 -0
  13. pyglove/core/typing/callable_ext.py +240 -1
  14. pyglove/core/typing/callable_ext_test.py +255 -0
  15. pyglove/core/typing/inspect.py +63 -0
  16. pyglove/core/typing/inspect_test.py +39 -0
  17. pyglove/core/views/__init__.py +30 -0
  18. pyglove/core/views/base.py +906 -0
  19. pyglove/core/views/base_test.py +615 -0
  20. pyglove/core/views/html/__init__.py +27 -0
  21. pyglove/core/views/html/base.py +529 -0
  22. pyglove/core/views/html/base_test.py +804 -0
  23. pyglove/core/views/html/tree_view.py +1052 -0
  24. pyglove/core/views/html/tree_view_test.py +748 -0
  25. {pyglove-0.4.5.dev202410020809.dist-info → pyglove-0.4.5.dev202410100808.dist-info}/METADATA +1 -1
  26. {pyglove-0.4.5.dev202410020809.dist-info → pyglove-0.4.5.dev202410100808.dist-info}/RECORD +29 -21
  27. {pyglove-0.4.5.dev202410020809.dist-info → pyglove-0.4.5.dev202410100808.dist-info}/LICENSE +0 -0
  28. {pyglove-0.4.5.dev202410020809.dist-info → pyglove-0.4.5.dev202410100808.dist-info}/WHEEL +0 -0
  29. {pyglove-0.4.5.dev202410020809.dist-info → pyglove-0.4.5.dev202410100808.dist-info}/top_level.txt +0 -0
@@ -13,6 +13,8 @@
13
13
  # limitations under the License.
14
14
  """Tests for pyglove.diff."""
15
15
 
16
+ import inspect
17
+ from typing import Any
16
18
  import unittest
17
19
 
18
20
  from pyglove.core import typing as pg_typing
@@ -337,6 +339,294 @@ class DiffTest(unittest.TestCase):
337
339
  '_type': Diff(B, C),
338
340
  })
339
341
 
342
+ def test_to_html(self):
343
+
344
+ class Foo(Object):
345
+ x: Any
346
+ y: Any
347
+
348
+ class Bar(Foo):
349
+ pass
350
+
351
+ def assert_style(html, expected):
352
+ expected = inspect.cleandoc(expected).strip()
353
+ actual = html.style_section.strip()
354
+ if actual != expected:
355
+ print(actual)
356
+ self.assertEqual(actual.strip(), expected)
357
+
358
+ def assert_content(html, expected):
359
+ expected = inspect.cleandoc(expected).strip()
360
+ actual = html.content.strip()
361
+ if actual != expected:
362
+ print(actual)
363
+ self.assertEqual(actual.strip(), expected)
364
+
365
+ assert_style(
366
+ pg_diff(1, 1).to_html(),
367
+ """
368
+ <style>
369
+ /* Tooltip styles. */
370
+ span.tooltip {
371
+ visibility: hidden;
372
+ white-space: pre-wrap;
373
+ font-weight: normal;
374
+ background-color: #484848;
375
+ color: #fff;
376
+ padding: 10px;
377
+ border-radius: 6px;
378
+ position: absolute;
379
+ z-index: 1;
380
+ }
381
+ /* Summary styles. */
382
+ details.pyglove summary {
383
+ font-weight: bold;
384
+ margin: -0.5em -0.5em 0;
385
+ padding: 0.5em;
386
+ }
387
+ .summary_name {
388
+ display: inline;
389
+ padding: 0 5px;
390
+ }
391
+ .summary_title {
392
+ display: inline;
393
+ }
394
+ .summary_name + div.summary_title {
395
+ display: inline;
396
+ color: #aaa;
397
+ }
398
+ .summary_title:hover + span.tooltip {
399
+ visibility: visible;
400
+ }
401
+ /* Type-specific styles. */
402
+ .pyglove.str .summary_title {
403
+ color: darkred;
404
+ font-style: italic;
405
+ }
406
+ .diff .summary_title::after {
407
+ content: ' (diff)';
408
+ color: #aaa;
409
+ }
410
+ .diff .summary_title {
411
+ background-color: yellow;
412
+ }
413
+ .diff table td {
414
+ padding: 4px;
415
+ }
416
+ .diff_value {
417
+ display: inline-block;
418
+ align-items: center;
419
+ }
420
+ .no_diff_key {
421
+ opacity: 0.4;
422
+ }
423
+ .diff_left.diff_right::after {
424
+ content: '(no diff)';
425
+ margin-left: 0.5em;
426
+ color: #aaa;
427
+ font-style: italic;
428
+ }
429
+ .diff_empty::before {
430
+ content: '(empty)';
431
+ font-style: italic;
432
+ margin-left: 0.5em;
433
+ color: #aaa;
434
+ }
435
+ .diff_value .diff_left::before {
436
+ content: '🇱';
437
+ }
438
+ .diff_value .diff_left > .simple_value {
439
+ background-color: #ffcccc;
440
+ }
441
+ .diff_value .diff_left > details {
442
+ background-color: #ffcccc;
443
+ }
444
+ .diff_value .diff_right::before {
445
+ content: '🇷';
446
+ }
447
+ .diff_value .diff_right > .simple_value {
448
+ background-color: #ccffcc;
449
+ }
450
+ .diff_value .diff_right > details {
451
+ background-color: #ccffcc;
452
+ }
453
+ /* Value details styles. */
454
+ details.pyglove {
455
+ border: 1px solid #aaa;
456
+ border-radius: 4px;
457
+ padding: 0.5em 0.5em 0;
458
+ margin: 0.1em 0;
459
+ }
460
+ details.pyglove.special_value {
461
+ margin-bottom: 0.75em;
462
+ }
463
+ details.pyglove[open] {
464
+ padding: 0.5em 0.5em 0.5em;
465
+ }
466
+ .highlight {
467
+ background-color: Mark;
468
+ }
469
+ .lowlight {
470
+ opacity: 0.2;
471
+ }
472
+ </style>
473
+ """
474
+ )
475
+
476
+ # No diff leaf value (diff only)
477
+ assert_content(
478
+ pg_diff(1, 1).to_html(),
479
+ """
480
+ <details open class="pyglove diff"><summary><div class="summary_title">Diff</div><span class="tooltip">No diff</span></summary><span class="diff_empty"></span></details>
481
+ """
482
+ )
483
+
484
+ # No diff leaf value (keep both)
485
+ assert_content(
486
+ pg_diff(1, 1, mode='both').to_html(),
487
+ """
488
+ <span class="simple_value int diff_left diff_right">1</span>
489
+ """
490
+ )
491
+ # No diff complex value (diff only)
492
+ assert_content(
493
+ pg_diff(
494
+ Foo(x=1, y=[Foo(x=2, y=3)]),
495
+ Foo(x=1, y=[Foo(x=2, y=3)])
496
+ ).to_html(
497
+ enable_summary_tooltip=False,
498
+ enable_key_tooltip=False,
499
+ ),
500
+ """
501
+ <details open class="pyglove diff"><summary><div class="summary_title">Diff</div></summary><span class="diff_empty"></span></details>
502
+ """
503
+ )
504
+ # No diff complex value.
505
+ assert_content(
506
+ pg_diff(
507
+ Foo(x=1, y=[Foo(x=2, y=3)]),
508
+ Foo(x=1, y=[Foo(x=2, y=3)]), mode='both'
509
+ ).to_html(
510
+ enable_summary_tooltip=False,
511
+ enable_key_tooltip=False,
512
+ ),
513
+ """
514
+ <details open class="pyglove diff"><summary><div class="summary_title">Foo(...)</div></summary><div class="complex_value foo diff_left diff_right"><table><tr><td><span class="object_key">x</span></td><td><span class="simple_value int">1</span></td></tr><tr><td><span class="object_key">y</span></td><td><details class="pyglove list"><summary><div class="summary_title">List(...)</div></summary><div class="complex_value list"><table><tr><td><span class="object_key">0</span></td><td><details class="pyglove foo"><summary><div class="summary_title">Foo(...)</div></summary><div class="complex_value foo"><table><tr><td><span class="object_key">x</span></td><td><span class="simple_value int">2</span></td></tr><tr><td><span class="object_key">y</span></td><td><span class="simple_value int">3</span></td></tr></table></div></details></td></tr></table></div></details></td></tr></table></div></details>
515
+ """
516
+ )
517
+
518
+ # Diff on simple value.
519
+ assert_content(
520
+ pg_diff(1, 2).to_html(),
521
+ """
522
+ <div class="diff_value"><div class="diff_left"><span class="simple_value int">1</span></div><div class="diff_right"><span class="simple_value int">2</span></div></div>
523
+ """
524
+ )
525
+
526
+ # Diff on list.
527
+ assert_content(
528
+ pg_diff([0, 1, 2], [0, 2], mode='both').to_html(
529
+ enable_summary_tooltip=False,
530
+ enable_key_tooltip=False,
531
+ ),
532
+ """
533
+ <details open class="pyglove diff"><summary><div class="summary_title">List</div></summary><div class="complex_value list"><table><tr><td><span class="object_key no_diff_key">0</span><span class="tooltip">[0]</span></td><td><span class="simple_value int diff_left diff_right">0</span></td></tr><tr><td><span class="object_key">1</span><span class="tooltip">[1]</span></td><td><div class="diff_value"><div class="diff_left"><span class="simple_value int">1</span></div><div class="diff_right"><span class="simple_value int">2</span></div></div></td></tr><tr><td><span class="object_key">2</span><span class="tooltip">[2]</span></td><td><div class="diff_value"><div class="diff_left"><span class="simple_value int">2</span></div></div></td></tr></table></div></details>
534
+ """
535
+ )
536
+
537
+ # Diff on dict.
538
+ assert_content(
539
+ pg_diff(dict(x=1, y=2, z=3), dict(x=1, y=3, w=4), mode='both').to_html(
540
+ enable_summary_tooltip=False,
541
+ enable_key_tooltip=False,
542
+ ),
543
+ """
544
+ <details open class="pyglove diff"><summary><div class="summary_title">dict</div></summary><div class="complex_value dict"><table><tr><td><span class="object_key no_diff_key">x</span><span class="tooltip">x</span></td><td><span class="simple_value int diff_left diff_right">1</span></td></tr><tr><td><span class="object_key">y</span><span class="tooltip">y</span></td><td><div class="diff_value"><div class="diff_left"><span class="simple_value int">2</span></div><div class="diff_right"><span class="simple_value int">3</span></div></div></td></tr><tr><td><span class="object_key">z</span><span class="tooltip">z</span></td><td><div class="diff_value"><div class="diff_left"><span class="simple_value int">3</span></div></div></td></tr><tr><td><span class="object_key">w</span><span class="tooltip">w</span></td><td><div class="diff_value"><div class="diff_right"><span class="simple_value int">4</span></div></div></td></tr></table></div></details>
545
+ """
546
+ )
547
+
548
+ # Diff on symbolic objects of the same type.
549
+ assert_content(
550
+ pg_diff(
551
+ Foo(x=2, y=Foo(x=3, y=3)),
552
+ Foo(x=1, y=Foo(x=2, y=3)),
553
+ mode='both',
554
+ ).to_html(
555
+ enable_summary_tooltip=False,
556
+ enable_key_tooltip=False,
557
+ ),
558
+ """
559
+ <details open class="pyglove diff"><summary><div class="summary_title">Foo</div></summary><div class="complex_value foo"><table><tr><td><span class="object_key">x</span><span class="tooltip">x</span></td><td><div class="diff_value"><div class="diff_left"><span class="simple_value int">2</span></div><div class="diff_right"><span class="simple_value int">1</span></div></div></td></tr><tr><td><span class="object_key">y</span><span class="tooltip">y</span></td><td><details class="pyglove diff"><summary><div class="summary_title">Foo</div></summary><div class="complex_value foo"><table><tr><td><span class="object_key">x</span><span class="tooltip">y.x</span></td><td><div class="diff_value"><div class="diff_left"><span class="simple_value int">3</span></div><div class="diff_right"><span class="simple_value int">2</span></div></div></td></tr><tr><td><span class="object_key no_diff_key">y</span><span class="tooltip">y.y</span></td><td><span class="simple_value int diff_left diff_right">3</span></td></tr></table></div></details></td></tr></table></div></details>
560
+ """
561
+ )
562
+
563
+ # Diff on symbolic objects of different types.
564
+ assert_content(
565
+ pg_diff(
566
+ Foo(x=2, y=Foo(x=3, y=3)),
567
+ Bar(x=2, y=Foo(x=3, y=3)),
568
+ mode='both',
569
+ ).to_html(
570
+ enable_summary_tooltip=False,
571
+ enable_key_tooltip=False,
572
+ ),
573
+ """
574
+ <div class="diff_value"><div class="diff_left"><details class="pyglove foo"><summary><div class="summary_title">Foo(...)</div></summary><div class="complex_value foo"><table><tr><td><span class="object_key">x</span></td><td><span class="simple_value int">2</span></td></tr><tr><td><span class="object_key">y</span></td><td><details class="pyglove foo"><summary><div class="summary_title">Foo(...)</div></summary><div class="complex_value foo"><table><tr><td><span class="object_key">x</span></td><td><span class="simple_value int">3</span></td></tr><tr><td><span class="object_key">y</span></td><td><span class="simple_value int">3</span></td></tr></table></div></details></td></tr></table></div></details></div><div class="diff_right"><details class="pyglove bar"><summary><div class="summary_title">Bar(...)</div></summary><div class="complex_value bar"><table><tr><td><span class="object_key">x</span></td><td><span class="simple_value int">2</span></td></tr><tr><td><span class="object_key">y</span></td><td><details class="pyglove foo"><summary><div class="summary_title">Foo(...)</div></summary><div class="complex_value foo"><table><tr><td><span class="object_key">x</span></td><td><span class="simple_value int">3</span></td></tr><tr><td><span class="object_key">y</span></td><td><span class="simple_value int">3</span></td></tr></table></div></details></td></tr></table></div></details></div></div>
575
+ """
576
+ )
577
+
578
+ # Different types but same values, with value collapsing.
579
+ assert_content(
580
+ pg_diff(
581
+ Foo(x=2, y=Foo(x=3, y=3)),
582
+ Bar(x=2, y=Bar(x=3, y=3)),
583
+ mode='both', collapse=True,
584
+ ).to_html(
585
+ enable_summary_tooltip=False,
586
+ enable_key_tooltip=False,
587
+ ),
588
+ """
589
+ <details open class="pyglove diff"><summary><div class="summary_title">Foo | Bar</div></summary><div class="complex_value foo"><table><tr><td><span class="object_key no_diff_key">x</span><span class="tooltip">x</span></td><td><span class="simple_value int diff_left diff_right">2</span></td></tr><tr><td><span class="object_key">y</span><span class="tooltip">y</span></td><td><details class="pyglove diff"><summary><div class="summary_title">Foo | Bar</div></summary><div class="complex_value foo"><table><tr><td><span class="object_key no_diff_key">x</span><span class="tooltip">y.x</span></td><td><span class="simple_value int diff_left diff_right">3</span></td></tr><tr><td><span class="object_key no_diff_key">y</span><span class="tooltip">y.y</span></td><td><span class="simple_value int diff_left diff_right">3</span></td></tr></table></div></details></td></tr></table></div></details>
590
+ """
591
+ )
592
+
593
+ # Different types and different values, with value collapsing.
594
+ assert_content(
595
+ pg_diff(
596
+ Foo(x=2, y=Foo(x=3, y=3)),
597
+ Bar(x=3, y=Bar(x=2, y=3)),
598
+ mode='both', collapse=True,
599
+ ).to_html(
600
+ enable_summary_tooltip=False,
601
+ enable_key_tooltip=False,
602
+ ),
603
+ """
604
+ <details open class="pyglove diff"><summary><div class="summary_title">Foo | Bar</div></summary><div class="complex_value foo"><table><tr><td><span class="object_key">x</span><span class="tooltip">x</span></td><td><div class="diff_value"><div class="diff_left"><span class="simple_value int">2</span></div><div class="diff_right"><span class="simple_value int">3</span></div></div></td></tr><tr><td><span class="object_key">y</span><span class="tooltip">y</span></td><td><details class="pyglove diff"><summary><div class="summary_title">Foo | Bar</div></summary><div class="complex_value foo"><table><tr><td><span class="object_key">x</span><span class="tooltip">y.x</span></td><td><div class="diff_value"><div class="diff_left"><span class="simple_value int">3</span></div><div class="diff_right"><span class="simple_value int">2</span></div></div></td></tr><tr><td><span class="object_key no_diff_key">y</span><span class="tooltip">y.y</span></td><td><span class="simple_value int diff_left diff_right">3</span></td></tr></table></div></details></td></tr></table></div></details>
605
+ """
606
+ )
607
+
608
+ # Diff with uncollapsing UI.
609
+ assert_content(
610
+ pg_diff(
611
+ [
612
+ Foo(1, 2), Foo(1, 2), None,
613
+ Foo(x=1, y=Foo(2, 3)), [dict(x=1, y=2)]
614
+ ],
615
+ [
616
+ Foo(1, 2), Bar(1, 2), dict(x=1),
617
+ [1, 2], Foo(x=2, y=Foo(2, 4)), [dict(x=3)]
618
+ ],
619
+ mode='both'
620
+ ).to_html(
621
+ enable_summary_tooltip=False,
622
+ enable_key_tooltip=False,
623
+ uncollapse=['[0]', '[1].right', '[3].left.y'],
624
+ ),
625
+ """
626
+ <details open class="pyglove diff"><summary><div class="summary_title">List</div></summary><div class="complex_value list"><table><tr><td><span class="object_key no_diff_key">0</span><span class="tooltip">[0]</span></td><td><details open class="pyglove diff"><summary><div class="summary_title">Foo(...)</div></summary><div class="complex_value foo diff_left diff_right"><table><tr><td><span class="object_key">x</span></td><td><span class="simple_value int">1</span></td></tr><tr><td><span class="object_key">y</span></td><td><span class="simple_value int">2</span></td></tr></table></div></details></td></tr><tr><td><span class="object_key">1</span><span class="tooltip">[1]</span></td><td><div class="diff_value"><div class="diff_left"><details class="pyglove foo"><summary><div class="summary_title">Foo(...)</div></summary><div class="complex_value foo"><table><tr><td><span class="object_key">x</span></td><td><span class="simple_value int">1</span></td></tr><tr><td><span class="object_key">y</span></td><td><span class="simple_value int">2</span></td></tr></table></div></details></div><div class="diff_right"><details open class="pyglove bar"><summary><div class="summary_title">Bar(...)</div></summary><div class="complex_value bar"><table><tr><td><span class="object_key">x</span></td><td><span class="simple_value int">1</span></td></tr><tr><td><span class="object_key">y</span></td><td><span class="simple_value int">2</span></td></tr></table></div></details></div></div></td></tr><tr><td><span class="object_key">2</span><span class="tooltip">[2]</span></td><td><div class="diff_value"><div class="diff_left"><span class="simple_value none-type">None</span></div><div class="diff_right"><details class="pyglove dict"><summary><div class="summary_title">Dict(...)</div></summary><div class="complex_value dict"><table><tr><td><span class="object_key">x</span></td><td><span class="simple_value int">1</span></td></tr></table></div></details></div></div></td></tr><tr><td><span class="object_key">3</span><span class="tooltip">[3]</span></td><td><div class="diff_value"><div class="diff_left"><details open class="pyglove foo"><summary><div class="summary_title">Foo(...)</div></summary><div class="complex_value foo"><table><tr><td><span class="object_key">x</span></td><td><span class="simple_value int">1</span></td></tr><tr><td><span class="object_key">y</span></td><td><details open class="pyglove foo"><summary><div class="summary_title">Foo(...)</div></summary><div class="complex_value foo"><table><tr><td><span class="object_key">x</span></td><td><span class="simple_value int">2</span></td></tr><tr><td><span class="object_key">y</span></td><td><span class="simple_value int">3</span></td></tr></table></div></details></td></tr></table></div></details></div><div class="diff_right"><details class="pyglove list"><summary><div class="summary_title">List(...)</div></summary><div class="complex_value list"><table><tr><td><span class="object_key">0</span></td><td><span class="simple_value int">1</span></td></tr><tr><td><span class="object_key">1</span></td><td><span class="simple_value int">2</span></td></tr></table></div></details></div></div></td></tr><tr><td><span class="object_key">4</span><span class="tooltip">[4]</span></td><td><div class="diff_value"><div class="diff_left"><details class="pyglove list"><summary><div class="summary_title">List(...)</div></summary><div class="complex_value list"><table><tr><td><span class="object_key">0</span></td><td><details class="pyglove dict"><summary><div class="summary_title">Dict(...)</div></summary><div class="complex_value dict"><table><tr><td><span class="object_key">x</span></td><td><span class="simple_value int">1</span></td></tr><tr><td><span class="object_key">y</span></td><td><span class="simple_value int">2</span></td></tr></table></div></details></td></tr></table></div></details></div><div class="diff_right"><details class="pyglove foo"><summary><div class="summary_title">Foo(...)</div></summary><div class="complex_value foo"><table><tr><td><span class="object_key">x</span></td><td><span class="simple_value int">2</span></td></tr><tr><td><span class="object_key">y</span></td><td><details class="pyglove foo"><summary><div class="summary_title">Foo(...)</div></summary><div class="complex_value foo"><table><tr><td><span class="object_key">x</span></td><td><span class="simple_value int">2</span></td></tr><tr><td><span class="object_key">y</span></td><td><span class="simple_value int">4</span></td></tr></table></div></details></td></tr></table></div></details></div></div></td></tr><tr><td><span class="object_key">5</span><span class="tooltip">[5]</span></td><td><div class="diff_value"><div class="diff_right"><details class="pyglove list"><summary><div class="summary_title">List(...)</div></summary><div class="complex_value list"><table><tr><td><span class="object_key">0</span></td><td><details class="pyglove dict"><summary><div class="summary_title">Dict(...)</div></summary><div class="complex_value dict"><table><tr><td><span class="object_key">x</span></td><td><span class="simple_value int">3</span></td></tr></table></div></details></td></tr></table></div></details></div></div></td></tr></table></div></details>
627
+ """
628
+ )
629
+
340
630
 
341
631
  if __name__ == '__main__':
342
632
  unittest.main()
@@ -3469,16 +3469,11 @@ class FormatTest(unittest.TestCase):
3469
3469
  def test_custom_format(self):
3470
3470
 
3471
3471
  class Foo(Object): # pylint: disable=redefined-outer-name]
3472
-
3473
- def _repr_html_(self):
3474
- return '<foo/>'
3472
+ pass
3475
3473
 
3476
3474
  class Bar(Object):
3477
3475
  foo: Foo
3478
3476
 
3479
- def _repr_xml_(self):
3480
- return f'<bar>{self.foo}</bar>'
3481
-
3482
3477
  def _method(attr_name):
3483
3478
  def fn(v, root_indent):
3484
3479
  del root_indent
@@ -3487,10 +3482,10 @@ class FormatTest(unittest.TestCase):
3487
3482
  return fn
3488
3483
 
3489
3484
  with object_utils.str_format(custom_format=_method('_repr_xml_')):
3490
- self.assertEqual(str(Bar(Foo())), '<bar>Foo()</bar>')
3485
+ self.assertEqual(str(Bar(Foo())), 'Bar(\n foo = Foo()\n)')
3491
3486
 
3492
3487
  with object_utils.str_format(custom_format=_method('_repr_html_')):
3493
- self.assertEqual(str(Bar(Foo())), 'Bar(\n foo = <foo/>\n)')
3488
+ self.assertIn('<html>', str(Bar(Foo())))
3494
3489
 
3495
3490
 
3496
3491
  class Foo(Object):
@@ -19,6 +19,7 @@ from pyglove.core import object_utils
19
19
  from pyglove.core import typing as pg_typing
20
20
  from pyglove.core.symbolic import base
21
21
  from pyglove.core.symbolic.object import Object
22
+ from pyglove.core.views import html
22
23
 
23
24
 
24
25
  class Ref(Object, base.Inferential):
@@ -162,6 +163,34 @@ class Ref(Object, base.Inferential):
162
163
  + ')'
163
164
  )
164
165
 
166
+ def _html_tree_view_content(
167
+ self,
168
+ *,
169
+ view: html.HtmlTreeView,
170
+ **kwargs: Any) -> html.Html:
171
+ """Overrides `_html_content` to render the referenced value."""
172
+ return view.content(self._value, **kwargs)
173
+
174
+ def _html_tree_view_summary(
175
+ self,
176
+ *,
177
+ view: html.HtmlTreeView,
178
+ title: Optional[str] = None,
179
+ **kwargs: Any) -> Optional[html.Html]:
180
+ """Overrides `_html_content` to render the referenced value."""
181
+ return view.summary(
182
+ self,
183
+ title=title or f'{type(self._value).__name__}(...)',
184
+ **kwargs
185
+ ).add_style(
186
+ """
187
+ details.ref .summary_title::before {
188
+ content: 'ref: ';
189
+ color: #aaa;
190
+ }
191
+ """
192
+ )
193
+
165
194
 
166
195
  def maybe_ref(value: Any) -> Optional[Ref]:
167
196
  """Returns a reference if a value is not symbolic or already has a parent."""
@@ -155,6 +155,149 @@ class RefTest(unittest.TestCase):
155
155
  self.assertIs(a_prime, a.value)
156
156
  self.assertFalse(contains(a_prime, type=ref.Ref))
157
157
 
158
+ def test_to_html(self):
159
+
160
+ def assert_style(html, expected):
161
+ expected = inspect.cleandoc(expected).strip()
162
+ actual = html.style_section.strip()
163
+ if actual != expected:
164
+ print(actual)
165
+ self.assertEqual(actual.strip(), expected)
166
+
167
+ def assert_content(html, expected):
168
+ expected = inspect.cleandoc(expected).strip()
169
+ actual = html.content.strip()
170
+ if actual != expected:
171
+ print(actual)
172
+ self.assertEqual(actual.strip(), expected)
173
+
174
+ class Foo(Object):
175
+ x: Any
176
+
177
+ assert_style(
178
+ Foo(ref.Ref(Foo(1))).to_html(),
179
+ """
180
+ <style>
181
+ /* Tooltip styles. */
182
+ span.tooltip {
183
+ visibility: hidden;
184
+ white-space: pre-wrap;
185
+ font-weight: normal;
186
+ background-color: #484848;
187
+ color: #fff;
188
+ padding: 10px;
189
+ border-radius: 6px;
190
+ position: absolute;
191
+ z-index: 1;
192
+ }
193
+ /* Summary styles. */
194
+ details.pyglove summary {
195
+ font-weight: bold;
196
+ margin: -0.5em -0.5em 0;
197
+ padding: 0.5em;
198
+ }
199
+ .summary_name {
200
+ display: inline;
201
+ padding: 0 5px;
202
+ }
203
+ .summary_title {
204
+ display: inline;
205
+ }
206
+ .summary_name + div.summary_title {
207
+ display: inline;
208
+ color: #aaa;
209
+ }
210
+ .summary_title:hover + span.tooltip {
211
+ visibility: visible;
212
+ }
213
+ /* Type-specific styles. */
214
+ .pyglove.str .summary_title {
215
+ color: darkred;
216
+ font-style: italic;
217
+ }
218
+ /* Object key styles. */
219
+ .object_key {
220
+ margin-right: 0.25em;
221
+ }
222
+ .object_key:hover + .tooltip {
223
+ visibility: visible;
224
+ background-color: darkblue;
225
+ }
226
+ .complex_value .object_key{
227
+ color: gray;
228
+ border: 1px solid lightgray;
229
+ background-color: ButtonFace;
230
+ border-radius: 0.2em;
231
+ padding: 0.3em;
232
+ }
233
+ .complex_value.list .object_key{
234
+ border: 0;
235
+ color: lightgray;
236
+ background-color: transparent;
237
+ border-radius: 0;
238
+ padding: 0;
239
+ }
240
+ .complex_value.list .object_key::before{
241
+ content: '[';
242
+ }
243
+ .complex_value.list .object_key::after{
244
+ content: ']';
245
+ }
246
+ /* Simple value styles. */
247
+ .simple_value {
248
+ color: blue;
249
+ display: inline-block;
250
+ white-space: pre-wrap;
251
+ padding: 0.2em;
252
+ margin-top: 0.15em;
253
+ }
254
+ .simple_value.str {
255
+ color: darkred;
256
+ font-style: italic;
257
+ }
258
+ .simple_value.int, .simple_value.float {
259
+ color: darkblue;
260
+ }
261
+ /* Complex value styles. */
262
+ span.empty_container::before {
263
+ content: '(empty)';
264
+ font-style: italic;
265
+ margin-left: 0.5em;
266
+ color: #aaa;
267
+ }
268
+ /* Value details styles. */
269
+ details.pyglove {
270
+ border: 1px solid #aaa;
271
+ border-radius: 4px;
272
+ padding: 0.5em 0.5em 0;
273
+ margin: 0.1em 0;
274
+ }
275
+ details.pyglove.special_value {
276
+ margin-bottom: 0.75em;
277
+ }
278
+ details.pyglove[open] {
279
+ padding: 0.5em 0.5em 0.5em;
280
+ }
281
+ .highlight {
282
+ background-color: Mark;
283
+ }
284
+ .lowlight {
285
+ opacity: 0.2;
286
+ }
287
+ </style>
288
+ """
289
+ )
290
+ assert_content(
291
+ Foo(ref.Ref(Foo(1))).to_html(
292
+ use_inferred=False,
293
+ enable_summary_tooltip=False,
294
+ enable_key_tooltip=False,
295
+ ),
296
+ """
297
+ <details open class="pyglove foo"><summary><div class="summary_title">Foo(...)</div></summary><div class="complex_value foo"><table><tr><td><span class="object_key">x</span></td><td><details class="pyglove ref"><summary><div class="summary_title">Foo(...)</div></summary><div class="complex_value foo"><table><tr><td><span class="object_key">x</span></td><td><span class="simple_value int">1</span></td></tr></table></div></details></td></tr></table></div></details>
298
+ """
299
+ )
300
+
158
301
 
159
302
  if __name__ == '__main__':
160
303
  unittest.main()
@@ -362,6 +362,7 @@ from pyglove.core.typing.type_conversion import get_json_value_converter
362
362
  # Inspect helpers.
363
363
  from pyglove.core.typing.inspect import is_subclass
364
364
  from pyglove.core.typing.inspect import is_instance
365
+ from pyglove.core.typing.inspect import get_outer_class
365
366
  from pyglove.core.typing.inspect import get_type
366
367
  from pyglove.core.typing.inspect import get_type_args
367
368
  from pyglove.core.typing.inspect import is_generic
@@ -385,6 +386,9 @@ from pyglove.core.typing.callable_signature import schema
385
386
  get_signature = signature
386
387
 
387
388
  # Callable extensions.
389
+ from pyglove.core.typing.callable_ext import PresetArgValue
390
+ from pyglove.core.typing.callable_ext import enable_preset_args
391
+ from pyglove.core.typing.callable_ext import preset_args
388
392
  from pyglove.core.typing.callable_ext import CallableWithOptionalKeywordArgs
389
393
 
390
394
  # PyType support.