pyglove 0.4.5.dev202411132359__py3-none-any.whl → 0.4.5.dev202501250807__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 +40 -21
- pyglove/core/coding/__init__.py +42 -0
- pyglove/core/coding/errors.py +111 -0
- pyglove/core/coding/errors_test.py +98 -0
- pyglove/core/coding/execution.py +312 -0
- pyglove/core/coding/execution_test.py +333 -0
- pyglove/core/{object_utils/codegen.py → coding/function_generation.py} +10 -4
- pyglove/core/{object_utils/codegen_test.py → coding/function_generation_test.py} +5 -7
- pyglove/core/coding/parsing.py +153 -0
- pyglove/core/coding/parsing_test.py +150 -0
- pyglove/core/coding/permissions.py +100 -0
- pyglove/core/coding/permissions_test.py +93 -0
- pyglove/core/geno/base.py +53 -38
- pyglove/core/geno/base_test.py +2 -4
- pyglove/core/geno/categorical.py +36 -27
- pyglove/core/geno/custom.py +18 -15
- pyglove/core/geno/numerical.py +19 -16
- pyglove/core/geno/space.py +3 -4
- pyglove/core/hyper/base.py +6 -6
- pyglove/core/hyper/categorical.py +91 -52
- pyglove/core/hyper/custom.py +7 -7
- pyglove/core/hyper/custom_test.py +9 -10
- pyglove/core/hyper/derived.py +30 -22
- pyglove/core/hyper/derived_test.py +3 -5
- pyglove/core/hyper/dynamic_evaluation.py +3 -4
- pyglove/core/hyper/evolvable.py +57 -46
- pyglove/core/hyper/numerical.py +48 -24
- pyglove/core/hyper/numerical_test.py +9 -9
- pyglove/core/hyper/object_template.py +58 -46
- pyglove/core/logging_test.py +0 -2
- pyglove/core/patching/object_factory.py +4 -4
- pyglove/core/patching/pattern_based.py +4 -4
- pyglove/core/patching/rule_based.py +4 -3
- pyglove/core/symbolic/__init__.py +4 -0
- pyglove/core/symbolic/base.py +200 -136
- pyglove/core/symbolic/base_test.py +17 -19
- pyglove/core/symbolic/boilerplate.py +4 -5
- pyglove/core/symbolic/class_wrapper.py +10 -14
- pyglove/core/symbolic/class_wrapper_test.py +2 -2
- pyglove/core/symbolic/compounding.py +2 -2
- pyglove/core/symbolic/compounding_test.py +2 -4
- pyglove/core/symbolic/contextual_object.py +288 -0
- pyglove/core/symbolic/contextual_object_test.py +327 -0
- pyglove/core/symbolic/dict.py +115 -87
- pyglove/core/symbolic/dict_test.py +188 -131
- pyglove/core/symbolic/diff.py +12 -12
- pyglove/core/symbolic/flags.py +1 -1
- pyglove/core/symbolic/functor.py +16 -15
- pyglove/core/symbolic/functor_test.py +2 -4
- pyglove/core/symbolic/inferred.py +2 -2
- pyglove/core/symbolic/list.py +70 -47
- pyglove/core/symbolic/list_test.py +117 -98
- pyglove/core/symbolic/object.py +59 -58
- pyglove/core/symbolic/object_test.py +143 -90
- pyglove/core/symbolic/origin.py +5 -7
- pyglove/core/symbolic/pure_symbolic.py +4 -3
- pyglove/core/symbolic/ref.py +33 -16
- pyglove/core/symbolic/ref_test.py +17 -0
- pyglove/core/tuning/local_backend.py +2 -2
- pyglove/core/tuning/protocols.py +3 -3
- pyglove/core/typing/annotation_conversion.py +8 -3
- pyglove/core/typing/annotation_conversion_test.py +8 -0
- pyglove/core/typing/callable_ext.py +11 -13
- pyglove/core/typing/callable_signature.py +22 -19
- pyglove/core/typing/callable_signature_test.py +3 -5
- pyglove/core/typing/class_schema.py +93 -54
- pyglove/core/typing/class_schema_test.py +4 -5
- pyglove/core/typing/custom_typing.py +5 -4
- pyglove/core/typing/key_specs.py +5 -7
- pyglove/core/typing/key_specs_test.py +4 -4
- pyglove/core/typing/type_conversion.py +4 -5
- pyglove/core/typing/type_conversion_test.py +12 -12
- pyglove/core/typing/typed_missing.py +6 -7
- pyglove/core/typing/typed_missing_test.py +7 -8
- pyglove/core/typing/value_specs.py +287 -144
- pyglove/core/typing/value_specs_test.py +148 -25
- pyglove/core/utils/__init__.py +172 -0
- pyglove/core/{object_utils → utils}/common_traits.py +2 -2
- pyglove/core/{object_utils → utils}/common_traits_test.py +1 -3
- pyglove/core/utils/contextual.py +147 -0
- pyglove/core/utils/contextual_test.py +88 -0
- pyglove/core/{object_utils → utils}/docstr_utils_test.py +1 -3
- pyglove/core/{object_utils → utils}/error_utils.py +3 -3
- pyglove/core/{object_utils → utils}/error_utils_test.py +1 -1
- pyglove/core/{object_utils → utils}/formatting.py +1 -1
- pyglove/core/{object_utils → utils}/formatting_test.py +1 -2
- pyglove/core/{object_utils → utils}/hierarchical.py +23 -25
- pyglove/core/{object_utils → utils}/hierarchical_test.py +3 -5
- pyglove/core/{object_utils → utils}/json_conversion.py +1 -1
- pyglove/core/{object_utils → utils}/json_conversion_test.py +1 -3
- pyglove/core/{object_utils → utils}/missing.py +2 -2
- pyglove/core/{object_utils → utils}/missing_test.py +2 -4
- pyglove/core/utils/text_color.py +128 -0
- pyglove/core/utils/text_color_test.py +94 -0
- pyglove/core/{object_utils → utils}/thread_local_test.py +1 -3
- pyglove/core/{object_utils → utils}/timing.py +21 -10
- pyglove/core/{object_utils → utils}/timing_test.py +14 -12
- pyglove/core/{object_utils → utils}/value_location.py +2 -2
- pyglove/core/{object_utils → utils}/value_location_test.py +2 -4
- pyglove/core/views/base.py +25 -29
- pyglove/core/views/html/base.py +15 -16
- pyglove/core/views/html/controls/base.py +46 -9
- pyglove/core/views/html/controls/label.py +13 -2
- pyglove/core/views/html/controls/label_test.py +27 -8
- pyglove/core/views/html/controls/progress_bar.py +3 -5
- pyglove/core/views/html/controls/progress_bar_test.py +2 -2
- pyglove/core/views/html/controls/tab.py +217 -66
- pyglove/core/views/html/controls/tab_test.py +46 -15
- pyglove/core/views/html/tree_view.py +39 -37
- {pyglove-0.4.5.dev202411132359.dist-info → pyglove-0.4.5.dev202501250807.dist-info}/METADATA +17 -3
- pyglove-0.4.5.dev202501250807.dist-info/RECORD +218 -0
- {pyglove-0.4.5.dev202411132359.dist-info → pyglove-0.4.5.dev202501250807.dist-info}/WHEEL +1 -1
- pyglove/core/object_utils/__init__.py +0 -164
- pyglove-0.4.5.dev202411132359.dist-info/RECORD +0 -203
- /pyglove/core/{object_utils → utils}/docstr_utils.py +0 -0
- /pyglove/core/{object_utils → utils}/thread_local.py +0 -0
- {pyglove-0.4.5.dev202411132359.dist-info → pyglove-0.4.5.dev202501250807.dist-info}/LICENSE +0 -0
- {pyglove-0.4.5.dev202411132359.dist-info → pyglove-0.4.5.dev202501250807.dist-info}/top_level.txt +0 -0
@@ -11,16 +11,14 @@
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
|
-
"""Tests for pyglove.Dict."""
|
15
|
-
|
16
14
|
import copy
|
17
15
|
import inspect
|
18
16
|
import io
|
19
17
|
import pickle
|
20
18
|
import unittest
|
21
19
|
|
22
|
-
from pyglove.core import object_utils
|
23
20
|
from pyglove.core import typing as pg_typing
|
21
|
+
from pyglove.core import utils
|
24
22
|
from pyglove.core.symbolic import base
|
25
23
|
from pyglove.core.symbolic import flags
|
26
24
|
from pyglove.core.symbolic import inferred
|
@@ -31,7 +29,7 @@ from pyglove.core.symbolic.pure_symbolic import NonDeterministic
|
|
31
29
|
from pyglove.core.symbolic.pure_symbolic import PureSymbolic
|
32
30
|
|
33
31
|
|
34
|
-
MISSING_VALUE =
|
32
|
+
MISSING_VALUE = utils.MISSING_VALUE
|
35
33
|
|
36
34
|
|
37
35
|
class DictTest(unittest.TestCase):
|
@@ -44,14 +42,14 @@ class DictTest(unittest.TestCase):
|
|
44
42
|
self.assertEqual(len(sd), 0)
|
45
43
|
|
46
44
|
# Schemaless dict created from a regular dict.
|
47
|
-
sd = Dict({'a': 1})
|
45
|
+
sd = Dict({'a': 1, 1: 1})
|
48
46
|
self.assertIsNone(sd.value_spec)
|
49
|
-
self.assertEqual(sd,
|
47
|
+
self.assertEqual(sd, {'a': 1, 1: 1})
|
50
48
|
|
51
49
|
# Schemaless dict created from key value pairs.
|
52
|
-
sd = Dict((('a', 1),))
|
50
|
+
sd = Dict((('a', 1), (1, 1)))
|
53
51
|
self.assertIsNone(sd.value_spec)
|
54
|
-
self.assertEqual(sd,
|
52
|
+
self.assertEqual(sd, {'a': 1, 1: 1})
|
55
53
|
|
56
54
|
# Schemaless dict created from keyword args.
|
57
55
|
sd = Dict(a=1)
|
@@ -59,9 +57,9 @@ class DictTest(unittest.TestCase):
|
|
59
57
|
self.assertEqual(sd, dict(a=1))
|
60
58
|
|
61
59
|
# Schemaless dict created from both a regular dict and keyword args.
|
62
|
-
sd = Dict({'a': 1}, a=2)
|
60
|
+
sd = Dict({'a': 1, 1: 1}, a=2)
|
63
61
|
self.assertIsNone(sd.value_spec)
|
64
|
-
self.assertEqual(sd,
|
62
|
+
self.assertEqual(sd, {'a': 2, 1: 1})
|
65
63
|
|
66
64
|
# Schematized dict.
|
67
65
|
vs = pg_typing.Dict([('a', pg_typing.Int())])
|
@@ -305,8 +303,8 @@ class DictTest(unittest.TestCase):
|
|
305
303
|
with self.assertRaisesRegex(
|
306
304
|
base.WritePermissionError, 'Cannot modify field of a sealed Dict.'):
|
307
305
|
sd['b'] = 1
|
308
|
-
with self.assertRaisesRegex(KeyError, 'Key must be string type'):
|
309
|
-
sd[0] = 1
|
306
|
+
with self.assertRaisesRegex(KeyError, 'Key must be string or int type'):
|
307
|
+
sd[0.5] = 1
|
310
308
|
|
311
309
|
# Set item in a schematized dict.
|
312
310
|
sd = Dict(value_spec=pg_typing.Dict([('a', pg_typing.Int(default=0))]))
|
@@ -786,35 +784,45 @@ class DictTest(unittest.TestCase):
|
|
786
784
|
sd.missing_values(flatten=False), {'a': MISSING_VALUE})
|
787
785
|
|
788
786
|
# A non-schematized dict has a schematized child.
|
789
|
-
sd = Dict(x
|
787
|
+
sd = Dict({'x': sd, 1: sd})
|
790
788
|
self.assertIsNone(sd.value_spec)
|
791
|
-
self.assertEqual(
|
789
|
+
self.assertEqual(
|
790
|
+
sd.missing_values(), {'x.a': MISSING_VALUE, '[1].a': MISSING_VALUE}
|
791
|
+
)
|
792
792
|
|
793
793
|
def test_sym_has(self):
|
794
|
-
sd = Dict(x
|
794
|
+
sd = Dict({'x': 1, 1: dict(a=3), 'y': Dict({'z': 2, 2: 3})})
|
795
795
|
self.assertTrue(sd.sym_has('x'))
|
796
|
+
self.assertTrue(sd.sym_has(1))
|
796
797
|
self.assertTrue(sd.sym_has('y.z'))
|
797
|
-
self.assertTrue(sd.sym_has(
|
798
|
+
self.assertTrue(sd.sym_has('[1].a'))
|
799
|
+
self.assertTrue(sd.sym_has('y[2]'))
|
800
|
+
self.assertTrue(sd.sym_has(utils.KeyPath.parse('y.z')))
|
798
801
|
self.assertFalse(sd.sym_has('x.z'))
|
799
802
|
|
800
803
|
def test_sym_get(self):
|
801
|
-
sd = Dict(x
|
804
|
+
sd = Dict({'x': 1, 1: dict(a=3), 'y': Dict({'z': 2, 2: 3})})
|
802
805
|
self.assertEqual(sd.sym_get('x'), 1)
|
806
|
+
self.assertEqual(sd.sym_get(1), dict(a=3))
|
803
807
|
self.assertEqual(sd.sym_get('y.z'), 2)
|
808
|
+
self.assertEqual(sd.sym_get('[1].a'), 3)
|
809
|
+
self.assertEqual(sd.sym_get('y[2]'), 3)
|
804
810
|
self.assertIsNone(sd.sym_get('x.z', None))
|
805
811
|
with self.assertRaisesRegex(
|
806
812
|
KeyError, 'Cannot query sub-key \'z\' of object.'):
|
807
813
|
sd.sym_get('x.z')
|
808
814
|
|
809
815
|
def test_sym_hasattr(self):
|
810
|
-
sd = Dict(x
|
816
|
+
sd = Dict({'x': 1, 1: dict(a=3), 'y': Dict({'z': 2, 2: 3})})
|
811
817
|
self.assertTrue(sd.sym_hasattr('x'))
|
818
|
+
self.assertTrue(sd.sym_hasattr(1))
|
812
819
|
self.assertFalse(sd.sym_hasattr('y.z'))
|
813
820
|
self.assertFalse(sd.sym_hasattr('a'))
|
814
821
|
|
815
822
|
def test_sym_getattr(self):
|
816
|
-
sd = Dict(x
|
823
|
+
sd = Dict({'x': 1, 1: dict(a=3), 'y': Dict({'z': 2, 2: 3})})
|
817
824
|
self.assertEqual(sd.sym_getattr('x'), 1)
|
825
|
+
self.assertEqual(sd.sym_getattr(1), dict(a=3))
|
818
826
|
self.assertIsNone(sd.sym_getattr('a', None))
|
819
827
|
with self.assertRaisesRegex(
|
820
828
|
AttributeError,
|
@@ -832,8 +840,9 @@ class DictTest(unittest.TestCase):
|
|
832
840
|
with self.assertRaisesRegex(AttributeError, 'z'):
|
833
841
|
_ = sd.sym_inferred('z')
|
834
842
|
|
835
|
-
sd = Dict(y=1, x=
|
843
|
+
sd = Dict(y=1, x={'x': Dict(y=inferred.ValueFromParentChain()), 1: 2})
|
836
844
|
self.assertEqual(sd.x.x.y, 1)
|
845
|
+
self.assertEqual(sd.x[1], 2)
|
837
846
|
|
838
847
|
def test_sym_field(self):
|
839
848
|
sd = Dict(x=1, y=Dict(z=2))
|
@@ -859,9 +868,9 @@ class DictTest(unittest.TestCase):
|
|
859
868
|
self.assertIs(sd.sym_attr_field('x'), spec.schema.get_field('x'))
|
860
869
|
|
861
870
|
def test_sym_keys(self):
|
862
|
-
sd = Dict(x
|
871
|
+
sd = Dict({'x': 1, 'y': 2, 1: 3})
|
863
872
|
self.assertEqual(next(sd.sym_keys()), 'x')
|
864
|
-
self.assertEqual(list(sd.sym_keys()), ['x', 'y'])
|
873
|
+
self.assertEqual(list(sd.sym_keys()), ['x', 'y', 1])
|
865
874
|
|
866
875
|
sd = Dict(x=1, z=3, y=2, value_spec=pg_typing.Dict([
|
867
876
|
(pg_typing.StrKey(), pg_typing.Int())
|
@@ -874,9 +883,9 @@ class DictTest(unittest.TestCase):
|
|
874
883
|
self.assertEqual(list(sd.sym_keys()), ['x', 'y'])
|
875
884
|
|
876
885
|
def test_sym_values(self):
|
877
|
-
sd = Dict(x
|
886
|
+
sd = Dict({'x': 1, 'y': 2, 1: 3})
|
878
887
|
self.assertEqual(next(sd.sym_values()), 1)
|
879
|
-
self.assertEqual(list(sd.sym_values()), [1, 2])
|
888
|
+
self.assertEqual(list(sd.sym_values()), [1, 2, 3])
|
880
889
|
|
881
890
|
sd = Dict(x=1, z=3, y=2, value_spec=pg_typing.Dict([
|
882
891
|
(pg_typing.StrKey(), pg_typing.Int())
|
@@ -891,9 +900,9 @@ class DictTest(unittest.TestCase):
|
|
891
900
|
)
|
892
901
|
|
893
902
|
def test_sym_items(self):
|
894
|
-
sd = Dict(x
|
903
|
+
sd = Dict({'x': 1, 'y': 2, 1: 3})
|
895
904
|
self.assertEqual(next(sd.sym_items()), ('x', 1))
|
896
|
-
self.assertEqual(list(sd.sym_items()), [('x', 1), ('y', 2)])
|
905
|
+
self.assertEqual(list(sd.sym_items()), [('x', 1), ('y', 2), (1, 3)])
|
897
906
|
|
898
907
|
sd = Dict(x=1, z=3, y=2, value_spec=pg_typing.Dict([
|
899
908
|
(pg_typing.StrKey(), pg_typing.Int())
|
@@ -917,9 +926,9 @@ class DictTest(unittest.TestCase):
|
|
917
926
|
|
918
927
|
def test_sym_rebind(self):
|
919
928
|
# Refer to RebindTest for more detailed tests.
|
920
|
-
sd = Dict(x
|
921
|
-
sd.sym_rebind(x=2)
|
922
|
-
self.assertEqual(sd,
|
929
|
+
sd = Dict({'x': 1, 'y': 2, 1: 3})
|
930
|
+
sd.sym_rebind({1: 4}, x=2)
|
931
|
+
self.assertEqual(sd, {'x': 2, 'y': 2, 1: 4})
|
923
932
|
|
924
933
|
def test_sym_clone(self):
|
925
934
|
class A:
|
@@ -997,6 +1006,12 @@ class DictTest(unittest.TestCase):
|
|
997
1006
|
y: int = 1
|
998
1007
|
use_symbolic_comparison = True
|
999
1008
|
|
1009
|
+
sd = Dict({'x': A(2), 1: B(2)})
|
1010
|
+
self.assertEqual(sd.sym_nondefault(), {'x.x': 2, '[1].y': 2})
|
1011
|
+
|
1012
|
+
sd = Dict({'x': A(1), 1: B(1)})
|
1013
|
+
self.assertEqual(sd.sym_nondefault(), {'x.x': 1})
|
1014
|
+
|
1000
1015
|
sd = Dict(x=1, y=dict(a1=A(1)), value_spec=pg_typing.Dict([
|
1001
1016
|
('x', pg_typing.Int(default=0)),
|
1002
1017
|
('y', pg_typing.Dict([
|
@@ -1080,7 +1095,7 @@ class DictTest(unittest.TestCase):
|
|
1080
1095
|
self.assertTrue(Dict().sym_eq(Dict()))
|
1081
1096
|
self.assertTrue(base.eq(Dict(), Dict()))
|
1082
1097
|
|
1083
|
-
self.assertEqual(Dict(a
|
1098
|
+
self.assertEqual(Dict({'a': 1, 1: 2}), Dict({'a': 1, 1: 2}))
|
1084
1099
|
self.assertTrue(Dict(a=1).sym_eq(Dict(a=1)))
|
1085
1100
|
self.assertTrue(base.eq(Dict(a=1), Dict(a=1)))
|
1086
1101
|
self.assertTrue(
|
@@ -1130,6 +1145,8 @@ class DictTest(unittest.TestCase):
|
|
1130
1145
|
self.assertTrue(base.ne(Dict(), 1))
|
1131
1146
|
self.assertNotEqual(Dict(), Dict(a=1))
|
1132
1147
|
self.assertTrue(base.ne(Dict(), Dict(a=1)))
|
1148
|
+
self.assertNotEqual(Dict({1: 1}), Dict({'1': 1}))
|
1149
|
+
self.assertTrue(base.ne(Dict({1: 1}), Dict({'1': 1})))
|
1133
1150
|
self.assertNotEqual(Dict(a=0), Dict(a=1))
|
1134
1151
|
self.assertTrue(base.ne(Dict(a=0), Dict(a=1)))
|
1135
1152
|
|
@@ -1192,6 +1209,7 @@ class DictTest(unittest.TestCase):
|
|
1192
1209
|
self.assertEqual(hash(Dict(a=1)), hash(Dict(a=1)))
|
1193
1210
|
self.assertEqual(hash(Dict(a=dict(x=1))), hash(Dict(a=dict(x=1))))
|
1194
1211
|
self.assertNotEqual(hash(Dict()), hash(Dict(a=1)))
|
1212
|
+
self.assertNotEqual(hash(Dict({'1': 1})), hash(Dict({1: 1})))
|
1195
1213
|
self.assertNotEqual(hash(Dict(a=1)), hash(Dict(a=2)))
|
1196
1214
|
|
1197
1215
|
class A:
|
@@ -1247,7 +1265,7 @@ class DictTest(unittest.TestCase):
|
|
1247
1265
|
self.assertEqual(sd.x.a.sym_path, 'x.a')
|
1248
1266
|
self.assertEqual(sd.y[0].b.sym_path, 'y[0].b')
|
1249
1267
|
|
1250
|
-
sd.sym_setpath(
|
1268
|
+
sd.sym_setpath(utils.KeyPath('a'))
|
1251
1269
|
self.assertEqual(sd.sym_path, 'a')
|
1252
1270
|
self.assertEqual(sd.x.sym_path, 'a.x')
|
1253
1271
|
self.assertEqual(sd.x.a.sym_path, 'a.x.a')
|
@@ -1704,96 +1722,112 @@ class RebindTest(unittest.TestCase):
|
|
1704
1722
|
'd': 'foo', # Unchanged.
|
1705
1723
|
'e': 'bar'
|
1706
1724
|
})
|
1707
|
-
self.assertEqual(
|
1708
|
-
|
1709
|
-
|
1710
|
-
|
1711
|
-
|
1712
|
-
|
1713
|
-
|
1714
|
-
|
1715
|
-
|
1716
|
-
|
1717
|
-
|
1718
|
-
|
1719
|
-
|
1720
|
-
|
1721
|
-
|
1722
|
-
|
1723
|
-
|
1724
|
-
|
1725
|
-
|
1726
|
-
|
1727
|
-
|
1728
|
-
|
1729
|
-
|
1730
|
-
|
1731
|
-
|
1732
|
-
|
1733
|
-
|
1734
|
-
|
1735
|
-
|
1736
|
-
|
1737
|
-
|
1738
|
-
|
1739
|
-
|
1740
|
-
|
1741
|
-
|
1742
|
-
|
1743
|
-
|
1744
|
-
|
1745
|
-
|
1746
|
-
|
1747
|
-
|
1748
|
-
|
1749
|
-
|
1750
|
-
|
1751
|
-
|
1752
|
-
|
1753
|
-
|
1754
|
-
|
1755
|
-
|
1756
|
-
|
1757
|
-
|
1758
|
-
|
1759
|
-
|
1760
|
-
|
1761
|
-
|
1762
|
-
|
1763
|
-
|
1764
|
-
|
1765
|
-
|
1766
|
-
|
1767
|
-
|
1768
|
-
|
1769
|
-
|
1770
|
-
|
1771
|
-
|
1772
|
-
|
1773
|
-
|
1774
|
-
|
1775
|
-
|
1776
|
-
|
1777
|
-
|
1778
|
-
|
1779
|
-
|
1780
|
-
|
1781
|
-
|
1782
|
-
|
1783
|
-
|
1784
|
-
|
1785
|
-
|
1786
|
-
|
1787
|
-
|
1788
|
-
|
1789
|
-
|
1790
|
-
|
1791
|
-
|
1792
|
-
|
1793
|
-
|
1794
|
-
|
1795
|
-
|
1796
|
-
|
1725
|
+
self.assertEqual(
|
1726
|
+
updates,
|
1727
|
+
[
|
1728
|
+
{ # Notification to `sd.c[0]`.
|
1729
|
+
'p': base.FieldUpdate(
|
1730
|
+
utils.KeyPath.parse('c[0].p'),
|
1731
|
+
target=sd.c[0],
|
1732
|
+
field=None,
|
1733
|
+
old_value=1,
|
1734
|
+
new_value=MISSING_VALUE,
|
1735
|
+
),
|
1736
|
+
'q': base.FieldUpdate(
|
1737
|
+
utils.KeyPath.parse('c[0].q'),
|
1738
|
+
target=sd.c[0],
|
1739
|
+
field=None,
|
1740
|
+
old_value=MISSING_VALUE,
|
1741
|
+
new_value=2,
|
1742
|
+
),
|
1743
|
+
},
|
1744
|
+
{ # Notification to `sd.c`.
|
1745
|
+
'[0].p': base.FieldUpdate(
|
1746
|
+
utils.KeyPath.parse('c[0].p'),
|
1747
|
+
target=sd.c[0],
|
1748
|
+
field=None,
|
1749
|
+
old_value=1,
|
1750
|
+
new_value=MISSING_VALUE,
|
1751
|
+
),
|
1752
|
+
'[0].q': base.FieldUpdate(
|
1753
|
+
utils.KeyPath.parse('c[0].q'),
|
1754
|
+
target=sd.c[0],
|
1755
|
+
field=None,
|
1756
|
+
old_value=MISSING_VALUE,
|
1757
|
+
new_value=2,
|
1758
|
+
),
|
1759
|
+
},
|
1760
|
+
{ # Notification to `sd.b.y`.
|
1761
|
+
'z': base.FieldUpdate(
|
1762
|
+
utils.KeyPath.parse('b.y.z'),
|
1763
|
+
target=sd.b.y,
|
1764
|
+
field=None,
|
1765
|
+
old_value=MISSING_VALUE,
|
1766
|
+
new_value=1,
|
1767
|
+
),
|
1768
|
+
},
|
1769
|
+
{ # Notification to `sd.b`.
|
1770
|
+
'x': base.FieldUpdate(
|
1771
|
+
utils.KeyPath.parse('b.x'),
|
1772
|
+
target=sd.b,
|
1773
|
+
field=None,
|
1774
|
+
old_value=1,
|
1775
|
+
new_value=2,
|
1776
|
+
),
|
1777
|
+
'y.z': base.FieldUpdate(
|
1778
|
+
utils.KeyPath.parse('b.y.z'),
|
1779
|
+
target=sd.b.y,
|
1780
|
+
field=None,
|
1781
|
+
old_value=MISSING_VALUE,
|
1782
|
+
new_value=1,
|
1783
|
+
),
|
1784
|
+
},
|
1785
|
+
{ # Notification to `sd`.
|
1786
|
+
'a': base.FieldUpdate(
|
1787
|
+
utils.KeyPath.parse('a'),
|
1788
|
+
target=sd,
|
1789
|
+
field=None,
|
1790
|
+
old_value=1,
|
1791
|
+
new_value=2,
|
1792
|
+
),
|
1793
|
+
'b.x': base.FieldUpdate(
|
1794
|
+
utils.KeyPath.parse('b.x'),
|
1795
|
+
target=sd.b,
|
1796
|
+
field=None,
|
1797
|
+
old_value=1,
|
1798
|
+
new_value=2,
|
1799
|
+
),
|
1800
|
+
'b.y.z': base.FieldUpdate(
|
1801
|
+
utils.KeyPath.parse('b.y.z'),
|
1802
|
+
target=sd.b.y,
|
1803
|
+
field=None,
|
1804
|
+
old_value=MISSING_VALUE,
|
1805
|
+
new_value=1,
|
1806
|
+
),
|
1807
|
+
'c[0].p': base.FieldUpdate(
|
1808
|
+
utils.KeyPath.parse('c[0].p'),
|
1809
|
+
target=sd.c[0],
|
1810
|
+
field=None,
|
1811
|
+
old_value=1,
|
1812
|
+
new_value=MISSING_VALUE,
|
1813
|
+
),
|
1814
|
+
'c[0].q': base.FieldUpdate(
|
1815
|
+
utils.KeyPath.parse('c[0].q'),
|
1816
|
+
target=sd.c[0],
|
1817
|
+
field=None,
|
1818
|
+
old_value=MISSING_VALUE,
|
1819
|
+
new_value=2,
|
1820
|
+
),
|
1821
|
+
'e': base.FieldUpdate(
|
1822
|
+
utils.KeyPath.parse('e'),
|
1823
|
+
target=sd,
|
1824
|
+
field=None,
|
1825
|
+
old_value=MISSING_VALUE,
|
1826
|
+
new_value='bar',
|
1827
|
+
),
|
1828
|
+
},
|
1829
|
+
],
|
1830
|
+
)
|
1797
1831
|
|
1798
1832
|
def test_rebind_with_fn(self):
|
1799
1833
|
sd = Dict(a=1, b=dict(x=2, y='foo', z=[0, 1, 2]))
|
@@ -1845,10 +1879,6 @@ class RebindTest(unittest.TestCase):
|
|
1845
1879
|
ValueError, 'There are no values to rebind.'):
|
1846
1880
|
Dict().rebind({})
|
1847
1881
|
|
1848
|
-
with self.assertRaisesRegex(
|
1849
|
-
KeyError, 'Key must be string type. Encountered 1'):
|
1850
|
-
Dict().rebind({1: 1})
|
1851
|
-
|
1852
1882
|
with self.assertRaisesRegex(
|
1853
1883
|
ValueError, 'Required value is not specified.'):
|
1854
1884
|
Dict(a=1, value_spec=pg_typing.Dict([('a', pg_typing.Int())])).rebind({
|
@@ -1859,13 +1889,17 @@ class SerializationTest(unittest.TestCase):
|
|
1859
1889
|
"""Dedicated tests for `pg.Dict` serialization."""
|
1860
1890
|
|
1861
1891
|
def test_schemaless(self):
|
1862
|
-
sd = Dict()
|
1892
|
+
sd = Dict({1: 2})
|
1863
1893
|
sd.b = 0
|
1864
1894
|
sd.c = None
|
1865
1895
|
sd.a = 'foo'
|
1866
1896
|
|
1867
1897
|
# Key order is preserved.
|
1868
|
-
self.assertEqual(
|
1898
|
+
self.assertEqual(
|
1899
|
+
sd.to_json_str(),
|
1900
|
+
'{"n_:1": 2, "b": 0, "c": null, "a": "foo"}'
|
1901
|
+
)
|
1902
|
+
self.assertEqual(base.from_json_str(sd.to_json_str()), sd)
|
1869
1903
|
|
1870
1904
|
def test_schematized(self):
|
1871
1905
|
sd = Dict.partial(
|
@@ -2076,7 +2110,7 @@ class FormatTest(unittest.TestCase):
|
|
2076
2110
|
|
2077
2111
|
def test_compact_python_format(self):
|
2078
2112
|
self.assertEqual(
|
2079
|
-
|
2113
|
+
utils.format(
|
2080
2114
|
self._dict, compact=True, python_format=True, markdown=True
|
2081
2115
|
),
|
2082
2116
|
"`{'a1': 1, 'a2': {'b1': {'c1': [{'d1': MISSING_VALUE, "
|
@@ -2086,9 +2120,12 @@ class FormatTest(unittest.TestCase):
|
|
2086
2120
|
|
2087
2121
|
def test_noncompact_python_format(self):
|
2088
2122
|
self.assertEqual(
|
2089
|
-
|
2090
|
-
self._dict,
|
2091
|
-
|
2123
|
+
utils.format(
|
2124
|
+
self._dict,
|
2125
|
+
compact=False,
|
2126
|
+
verbose=False,
|
2127
|
+
python_format=True,
|
2128
|
+
markdown=True,
|
2092
2129
|
),
|
2093
2130
|
inspect.cleandoc("""
|
2094
2131
|
```
|
@@ -2188,6 +2225,7 @@ class FormatTest(unittest.TestCase):
|
|
2188
2225
|
}"""))
|
2189
2226
|
|
2190
2227
|
def test_noncompact_verbose_with_extra_blankline_for_field_docstr(self):
|
2228
|
+
self.maxDiff = None
|
2191
2229
|
self.assertEqual(
|
2192
2230
|
self._dict.format(
|
2193
2231
|
compact=False, verbose=True, extra_blankline_for_field_docstr=True),
|
@@ -2306,6 +2344,25 @@ class FormatTest(unittest.TestCase):
|
|
2306
2344
|
'{\n x = 1,\n y = True,\n z = {\n v = 1,\n w = True\n }\n}'
|
2307
2345
|
)
|
2308
2346
|
|
2347
|
+
def test_compact_int_key(self):
|
2348
|
+
d = Dict({1: 1, '1': 2})
|
2349
|
+
self.assertEqual(d.format(compact=True), '{[1]=1, 1=2}')
|
2350
|
+
self.assertEqual(
|
2351
|
+
d.format(compact=True, python_format=True),
|
2352
|
+
'{1: 1, \'1\': 2}'
|
2353
|
+
)
|
2354
|
+
|
2355
|
+
def test_non_compact_int_key(self):
|
2356
|
+
d = Dict({1: 1, '1': 2})
|
2357
|
+
self.assertEqual(
|
2358
|
+
d.format(compact=False, verbose=False),
|
2359
|
+
'{\n [1] = 1,\n 1 = 2\n}'
|
2360
|
+
)
|
2361
|
+
self.assertEqual(
|
2362
|
+
d.format(compact=False, python_format=True),
|
2363
|
+
'{\n 1: 1,\n \'1\': 2\n}'
|
2364
|
+
)
|
2365
|
+
|
2309
2366
|
|
2310
2367
|
def _on_change_callback(updates):
|
2311
2368
|
del updates
|
pyglove/core/symbolic/diff.py
CHANGED
@@ -15,8 +15,8 @@
|
|
15
15
|
|
16
16
|
from typing import Any, Callable, Optional, Sequence, Tuple, Union
|
17
17
|
|
18
|
-
from pyglove.core import object_utils
|
19
18
|
from pyglove.core import typing as pg_typing
|
19
|
+
from pyglove.core import utils
|
20
20
|
from pyglove.core.symbolic import base
|
21
21
|
from pyglove.core.symbolic import list as pg_list
|
22
22
|
from pyglove.core.symbolic import object as pg_object
|
@@ -112,8 +112,7 @@ class Diff(PureSymbolic, pg_object.Object, tree_view.HtmlTreeView.Extension):
|
|
112
112
|
return 'No diff'
|
113
113
|
# When there is no diff, but the same value needs to be displayed
|
114
114
|
# we simply return the value.
|
115
|
-
return
|
116
|
-
self.value, compact, verbose, root_indent, **kwargs)
|
115
|
+
return utils.format(self.value, compact, verbose, root_indent, **kwargs)
|
117
116
|
if self.is_leaf:
|
118
117
|
exclude_keys = kwargs.pop('exclude_keys', None)
|
119
118
|
exclude_keys = exclude_keys or set()
|
@@ -129,7 +128,7 @@ class Diff(PureSymbolic, pg_object.Object, tree_view.HtmlTreeView.Extension):
|
|
129
128
|
verbose=verbose,
|
130
129
|
root_indent=root_indent,
|
131
130
|
cls_name='',
|
132
|
-
bracket_type=
|
131
|
+
bracket_type=utils.BracketType.SQUARE,
|
133
132
|
**kwargs,
|
134
133
|
)
|
135
134
|
if self.left is self.right:
|
@@ -141,7 +140,7 @@ class Diff(PureSymbolic, pg_object.Object, tree_view.HtmlTreeView.Extension):
|
|
141
140
|
verbose=verbose,
|
142
141
|
root_indent=root_indent,
|
143
142
|
cls_name=cls_name,
|
144
|
-
bracket_type=
|
143
|
+
bracket_type=utils.BracketType.ROUND,
|
145
144
|
**kwargs,
|
146
145
|
)
|
147
146
|
|
@@ -155,7 +154,7 @@ class Diff(PureSymbolic, pg_object.Object, tree_view.HtmlTreeView.Extension):
|
|
155
154
|
max_summary_len_for_str: int = 80,
|
156
155
|
**kwargs,
|
157
156
|
) -> Optional[tree_view.Html]:
|
158
|
-
|
157
|
+
# pytype: enable=annotation-type-mismatch
|
159
158
|
if not bool(self):
|
160
159
|
v = self.value
|
161
160
|
if (isinstance(v, (int, float, bool, type(None)))
|
@@ -199,11 +198,11 @@ class Diff(PureSymbolic, pg_object.Object, tree_view.HtmlTreeView.Extension):
|
|
199
198
|
*,
|
200
199
|
view: tree_view.HtmlTreeView,
|
201
200
|
parent: Any = None,
|
202
|
-
root_path: Optional[
|
201
|
+
root_path: Optional[utils.KeyPath] = None,
|
203
202
|
css_classes: Optional[Sequence[str]] = None,
|
204
|
-
**kwargs
|
203
|
+
**kwargs,
|
205
204
|
) -> tree_view.Html:
|
206
|
-
root_path = root_path or
|
205
|
+
root_path = root_path or utils.KeyPath()
|
207
206
|
if not bool(self):
|
208
207
|
if self.value == Diff.MISSING:
|
209
208
|
root = tree_view.Html.element(
|
@@ -353,7 +352,8 @@ def diff(
|
|
353
352
|
right: Any,
|
354
353
|
flatten: bool = False,
|
355
354
|
collapse: Union[bool, str, Callable[[Any, Any], bool]] = 'same_type',
|
356
|
-
mode: str = 'diff'
|
355
|
+
mode: str = 'diff',
|
356
|
+
) -> utils.Nestable[Diff]:
|
357
357
|
"""Inspect the symbolic diff between two objects.
|
358
358
|
|
359
359
|
For example::
|
@@ -479,7 +479,7 @@ def diff(
|
|
479
479
|
assert isinstance(container, base.Symbolic)
|
480
480
|
return container.sym_hasattr, container.sym_getattr, container.sym_items
|
481
481
|
|
482
|
-
def _diff(x, y) -> Tuple[
|
482
|
+
def _diff(x, y) -> Tuple[utils.Nestable[Diff], bool]:
|
483
483
|
if x is y or x == y:
|
484
484
|
return (Diff(x, y), False)
|
485
485
|
if not _should_collapse(x, y):
|
@@ -533,5 +533,5 @@ def diff(
|
|
533
533
|
if not has_diff and mode == 'diff':
|
534
534
|
diff_value = Diff()
|
535
535
|
if flatten:
|
536
|
-
diff_value =
|
536
|
+
diff_value = utils.flatten(diff_value)
|
537
537
|
return diff_value
|
pyglove/core/symbolic/flags.py
CHANGED