pyglove 0.4.5.dev202504280824__py3-none-any.whl → 0.4.5.dev202504300810__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/symbolic/base.py +6 -2
- pyglove/core/typing/annotation_conversion.py +1 -1
- pyglove/core/typing/annotation_conversion_test.py +5 -5
- pyglove/core/typing/annotation_future_test.py +15 -5
- pyglove/core/typing/class_schema.py +4 -1
- pyglove/core/typing/class_schema_test.py +16 -5
- pyglove/core/typing/value_specs.py +47 -14
- pyglove/core/typing/value_specs_test.py +94 -2
- pyglove/core/views/html/base.py +7 -4
- pyglove/core/views/html/base_test.py +7 -5
- pyglove/core/views/html/controls/label.py +18 -0
- pyglove/core/views/html/controls/label_test.py +1 -1
- pyglove/core/views/html/controls/tab.py +44 -1
- pyglove/core/views/html/controls/tab_test.py +76 -4
- pyglove/core/views/html/tree_view.py +1 -1
- {pyglove-0.4.5.dev202504280824.dist-info → pyglove-0.4.5.dev202504300810.dist-info}/METADATA +1 -1
- {pyglove-0.4.5.dev202504280824.dist-info → pyglove-0.4.5.dev202504300810.dist-info}/RECORD +20 -20
- {pyglove-0.4.5.dev202504280824.dist-info → pyglove-0.4.5.dev202504300810.dist-info}/WHEEL +1 -1
- {pyglove-0.4.5.dev202504280824.dist-info → pyglove-0.4.5.dev202504300810.dist-info}/licenses/LICENSE +0 -0
- {pyglove-0.4.5.dev202504280824.dist-info → pyglove-0.4.5.dev202504300810.dist-info}/top_level.txt +0 -0
pyglove/core/symbolic/base.py
CHANGED
@@ -2458,7 +2458,8 @@ def symbolic_transform_fn(allow_partial: bool):
|
|
2458
2458
|
return value
|
2459
2459
|
if isinstance(value, dict):
|
2460
2460
|
value_spec = pg_typing.ensure_value_spec(
|
2461
|
-
field.value, pg_typing.Dict(), path
|
2461
|
+
field.value, pg_typing.Dict().noneable(), path
|
2462
|
+
)
|
2462
2463
|
value = Symbolic.DictType( # pytype: disable=not-callable # pylint: disable=not-callable
|
2463
2464
|
value,
|
2464
2465
|
value_spec=value_spec,
|
@@ -2471,7 +2472,10 @@ def symbolic_transform_fn(allow_partial: bool):
|
|
2471
2472
|
pass_through=True)
|
2472
2473
|
elif isinstance(value, list):
|
2473
2474
|
value_spec = pg_typing.ensure_value_spec(
|
2474
|
-
field.value,
|
2475
|
+
field.value,
|
2476
|
+
pg_typing.List(pg_typing.Any()).noneable(),
|
2477
|
+
path
|
2478
|
+
)
|
2475
2479
|
value = Symbolic.ListType( # pytype: disable=not-callable # pylint: disable=not-callable
|
2476
2480
|
value,
|
2477
2481
|
value_spec=value_spec,
|
@@ -407,7 +407,7 @@ def _value_spec_from_type_annotation(
|
|
407
407
|
else:
|
408
408
|
spec = vs.Union([_sub_value_spec_from_annotation(x) for x in args])
|
409
409
|
if optional:
|
410
|
-
spec = spec.noneable()
|
410
|
+
spec = spec.noneable(use_none_as_default=False)
|
411
411
|
return spec
|
412
412
|
elif origin is typing.Final:
|
413
413
|
return _value_spec_from_type_annotation(
|
@@ -459,11 +459,11 @@ class ValueSpecFromAnnotationTest(unittest.TestCase):
|
|
459
459
|
def test_optional(self):
|
460
460
|
self.assertEqual(
|
461
461
|
ValueSpec.from_annotation(typing.Optional[int], True),
|
462
|
-
vs.Int().noneable())
|
462
|
+
vs.Int().noneable(use_none_as_default=False))
|
463
463
|
if annotation_conversion._UnionType:
|
464
464
|
self.assertEqual(
|
465
465
|
ValueSpec.from_annotation(int | None, True),
|
466
|
-
vs.Int().noneable())
|
466
|
+
vs.Int().noneable(use_none_as_default=False))
|
467
467
|
|
468
468
|
def test_union(self):
|
469
469
|
self.assertEqual(
|
@@ -471,11 +471,11 @@ class ValueSpecFromAnnotationTest(unittest.TestCase):
|
|
471
471
|
vs.Union([vs.Int(), vs.Str()]))
|
472
472
|
self.assertEqual(
|
473
473
|
ValueSpec.from_annotation(typing.Union[int, str, None], True),
|
474
|
-
vs.Union([vs.Int(), vs.Str()]).noneable())
|
474
|
+
vs.Union([vs.Int(), vs.Str()]).noneable(use_none_as_default=False))
|
475
475
|
if annotation_conversion._UnionType:
|
476
476
|
self.assertEqual(
|
477
|
-
ValueSpec.from_annotation(int | str, True),
|
478
|
-
vs.Union([vs.Int(), vs.Str()]))
|
477
|
+
ValueSpec.from_annotation(int | str | None, True),
|
478
|
+
vs.Union([vs.Int(), vs.Str()]).noneable(use_none_as_default=False))
|
479
479
|
|
480
480
|
def test_final(self):
|
481
481
|
self.assertEqual(
|
@@ -67,7 +67,9 @@ class AnnotationFutureConversionTest(unittest.TestCase):
|
|
67
67
|
class Bar(pg.Object):
|
68
68
|
x: list[int | None]
|
69
69
|
|
70
|
-
self.assert_value_spec(
|
70
|
+
self.assert_value_spec(
|
71
|
+
Bar, 'x', vs.List(vs.Int().noneable(use_none_as_default=False))
|
72
|
+
)
|
71
73
|
|
72
74
|
def test_var_length_tuple(self):
|
73
75
|
|
@@ -88,13 +90,17 @@ class AnnotationFutureConversionTest(unittest.TestCase):
|
|
88
90
|
class Foo(pg.Object):
|
89
91
|
x: typing.Optional[int]
|
90
92
|
|
91
|
-
self.assert_value_spec(
|
93
|
+
self.assert_value_spec(
|
94
|
+
Foo, 'x', vs.Int().noneable(use_none_as_default=False)
|
95
|
+
)
|
92
96
|
|
93
97
|
if sys.version_info >= (3, 10):
|
94
98
|
class Bar(pg.Object):
|
95
99
|
x: int | None
|
96
100
|
|
97
|
-
self.assert_value_spec(
|
101
|
+
self.assert_value_spec(
|
102
|
+
Bar, 'x', vs.Int().noneable(use_none_as_default=False)
|
103
|
+
)
|
98
104
|
|
99
105
|
def test_union(self):
|
100
106
|
|
@@ -102,7 +108,11 @@ class AnnotationFutureConversionTest(unittest.TestCase):
|
|
102
108
|
x: Union[int, typing.Union[str, bool], None]
|
103
109
|
|
104
110
|
self.assert_value_spec(
|
105
|
-
Foo,
|
111
|
+
Foo,
|
112
|
+
'x',
|
113
|
+
vs.Union(
|
114
|
+
[vs.Int(), vs.Str(), vs.Bool()]
|
115
|
+
).noneable(use_none_as_default=False)
|
106
116
|
)
|
107
117
|
|
108
118
|
if sys.version_info >= (3, 10):
|
@@ -125,7 +135,7 @@ class AnnotationFutureConversionTest(unittest.TestCase):
|
|
125
135
|
|
126
136
|
def test_self_referencial(self):
|
127
137
|
self.assert_value_spec(
|
128
|
-
self.A, 'a', vs.Object(self.A).noneable()
|
138
|
+
self.A, 'a', vs.Object(self.A).noneable(use_none_as_default=False)
|
129
139
|
)
|
130
140
|
self.assert_value_spec(
|
131
141
|
self.A, 'b', vs.List(vs.Object(self.A))
|
@@ -370,7 +370,10 @@ class ValueSpec(utils.Formattable, utils.JSONConvertible):
|
|
370
370
|
"""Returns forward referenes used by the value spec."""
|
371
371
|
|
372
372
|
@abc.abstractmethod
|
373
|
-
def noneable(
|
373
|
+
def noneable(
|
374
|
+
self,
|
375
|
+
is_noneable=True,
|
376
|
+
use_none_as_default: bool = True) -> 'ValueSpec':
|
374
377
|
"""Marks none-able and returns `self`."""
|
375
378
|
|
376
379
|
@property
|
@@ -885,20 +885,31 @@ class CreateSchemaTest(unittest.TestCase):
|
|
885
885
|
self.assertEqual(s['e'], Field('e', vs.Enum(0, [0, 1])))
|
886
886
|
self.assertEqual(s.metadata, {'user_data': 2})
|
887
887
|
self.assertEqual(s['f'], Field('f', vs.Int()))
|
888
|
-
self.assertEqual(
|
888
|
+
self.assertEqual(
|
889
|
+
s['f1'], Field('f1', vs.Int().noneable(use_none_as_default=False))
|
890
|
+
)
|
889
891
|
self.assertEqual(s['g'], Field('g', vs.Float()))
|
890
|
-
self.assertEqual(
|
892
|
+
self.assertEqual(
|
893
|
+
s['g1'], Field('g1', vs.Float().noneable(use_none_as_default=False))
|
894
|
+
)
|
891
895
|
self.assertEqual(s['h'], Field('h', vs.Bool()))
|
892
|
-
self.assertEqual(
|
896
|
+
self.assertEqual(
|
897
|
+
s['h1'], Field('h1', vs.Bool().noneable(use_none_as_default=False))
|
898
|
+
)
|
893
899
|
self.assertEqual(s['i'], Field('i', vs.Str()))
|
894
|
-
self.assertEqual(
|
900
|
+
self.assertEqual(
|
901
|
+
s['i1'], Field('i1', vs.Str().noneable(use_none_as_default=False))
|
902
|
+
)
|
895
903
|
self.assertEqual(
|
896
904
|
s['j'], Field('j', vs.Union([vs.Int(), vs.Float(), vs.Bool()]))
|
897
905
|
)
|
898
906
|
self.assertEqual(s['k'], Field('k', vs.List(vs.Any())))
|
899
907
|
self.assertEqual(s['l'], Field('l', vs.List(vs.Int())))
|
900
908
|
self.assertEqual(s['L'], Field('L', vs.List(vs.Int())))
|
901
|
-
self.assertEqual(
|
909
|
+
self.assertEqual(
|
910
|
+
s['l1'],
|
911
|
+
Field('l1', vs.List(vs.Int()).noneable(use_none_as_default=False))
|
912
|
+
)
|
902
913
|
self.assertEqual(
|
903
914
|
s['l2'],
|
904
915
|
Field('l2', vs.List(vs.Str()).set_default(['black', 'white'])),
|
@@ -140,11 +140,18 @@ class ValueSpecBase(ValueSpec):
|
|
140
140
|
"""Returns True if current value spec accepts None."""
|
141
141
|
return self._is_noneable
|
142
142
|
|
143
|
-
def noneable(
|
143
|
+
def noneable(
|
144
|
+
self,
|
145
|
+
is_noneable: bool = True,
|
146
|
+
use_none_as_default: bool = True
|
147
|
+
) -> 'ValueSpecBase':
|
144
148
|
"""Marks None is acceptable and returns `self`."""
|
145
|
-
self._is_noneable =
|
146
|
-
if
|
147
|
-
self.
|
149
|
+
self._is_noneable = is_noneable
|
150
|
+
if is_noneable:
|
151
|
+
if use_none_as_default and not self.has_default: # pytype: disable=attribute-error
|
152
|
+
self.set_default(None, False)
|
153
|
+
elif self.default is None:
|
154
|
+
self.set_default(MISSING_VALUE, False)
|
148
155
|
return self
|
149
156
|
|
150
157
|
@property
|
@@ -942,11 +949,21 @@ class Enum(Generic, PrimitiveType):
|
|
942
949
|
return self.default
|
943
950
|
return self.apply(*args)
|
944
951
|
|
945
|
-
def noneable(
|
952
|
+
def noneable(
|
953
|
+
self,
|
954
|
+
is_noneable: bool = True,
|
955
|
+
use_none_as_default: bool = True
|
956
|
+
) -> 'Enum':
|
946
957
|
"""Noneable is specially treated for Enum."""
|
947
|
-
if
|
948
|
-
self._values
|
949
|
-
|
958
|
+
if is_noneable:
|
959
|
+
if None not in self._values:
|
960
|
+
self._values.append(None)
|
961
|
+
else:
|
962
|
+
if None in self._values:
|
963
|
+
self._values.remove(None)
|
964
|
+
if self._default is None:
|
965
|
+
self._default = MISSING_VALUE
|
966
|
+
self._is_noneable = is_noneable
|
950
967
|
return self
|
951
968
|
|
952
969
|
@property
|
@@ -1714,10 +1731,19 @@ class Dict(Generic, ValueSpecBase):
|
|
1714
1731
|
"""Returns the schema of this dict spec."""
|
1715
1732
|
return self._schema
|
1716
1733
|
|
1717
|
-
def noneable(
|
1734
|
+
def noneable(
|
1735
|
+
self,
|
1736
|
+
is_noneable: bool = True,
|
1737
|
+
use_none_as_default: bool = True
|
1738
|
+
) -> 'Dict':
|
1718
1739
|
"""Override noneable in Dict to always set default value None."""
|
1719
|
-
self._is_noneable =
|
1720
|
-
|
1740
|
+
self._is_noneable = is_noneable
|
1741
|
+
if is_noneable:
|
1742
|
+
if use_none_as_default:
|
1743
|
+
self.set_default(None, False)
|
1744
|
+
elif self._default is None:
|
1745
|
+
# Automatically generate default based on schema.
|
1746
|
+
self.set_default(MISSING_VALUE)
|
1721
1747
|
return self
|
1722
1748
|
|
1723
1749
|
def set_default(
|
@@ -2698,11 +2724,18 @@ class Union(Generic, ValueSpecBase):
|
|
2698
2724
|
value_types.update(child_value_type)
|
2699
2725
|
return tuple(value_types)
|
2700
2726
|
|
2701
|
-
def noneable(
|
2727
|
+
def noneable(
|
2728
|
+
self,
|
2729
|
+
is_noneable: bool = True,
|
2730
|
+
use_none_as_default: bool = True
|
2731
|
+
) -> 'Union':
|
2702
2732
|
"""Customized noneable for Union."""
|
2703
|
-
super().noneable(
|
2733
|
+
super().noneable(
|
2734
|
+
is_noneable=is_noneable,
|
2735
|
+
use_none_as_default=use_none_as_default
|
2736
|
+
)
|
2704
2737
|
for c in self._candidates:
|
2705
|
-
c.noneable()
|
2738
|
+
c.noneable(is_noneable=is_noneable, use_none_as_default=False)
|
2706
2739
|
return self
|
2707
2740
|
|
2708
2741
|
@property
|
@@ -61,6 +61,10 @@ class BoolTest(ValueSpecTest):
|
|
61
61
|
def test_noneable(self):
|
62
62
|
self.assertFalse(vs.Bool().is_noneable)
|
63
63
|
self.assertTrue(vs.Bool().noneable().is_noneable)
|
64
|
+
self.assertIsNone(vs.Bool().noneable().default)
|
65
|
+
self.assertFalse(vs.Bool().noneable(use_none_as_default=False).has_default)
|
66
|
+
self.assertFalse(vs.Bool().noneable().noneable(False).is_noneable)
|
67
|
+
self.assertFalse(vs.Bool().noneable().noneable(False).has_default)
|
64
68
|
|
65
69
|
def test_repr(self):
|
66
70
|
self.assertEqual(repr(vs.Bool()), 'Bool()')
|
@@ -212,6 +216,10 @@ class StrTest(ValueSpecTest):
|
|
212
216
|
def test_noneable(self):
|
213
217
|
self.assertFalse(vs.Str().is_noneable)
|
214
218
|
self.assertTrue(vs.Str().noneable().is_noneable)
|
219
|
+
self.assertIsNone(vs.Str().noneable().default)
|
220
|
+
self.assertFalse(vs.Str().noneable(use_none_as_default=False).has_default)
|
221
|
+
self.assertFalse(vs.Str().noneable().noneable(False).is_noneable)
|
222
|
+
self.assertFalse(vs.Str().noneable().noneable(False).has_default)
|
215
223
|
|
216
224
|
def test_repr(self):
|
217
225
|
self.assertEqual(repr(vs.Str()), 'Str()')
|
@@ -371,6 +379,10 @@ class IntTest(ValueSpecTest):
|
|
371
379
|
def test_noneable(self):
|
372
380
|
self.assertFalse(vs.Int().is_noneable)
|
373
381
|
self.assertTrue(vs.Int().noneable().is_noneable)
|
382
|
+
self.assertIsNone(vs.Int().noneable().default)
|
383
|
+
self.assertFalse(vs.Int().noneable(use_none_as_default=False).has_default)
|
384
|
+
self.assertFalse(vs.Int().noneable().noneable(False).is_noneable)
|
385
|
+
self.assertFalse(vs.Int().noneable().noneable(False).has_default)
|
374
386
|
|
375
387
|
def test_repr(self):
|
376
388
|
self.assertEqual(repr(vs.Int()), 'Int()')
|
@@ -584,6 +596,10 @@ class FloatTest(ValueSpecTest):
|
|
584
596
|
def test_noneable(self):
|
585
597
|
self.assertFalse(vs.Float().is_noneable)
|
586
598
|
self.assertTrue(vs.Float().noneable().is_noneable)
|
599
|
+
self.assertIsNone(vs.Float().noneable().default)
|
600
|
+
self.assertFalse(vs.Float().noneable(use_none_as_default=False).has_default)
|
601
|
+
self.assertFalse(vs.Float().noneable().noneable(False).is_noneable)
|
602
|
+
self.assertFalse(vs.Float().noneable().noneable(False).has_default)
|
587
603
|
|
588
604
|
def test_repr(self):
|
589
605
|
self.assertEqual(str(vs.Float()), 'Float()')
|
@@ -809,6 +825,18 @@ class EnumTest(ValueSpecTest):
|
|
809
825
|
self.assertEqual(
|
810
826
|
vs.Enum('a', ['a', 'b']).noneable(),
|
811
827
|
vs.Enum('a', ['a', 'b', None]))
|
828
|
+
self.assertEqual(
|
829
|
+
vs.Enum('a', ['a', 'b']).noneable().default,
|
830
|
+
'a'
|
831
|
+
)
|
832
|
+
self.assertEqual(
|
833
|
+
vs.Enum('a', ['a', None]).noneable(False),
|
834
|
+
vs.Enum('a', ['a'])
|
835
|
+
)
|
836
|
+
self.assertEqual(
|
837
|
+
vs.Enum(None, [None, 'a']).noneable(False),
|
838
|
+
vs.Enum(typed_missing.MISSING_VALUE, ['a'])
|
839
|
+
)
|
812
840
|
|
813
841
|
def test_repr(self):
|
814
842
|
self.assertEqual(
|
@@ -960,6 +988,12 @@ class ListTest(ValueSpecTest):
|
|
960
988
|
def test_noneable(self):
|
961
989
|
self.assertFalse(vs.List(vs.Int()).is_noneable)
|
962
990
|
self.assertTrue(vs.List(vs.Int()).noneable().is_noneable)
|
991
|
+
self.assertIsNone(vs.List(vs.Int()).noneable().default)
|
992
|
+
self.assertFalse(
|
993
|
+
vs.List(vs.Int()).noneable(use_none_as_default=False).has_default
|
994
|
+
)
|
995
|
+
self.assertFalse(vs.List(vs.Int()).noneable().noneable(False).is_noneable)
|
996
|
+
self.assertFalse(vs.List(vs.Int()).noneable().noneable(False).has_default)
|
963
997
|
|
964
998
|
def test_str(self):
|
965
999
|
self.assertEqual(
|
@@ -1268,6 +1302,12 @@ class TupleTest(ValueSpecTest):
|
|
1268
1302
|
def test_noneable(self):
|
1269
1303
|
self.assertFalse(vs.Tuple([vs.Int()]).is_noneable)
|
1270
1304
|
self.assertTrue(vs.Tuple([vs.Int()]).noneable().is_noneable)
|
1305
|
+
self.assertFalse(
|
1306
|
+
vs.Tuple([vs.Int()]).noneable().noneable(False).is_noneable
|
1307
|
+
)
|
1308
|
+
self.assertFalse(
|
1309
|
+
vs.Tuple([vs.Int()]).noneable().noneable(False).has_default
|
1310
|
+
)
|
1271
1311
|
|
1272
1312
|
def test_fixed_length(self):
|
1273
1313
|
self.assertFalse(vs.Tuple(vs.Int()).fixed_length)
|
@@ -1750,6 +1790,12 @@ class DictTest(ValueSpecTest):
|
|
1750
1790
|
def test_noneable(self):
|
1751
1791
|
self.assertFalse(vs.Dict().is_noneable)
|
1752
1792
|
self.assertTrue(vs.Dict().noneable().is_noneable)
|
1793
|
+
self.assertFalse(vs.Dict().noneable().noneable(False).is_noneable)
|
1794
|
+
self.assertFalse(vs.Dict().noneable().noneable(False).has_default)
|
1795
|
+
self.assertEqual(
|
1796
|
+
vs.Dict({'x': vs.Int(default=1)}).noneable().noneable(False).default,
|
1797
|
+
{'x': 1}
|
1798
|
+
)
|
1753
1799
|
|
1754
1800
|
def test_repr(self):
|
1755
1801
|
self.assertEqual(repr(vs.Dict()), 'Dict()')
|
@@ -2156,6 +2202,8 @@ class ObjectTest(ValueSpecTest):
|
|
2156
2202
|
self.assertFalse(vs.Object(self.A).is_noneable)
|
2157
2203
|
self.assertTrue(vs.Object(self.A).noneable().is_noneable)
|
2158
2204
|
self.assertTrue(vs.Object('Foo').noneable().is_noneable)
|
2205
|
+
self.assertFalse(vs.Object(self.A).noneable(False).is_noneable)
|
2206
|
+
self.assertFalse(vs.Object(self.A).noneable(False).has_default)
|
2159
2207
|
|
2160
2208
|
def test_repr(self):
|
2161
2209
|
self.assertEqual(repr(vs.Object(self.A)), 'Object(A)')
|
@@ -2416,6 +2464,8 @@ class CallableTest(ValueSpecTest):
|
|
2416
2464
|
def test_noneable(self):
|
2417
2465
|
self.assertFalse(vs.Callable().is_noneable)
|
2418
2466
|
self.assertTrue(vs.Callable().noneable().is_noneable)
|
2467
|
+
self.assertFalse(vs.Callable().noneable().noneable(False).is_noneable)
|
2468
|
+
self.assertFalse(vs.Callable().noneable().noneable(False).has_default)
|
2419
2469
|
|
2420
2470
|
def test_repr(self):
|
2421
2471
|
self.assertEqual(repr(vs.Callable()), 'Callable()')
|
@@ -2861,6 +2911,8 @@ class TypeTest(ValueSpecTest):
|
|
2861
2911
|
def test_noneable(self):
|
2862
2912
|
self.assertFalse(vs.Type(Exception).is_noneable)
|
2863
2913
|
self.assertTrue(vs.Type(Exception).noneable().is_noneable)
|
2914
|
+
self.assertFalse(vs.Type(Exception).noneable(False).is_noneable)
|
2915
|
+
self.assertFalse(vs.Type(Exception).noneable(False).has_default)
|
2864
2916
|
|
2865
2917
|
def test_repr(self):
|
2866
2918
|
self.assertEqual(repr(vs.Type(Exception)), 'Type(<class \'Exception\'>)')
|
@@ -3123,6 +3175,41 @@ class UnionTest(ValueSpecTest):
|
|
3123
3175
|
)
|
3124
3176
|
self.assertTrue(
|
3125
3177
|
vs.Union([vs.Int().noneable(), vs.Bool()]).is_noneable)
|
3178
|
+
self.assertFalse(
|
3179
|
+
vs.Union([vs.Int().noneable(), vs.Bool()]).has_default)
|
3180
|
+
self.assertTrue(
|
3181
|
+
vs.Union([vs.Int().noneable(), vs.Bool()]).candidates[0].has_default
|
3182
|
+
)
|
3183
|
+
self.assertIsNone(
|
3184
|
+
vs.Union(
|
3185
|
+
[vs.Int(), vs.Bool()]
|
3186
|
+
).noneable().default
|
3187
|
+
)
|
3188
|
+
self.assertFalse(
|
3189
|
+
vs.Union(
|
3190
|
+
[vs.Int(), vs.Bool()]
|
3191
|
+
).noneable(use_none_as_default=False).has_default
|
3192
|
+
)
|
3193
|
+
self.assertTrue(
|
3194
|
+
vs.Union(
|
3195
|
+
[vs.Int(), vs.Bool()]
|
3196
|
+
).noneable().candidates[0].is_noneable
|
3197
|
+
)
|
3198
|
+
self.assertFalse(
|
3199
|
+
vs.Union(
|
3200
|
+
[vs.Int(), vs.Bool()]
|
3201
|
+
).noneable().candidates[0].has_default
|
3202
|
+
)
|
3203
|
+
self.assertFalse(
|
3204
|
+
vs.Union(
|
3205
|
+
[vs.Int().noneable(), vs.Bool()]
|
3206
|
+
).noneable(False).candidates[0].is_noneable
|
3207
|
+
)
|
3208
|
+
self.assertFalse(
|
3209
|
+
vs.Union(
|
3210
|
+
[vs.Int().noneable(), vs.Bool()]
|
3211
|
+
).noneable(False).candidates[0].has_default
|
3212
|
+
)
|
3126
3213
|
|
3127
3214
|
def test_str(self):
|
3128
3215
|
self.assertEqual(
|
@@ -3132,8 +3219,8 @@ class UnionTest(ValueSpecTest):
|
|
3132
3219
|
'Union([Int(), Bool()], default=1, frozen=True)')
|
3133
3220
|
self.assertEqual(
|
3134
3221
|
repr(vs.Union([vs.Int(), vs.Bool()], default=1).noneable()),
|
3135
|
-
'Union([Int(
|
3136
|
-
'Bool(
|
3222
|
+
'Union([Int(noneable=True), '
|
3223
|
+
'Bool(noneable=True)], default=1, noneable=True)')
|
3137
3224
|
|
3138
3225
|
def test_annotation(self):
|
3139
3226
|
self.assertEqual(
|
@@ -3436,6 +3523,11 @@ class AnyTest(ValueSpecTest):
|
|
3436
3523
|
|
3437
3524
|
def test_noneable(self):
|
3438
3525
|
self.assertTrue(vs.Any().is_noneable)
|
3526
|
+
self.assertFalse(vs.Any().has_default)
|
3527
|
+
self.assertTrue(vs.Any().noneable().is_noneable)
|
3528
|
+
self.assertTrue(vs.Any().noneable().has_default)
|
3529
|
+
self.assertFalse(vs.Any().noneable(False).is_noneable)
|
3530
|
+
self.assertFalse(vs.Any().noneable().noneable(False).has_default)
|
3439
3531
|
|
3440
3532
|
def test_repr(self):
|
3441
3533
|
self.assertEqual(repr(vs.Any()), 'Any()')
|
pyglove/core/views/html/base.py
CHANGED
@@ -18,7 +18,7 @@ import functools
|
|
18
18
|
import html as html_lib
|
19
19
|
import inspect
|
20
20
|
import typing
|
21
|
-
from typing import Any, Callable, Dict, Iterable,
|
21
|
+
from typing import Any, Callable, Dict, Iterable, Optional, Sequence, Union
|
22
22
|
|
23
23
|
from pyglove.core import typing as pg_typing
|
24
24
|
from pyglove.core import utils
|
@@ -297,7 +297,7 @@ class Html(base.Content):
|
|
297
297
|
def element(
|
298
298
|
cls,
|
299
299
|
tag: str,
|
300
|
-
inner_html: Optional[
|
300
|
+
inner_html: Optional[utils.Nestable[WritableTypes]] = None,
|
301
301
|
*,
|
302
302
|
options: Union[str, Iterable[str], None] = None,
|
303
303
|
css_classes: NestableStr = None,
|
@@ -340,8 +340,11 @@ class Html(base.Content):
|
|
340
340
|
|
341
341
|
# Write the inner HTML.
|
342
342
|
if inner_html:
|
343
|
-
|
344
|
-
|
343
|
+
if isinstance(inner_html, list):
|
344
|
+
for child in utils.flatten(inner_html).values():
|
345
|
+
s.write(child)
|
346
|
+
else:
|
347
|
+
s.write(inner_html)
|
345
348
|
|
346
349
|
# Write the closing tag.
|
347
350
|
s.write(f'</{tag}>')
|
@@ -800,12 +800,14 @@ class HtmlTest(TestCase):
|
|
800
800
|
'div',
|
801
801
|
[
|
802
802
|
'<hr>',
|
803
|
-
lambda: '<div>bar</div>',
|
803
|
+
[[lambda: '<div>bar</div>']],
|
804
804
|
None,
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
805
|
+
[
|
806
|
+
Html.element(
|
807
|
+
'div',
|
808
|
+
css_classes='my_class',
|
809
|
+
).add_style('div.my_class { color: red; }')
|
810
|
+
]
|
809
811
|
]
|
810
812
|
)),
|
811
813
|
"""
|
@@ -16,6 +16,7 @@
|
|
16
16
|
from typing import Annotated, Any, Dict, List, Optional, Union
|
17
17
|
|
18
18
|
from pyglove.core import typing as pg_typing
|
19
|
+
from pyglove.core import utils as pg_utils
|
19
20
|
from pyglove.core.symbolic import flags as pg_flags
|
20
21
|
from pyglove.core.symbolic import object as pg_object
|
21
22
|
# pylint: disable=g-importing-member
|
@@ -157,6 +158,23 @@ class LabelGroup(HtmlControl):
|
|
157
158
|
'The label for the name of the group.'
|
158
159
|
] = None
|
159
160
|
|
161
|
+
@pg_utils.explicit_method_override
|
162
|
+
def __init__(
|
163
|
+
self,
|
164
|
+
labels: List[Union[Label, str, Html, None, List[Any]]],
|
165
|
+
name: Optional[Label] = None,
|
166
|
+
id: Optional[str] = None, # pylint: disable=redefined-builtin
|
167
|
+
css_classes: Optional[List[str]] = None,
|
168
|
+
styles: Optional[Dict[str, str]] = None,
|
169
|
+
**kwargs,
|
170
|
+
) -> None:
|
171
|
+
if labels:
|
172
|
+
labels = [l for l in pg_utils.flatten(labels).values() if l is not None]
|
173
|
+
super().__init__(
|
174
|
+
labels=labels, name=name, id=id, css_classes=css_classes or [],
|
175
|
+
styles=styles or {}, **kwargs
|
176
|
+
)
|
177
|
+
|
160
178
|
def _on_bound(self):
|
161
179
|
super()._on_bound()
|
162
180
|
with pg_flags.notify_on_change(False):
|
@@ -136,7 +136,7 @@ class LabelTest(TestCase):
|
|
136
136
|
class LabelGroupTest(TestCase):
|
137
137
|
|
138
138
|
def test_basic(self):
|
139
|
-
group = label_lib.LabelGroup(['foo', 'bar'], name='baz')
|
139
|
+
group = label_lib.LabelGroup(['foo', None, ['bar']], name='baz')
|
140
140
|
self.assert_html_content(
|
141
141
|
group,
|
142
142
|
(
|
@@ -13,8 +13,9 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
"""Tab control."""
|
15
15
|
|
16
|
-
from typing import Annotated, List, Literal, Optional, Union
|
16
|
+
from typing import Annotated, Any, Dict, List, Literal, Optional, Union
|
17
17
|
|
18
|
+
from pyglove.core import utils as pg_utils
|
18
19
|
from pyglove.core.symbolic import flags as pg_flags
|
19
20
|
from pyglove.core.symbolic import object as pg_object
|
20
21
|
|
@@ -73,6 +74,48 @@ class TabControl(HtmlControl):
|
|
73
74
|
|
74
75
|
interactive = True
|
75
76
|
|
77
|
+
@pg_utils.explicit_method_override
|
78
|
+
def __init__(
|
79
|
+
self,
|
80
|
+
tabs: List[Union[Tab, None, List[Any]]],
|
81
|
+
selected: Union[int, str, List[str]] = 0,
|
82
|
+
tab_position: Literal['top', 'left'] = 'top',
|
83
|
+
id: Optional[str] = None, # pylint: disable=redefined-builtin
|
84
|
+
css_classes: Optional[List[str]] = None,
|
85
|
+
styles: Optional[Dict[str, str]] = None,
|
86
|
+
**kwargs,
|
87
|
+
):
|
88
|
+
if tabs:
|
89
|
+
tabs = [t for t in pg_utils.flatten(tabs).values() if t is not None]
|
90
|
+
selected = self._find_tab_index(tabs, selected)
|
91
|
+
if selected == -1:
|
92
|
+
selected = 0
|
93
|
+
super().__init__(
|
94
|
+
tabs, selected=selected, tab_position=tab_position, id=id,
|
95
|
+
css_classes=css_classes or [], styles=styles or {},
|
96
|
+
**kwargs
|
97
|
+
)
|
98
|
+
|
99
|
+
@classmethod
|
100
|
+
def _find_tab_index(
|
101
|
+
cls,
|
102
|
+
tabs: List[Tab],
|
103
|
+
index_or_name: Union[int, str, List[str]]) -> int:
|
104
|
+
"""Finds the index of a tab identified by an index or name."""
|
105
|
+
if isinstance(index_or_name, int):
|
106
|
+
return index_or_name
|
107
|
+
if isinstance(index_or_name, str):
|
108
|
+
priority_choices = [index_or_name]
|
109
|
+
else:
|
110
|
+
priority_choices = index_or_name
|
111
|
+
|
112
|
+
name_index = {t.name: i for i, t in enumerate(tabs)}
|
113
|
+
for name in priority_choices:
|
114
|
+
index = name_index.get(name)
|
115
|
+
if index is not None:
|
116
|
+
return index
|
117
|
+
return -1
|
118
|
+
|
76
119
|
def append(self, tab: Tab) -> None:
|
77
120
|
with pg_flags.notify_on_change(False):
|
78
121
|
self.tabs.append(tab)
|
@@ -28,15 +28,87 @@ class TabControlTest(unittest.TestCase):
|
|
28
28
|
print(actual)
|
29
29
|
self.assertEqual(actual, expected)
|
30
30
|
|
31
|
+
def test_find_tab_index(self):
|
32
|
+
self.assertEqual(
|
33
|
+
tab_lib.TabControl._find_tab_index(
|
34
|
+
[
|
35
|
+
tab_lib.Tab('foo', base.Html('<h1>foo</h1>')),
|
36
|
+
tab_lib.Tab('bar', base.Html('<h1>bar</h1>')),
|
37
|
+
],
|
38
|
+
1
|
39
|
+
),
|
40
|
+
1
|
41
|
+
)
|
42
|
+
self.assertEqual(
|
43
|
+
tab_lib.TabControl._find_tab_index(
|
44
|
+
[
|
45
|
+
tab_lib.Tab('foo', base.Html('<h1>foo</h1>'), name='foo'),
|
46
|
+
tab_lib.Tab('bar', base.Html('<h1>bar</h1>'), name='bar'),
|
47
|
+
],
|
48
|
+
'bar'
|
49
|
+
),
|
50
|
+
1
|
51
|
+
)
|
52
|
+
self.assertEqual(
|
53
|
+
tab_lib.TabControl._find_tab_index(
|
54
|
+
[
|
55
|
+
tab_lib.Tab('foo', base.Html('<h1>foo</h1>'), name='foo'),
|
56
|
+
tab_lib.Tab('bar', base.Html('<h1>bar</h1>'), name='bar'),
|
57
|
+
],
|
58
|
+
'baz'
|
59
|
+
),
|
60
|
+
-1
|
61
|
+
)
|
62
|
+
self.assertEqual(
|
63
|
+
tab_lib.TabControl._find_tab_index(
|
64
|
+
[
|
65
|
+
tab_lib.Tab('foo', base.Html('<h1>foo</h1>'), name='foo'),
|
66
|
+
tab_lib.Tab('bar', base.Html('<h1>bar</h1>'), name='bar'),
|
67
|
+
tab_lib.Tab('baz', base.Html('<h1>baz</h1>'), name='baz'),
|
68
|
+
],
|
69
|
+
['baz', 'bar']
|
70
|
+
),
|
71
|
+
2
|
72
|
+
)
|
73
|
+
|
74
|
+
def test_init(self):
|
75
|
+
tab = tab_lib.TabControl(
|
76
|
+
[
|
77
|
+
[tab_lib.Tab('foo', base.Html('<h1>foo</h1>'), name='foo')],
|
78
|
+
None,
|
79
|
+
tab_lib.Tab('bar', base.Html('<h1>bar</h1>'), name='bar'),
|
80
|
+
],
|
81
|
+
1
|
82
|
+
)
|
83
|
+
self.assertEqual(tab.selected, 1)
|
84
|
+
self.assertEqual(tab.tabs[0].name, 'foo')
|
85
|
+
self.assertEqual(tab.tabs[1].name, 'bar')
|
86
|
+
|
87
|
+
# Cannot find tab by name.
|
88
|
+
tab = tab_lib.TabControl(
|
89
|
+
[
|
90
|
+
tab_lib.Tab('foo', base.Html('<h1>foo</h1>'), name='foo'),
|
91
|
+
tab_lib.Tab('bar', base.Html('<h1>bar</h1>'), name='bar'),
|
92
|
+
],
|
93
|
+
'baz'
|
94
|
+
)
|
95
|
+
self.assertEqual(tab.selected, 0)
|
96
|
+
self.assertEqual(tab.tabs[0].name, 'foo')
|
97
|
+
self.assertEqual(tab.tabs[1].name, 'bar')
|
98
|
+
|
31
99
|
def test_basic(self):
|
32
100
|
tab = tab_lib.TabControl([
|
33
|
-
tab_lib.Tab(
|
34
|
-
|
35
|
-
|
101
|
+
tab_lib.Tab(
|
102
|
+
'foo', base.Html('<h1>foo</h1>'), css_classes=['foo'], name='foo'
|
103
|
+
),
|
104
|
+
None,
|
105
|
+
[tab_lib.Tab('bar', base.Html('<h1>bar</h1>'), name='bar')],
|
106
|
+
], selected='bar')
|
107
|
+
self.assertEqual(tab.selected, 1)
|
36
108
|
elem_id = tab.element_id()
|
37
109
|
self.assert_html_content(
|
38
110
|
tab,
|
39
|
-
f"""<table class="tab-control"><tr><td><div class="tab-button-group top" id="{elem_id}-button-group"><button class="tab-button
|
111
|
+
f"""<table class="tab-control"><tr><td><div class="tab-button-group top" id="{elem_id}-button-group"><button class="tab-button foo" onclick="openTab(event, '{elem_id}', '{elem_id}-0')"><span class="label">foo</span></button><button class="tab-button selected" onclick="openTab(event, '{elem_id}', '{elem_id}-1')"><span class="label">bar</span></button></div></td></tr><tr><td><div class="tab-content-group top" id="{elem_id}-content-group"><div class="tab-content foo" id="{elem_id}-0"><h1>foo</h1></div><div class="tab-content selected" id="{elem_id}-1"><h1>bar</h1></div></div></td></tr></table>"""
|
40
112
|
)
|
41
113
|
with tab.track_scripts() as scripts:
|
42
114
|
tab.extend([
|
@@ -66,7 +66,7 @@ pyglove/core/patching/pattern_based_test.py,sha256=PW1EcVfsFPB6wtgwg3s4dzvigWn3b
|
|
66
66
|
pyglove/core/patching/rule_based.py,sha256=JAQp8mWeIOxwIdqusA3GmXia-fxQhQsxbUTmE329wF8,17038
|
67
67
|
pyglove/core/patching/rule_based_test.py,sha256=qfy0ILmczV_LMHWEnwo2y079OrJsGYO0nKxSZdmIUcI,18782
|
68
68
|
pyglove/core/symbolic/__init__.py,sha256=JZvyaEcS1QxA8MaAGANBWMmRTQcGk_6F0kjFxjokJqg,6002
|
69
|
-
pyglove/core/symbolic/base.py,sha256=
|
69
|
+
pyglove/core/symbolic/base.py,sha256=3vOHKtanrksBv5B_0HAq2R_CnE355aPx1B9lyJNb5us,77683
|
70
70
|
pyglove/core/symbolic/base_test.py,sha256=yASIHIuWiUB1jf4nN-Y4XOjyvr8eQfRpr7s_1LZsu78,7188
|
71
71
|
pyglove/core/symbolic/boilerplate.py,sha256=sQ3G25r5bo_UmIdjreL4jkAuQCXIHVlvUfGjjkNod6Y,5955
|
72
72
|
pyglove/core/symbolic/boilerplate_test.py,sha256=1CZ1W6kq3l-3tpaknhGFa04V18bO7vPzis5qzWnxHEs,5252
|
@@ -109,15 +109,15 @@ pyglove/core/tuning/sample_test.py,sha256=JqwDPy3EPC_VjU9dipk90jj1kovZB3Zb9hAjAl
|
|
109
109
|
pyglove/core/typing/__init__.py,sha256=MwraQ-6K8NYI_xk4V3oaVSkrPqiU9InDtAgXPSRBvik,14494
|
110
110
|
pyglove/core/typing/annotated.py,sha256=llaajIDj9GK-4kUGJoO4JsHU6ESPOra2SZ-jG6xmsOQ,3203
|
111
111
|
pyglove/core/typing/annotated_test.py,sha256=p1qid3R-jeiOTTxOVq6hXW8XFvn-h1cUzJWISPst2l8,2484
|
112
|
-
pyglove/core/typing/annotation_conversion.py,sha256=
|
113
|
-
pyglove/core/typing/annotation_conversion_test.py,sha256=
|
114
|
-
pyglove/core/typing/annotation_future_test.py,sha256=
|
112
|
+
pyglove/core/typing/annotation_conversion.py,sha256=txUrChAhMNeaukV-PSQEA9BCjtonUQDWFHxnpTE0_K8,15582
|
113
|
+
pyglove/core/typing/annotation_conversion_test.py,sha256=GW7e3e00jcYJjJGRvCvMtW4QBHyVwFtZVcETdh1yBS8,17540
|
114
|
+
pyglove/core/typing/annotation_future_test.py,sha256=tAVuzWNfW8R4e4l7fx88Q4nJDM2LPUogNKNAIIPAEWQ,3959
|
115
115
|
pyglove/core/typing/callable_ext.py,sha256=PiBQWPeUAH7Lgmf2xKCZqgK7N0OSrTdbnEkV8Ph31OA,9127
|
116
116
|
pyglove/core/typing/callable_ext_test.py,sha256=TnWKU4_ZjvpbHZFtFHgFvCMDiCos8VmLlODcM_7Xg8M,10156
|
117
117
|
pyglove/core/typing/callable_signature.py,sha256=DRpt7aShfkn8pb3SCiZzS_27eHbkQ_d2UB8BUhJjs0Q,27176
|
118
118
|
pyglove/core/typing/callable_signature_test.py,sha256=iQmHsKPhJPQlMikDhEyxKyq7yWyXI9juKCLYgKhrH3U,25145
|
119
|
-
pyglove/core/typing/class_schema.py,sha256=
|
120
|
-
pyglove/core/typing/class_schema_test.py,sha256=
|
119
|
+
pyglove/core/typing/class_schema.py,sha256=u_pxvykZlwyd5ycdfHpWYI8ccttmBbzexrajNVbEVG4,54553
|
120
|
+
pyglove/core/typing/class_schema_test.py,sha256=sJkE7ndDSIKb0EUcjZiVFOeJYDI7Hdu2GdPJCMgZxrI,29556
|
121
121
|
pyglove/core/typing/custom_typing.py,sha256=qdnIKHWNt5kZAAFdpQXra8bBu6RljMbbJ_YDG2mhAUA,2205
|
122
122
|
pyglove/core/typing/inspect.py,sha256=VLSz1KAunNm2hx0eEMjiwxKLl9FHlKr9nHelLT25iEA,7726
|
123
123
|
pyglove/core/typing/inspect_test.py,sha256=xclevobF0X8c_B5b1q1dkBJZN1TsVA1RUhk5l25DUCM,10248
|
@@ -128,8 +128,8 @@ pyglove/core/typing/type_conversion.py,sha256=0L4Cbsw_QiM-gpsn-4y-XLEIvwiUB16Clj
|
|
128
128
|
pyglove/core/typing/type_conversion_test.py,sha256=BhASOGvtKXmYLWKCELU1RVB_Nmt1V-saSkGogvsNL7E,5342
|
129
129
|
pyglove/core/typing/typed_missing.py,sha256=-l1omAu0jBZv5BnsFYXBqfvQwVBnmPh_X1wcIKD9bOk,2734
|
130
130
|
pyglove/core/typing/typed_missing_test.py,sha256=TCNsb1SRpFaVdxYn2mB_yaLuja8w5Qn5NP7uGiZVBWs,2301
|
131
|
-
pyglove/core/typing/value_specs.py,sha256=
|
132
|
-
pyglove/core/typing/value_specs_test.py,sha256=
|
131
|
+
pyglove/core/typing/value_specs.py,sha256=8E83QDZMb3lMXhgzfVNt9u6Bg3NPkvpjLXetjkps8UU,103263
|
132
|
+
pyglove/core/typing/value_specs_test.py,sha256=eGXVxdduIM-oEaapJS9Kh7WSQHRUFegLIJ1GEzQkKHA,131017
|
133
133
|
pyglove/core/utils/__init__.py,sha256=I2bRTzigU7qJVEATGlLUFkYzkiCBBCCEwrQyhsrRNmI,8602
|
134
134
|
pyglove/core/utils/common_traits.py,sha256=PWxOgPhG5H60ZwfO8xNAEGRjFUqqDZQBWQYomOfvdy8,3640
|
135
135
|
pyglove/core/utils/common_traits_test.py,sha256=DIuZB_1xfmeTVfWnGOguDQcDAM_iGgBOe8C-5CsIqBc,1122
|
@@ -159,18 +159,18 @@ pyglove/core/views/__init__.py,sha256=gll9ZBRYz4p_-LWOdzSR2a6UTWcJ8nR430trrP0yLC
|
|
159
159
|
pyglove/core/views/base.py,sha256=VVWlkyUPvZoNlcwMpuxt-AujGnuIJPS4Ym-jGBX0g9I,26262
|
160
160
|
pyglove/core/views/base_test.py,sha256=UKbr_1TANOAnP7V5ICGF0UEkunfSaHiJ4nXZXhA0SaU,16642
|
161
161
|
pyglove/core/views/html/__init__.py,sha256=Ff51MK1AKdzLM0kczTqLR3fRlJSJLDaFUdVyCu_7-eY,1085
|
162
|
-
pyglove/core/views/html/base.py,sha256=
|
163
|
-
pyglove/core/views/html/base_test.py,sha256=
|
164
|
-
pyglove/core/views/html/tree_view.py,sha256=
|
162
|
+
pyglove/core/views/html/base.py,sha256=jwckE6qyKOcbpgkDDYlB5lUKY6iaynENp6sV3kjl5Dc,15113
|
163
|
+
pyglove/core/views/html/base_test.py,sha256=vPBP8nX5rodjs2llKznQNMJ8ct6nBmwp3Y1eam7T4VQ,22838
|
164
|
+
pyglove/core/views/html/tree_view.py,sha256=TKDPSO0iruasAw68uhTw6aMTjtHAQxIGjhkvNhB4rXE,52102
|
165
165
|
pyglove/core/views/html/tree_view_test.py,sha256=whUorrw0eiDaZsEzGB2B3EN3wx0vLFuNEe2RBU03GeU,74707
|
166
166
|
pyglove/core/views/html/controls/__init__.py,sha256=61qs5pnJPCTECCGBtkbNfIV3KcCu7cxfVNBEtIg1lMo,1318
|
167
167
|
pyglove/core/views/html/controls/base.py,sha256=aSYFEo-I6Yc28TMk1ZFw_Za85xMo8EvgfQLKISsLSe8,7675
|
168
|
-
pyglove/core/views/html/controls/label.py,sha256=
|
169
|
-
pyglove/core/views/html/controls/label_test.py,sha256=
|
168
|
+
pyglove/core/views/html/controls/label.py,sha256=2u7z_6o-ANf6EbxufFl_fZ1VFSUrjNwDnrM784FFdNs,6312
|
169
|
+
pyglove/core/views/html/controls/label_test.py,sha256=_Fi6vMITup8iFYTiU_1w7FZCXaYp1eMmVBxub8JMYbs,5170
|
170
170
|
pyglove/core/views/html/controls/progress_bar.py,sha256=0an0eCbPCDjwrR58C16NwLZ-cf3Oy0wQerLsiNgGHmk,5235
|
171
171
|
pyglove/core/views/html/controls/progress_bar_test.py,sha256=kKOJDZQtBPkmNcgIBrRQkNNzcTm51ojuFBTRUEDSsp0,3506
|
172
|
-
pyglove/core/views/html/controls/tab.py,sha256=
|
173
|
-
pyglove/core/views/html/controls/tab_test.py,sha256=
|
172
|
+
pyglove/core/views/html/controls/tab.py,sha256=dn2rs6IBqvjtKvk0BC8ypVYqjjHsqJvP_Bh9y94QjMc,10818
|
173
|
+
pyglove/core/views/html/controls/tab_test.py,sha256=deRXg4LM4dzVgods5HVTXznrOWdddF6wrcl1RuhmRCA,5656
|
174
174
|
pyglove/core/views/html/controls/tooltip.py,sha256=01BbpuM1twf3FYMUT09_Ck5JSSONe8QE9RmyA9nhCnU,3092
|
175
175
|
pyglove/core/views/html/controls/tooltip_test.py,sha256=17BY-WmZKpz9tCbySPcwG6KJyfeE_MeMyKxtfxorBQ0,3194
|
176
176
|
pyglove/ext/__init__.py,sha256=3jp8cJvKW6PENOZlmVAbT0w-GBRn_kjhc0wDX3XjpOE,755
|
@@ -212,8 +212,8 @@ pyglove/ext/scalars/randoms.py,sha256=LkMIIx7lOq_lvJvVS3BrgWGuWl7Pi91-lA-O8x_gZs
|
|
212
212
|
pyglove/ext/scalars/randoms_test.py,sha256=nEhiqarg8l_5EOucp59CYrpO2uKxS1pe0hmBdZUzRNM,2000
|
213
213
|
pyglove/ext/scalars/step_wise.py,sha256=IDw3tuTpv0KVh7AN44W43zqm1-E0HWPUlytWOQC9w3Y,3789
|
214
214
|
pyglove/ext/scalars/step_wise_test.py,sha256=TL1vJ19xVx2t5HKuyIzGoogF7N3Rm8YhLE6JF7i0iy8,2540
|
215
|
-
pyglove-0.4.5.
|
216
|
-
pyglove-0.4.5.
|
217
|
-
pyglove-0.4.5.
|
218
|
-
pyglove-0.4.5.
|
219
|
-
pyglove-0.4.5.
|
215
|
+
pyglove-0.4.5.dev202504300810.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
216
|
+
pyglove-0.4.5.dev202504300810.dist-info/METADATA,sha256=vipGZt0dKLshbJ9kyOhGBG0ven_rk7lFpUe-X7seIL0,7089
|
217
|
+
pyglove-0.4.5.dev202504300810.dist-info/WHEEL,sha256=ooBFpIzZCPdw3uqIQsOo4qqbA4ZRPxHnOH7peeONza0,91
|
218
|
+
pyglove-0.4.5.dev202504300810.dist-info/top_level.txt,sha256=wITzJSKcj8GZUkbq-MvUQnFadkiuAv_qv5qQMw0fIow,8
|
219
|
+
pyglove-0.4.5.dev202504300810.dist-info/RECORD,,
|
{pyglove-0.4.5.dev202504280824.dist-info → pyglove-0.4.5.dev202504300810.dist-info}/licenses/LICENSE
RENAMED
File without changes
|
{pyglove-0.4.5.dev202504280824.dist-info → pyglove-0.4.5.dev202504300810.dist-info}/top_level.txt
RENAMED
File without changes
|