pyglove 0.4.5.dev202410020809__py3-none-any.whl → 0.4.5.dev202410100808__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyglove/core/__init__.py +9 -0
- pyglove/core/object_utils/__init__.py +1 -0
- pyglove/core/object_utils/value_location.py +10 -0
- pyglove/core/object_utils/value_location_test.py +22 -0
- pyglove/core/symbolic/base.py +40 -2
- pyglove/core/symbolic/base_test.py +67 -0
- pyglove/core/symbolic/diff.py +190 -1
- pyglove/core/symbolic/diff_test.py +290 -0
- pyglove/core/symbolic/object_test.py +3 -8
- pyglove/core/symbolic/ref.py +29 -0
- pyglove/core/symbolic/ref_test.py +143 -0
- pyglove/core/typing/__init__.py +4 -0
- pyglove/core/typing/callable_ext.py +240 -1
- pyglove/core/typing/callable_ext_test.py +255 -0
- pyglove/core/typing/inspect.py +63 -0
- pyglove/core/typing/inspect_test.py +39 -0
- pyglove/core/views/__init__.py +30 -0
- pyglove/core/views/base.py +906 -0
- pyglove/core/views/base_test.py +615 -0
- pyglove/core/views/html/__init__.py +27 -0
- pyglove/core/views/html/base.py +529 -0
- pyglove/core/views/html/base_test.py +804 -0
- pyglove/core/views/html/tree_view.py +1052 -0
- pyglove/core/views/html/tree_view_test.py +748 -0
- {pyglove-0.4.5.dev202410020809.dist-info → pyglove-0.4.5.dev202410100808.dist-info}/METADATA +1 -1
- {pyglove-0.4.5.dev202410020809.dist-info → pyglove-0.4.5.dev202410100808.dist-info}/RECORD +29 -21
- {pyglove-0.4.5.dev202410020809.dist-info → pyglove-0.4.5.dev202410100808.dist-info}/LICENSE +0 -0
- {pyglove-0.4.5.dev202410020809.dist-info → pyglove-0.4.5.dev202410100808.dist-info}/WHEEL +0 -0
- {pyglove-0.4.5.dev202410020809.dist-info → pyglove-0.4.5.dev202410100808.dist-info}/top_level.txt +0 -0
@@ -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())), '
|
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.
|
3488
|
+
self.assertIn('<html>', str(Bar(Foo())))
|
3494
3489
|
|
3495
3490
|
|
3496
3491
|
class Foo(Object):
|
pyglove/core/symbolic/ref.py
CHANGED
@@ -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()
|
pyglove/core/typing/__init__.py
CHANGED
@@ -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.
|