pyglove 0.4.5.dev202501030808__py3-none-any.whl → 0.4.5.dev202501060809__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 +24 -21
- 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 +2 -4
- 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/base.py +167 -131
- pyglove/core/symbolic/base_test.py +17 -19
- pyglove/core/symbolic/boilerplate.py +4 -5
- pyglove/core/symbolic/class_wrapper.py +9 -9
- pyglove/core/symbolic/compounding.py +2 -2
- pyglove/core/symbolic/compounding_test.py +2 -4
- pyglove/core/symbolic/dict.py +70 -54
- pyglove/core/symbolic/dict_test.py +117 -100
- 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 +42 -40
- pyglove/core/symbolic/object_test.py +95 -88
- pyglove/core/symbolic/origin.py +5 -7
- pyglove/core/symbolic/pure_symbolic.py +4 -3
- pyglove/core/symbolic/ref.py +12 -8
- pyglove/core/tuning/local_backend.py +2 -2
- pyglove/core/tuning/protocols.py +3 -3
- pyglove/core/typing/annotation_conversion.py +3 -3
- pyglove/core/typing/callable_ext.py +11 -13
- pyglove/core/typing/callable_signature.py +19 -18
- pyglove/core/typing/callable_signature_test.py +3 -5
- pyglove/core/typing/class_schema.py +48 -44
- pyglove/core/typing/class_schema_test.py +3 -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 +210 -141
- pyglove/core/typing/value_specs_test.py +12 -13
- pyglove/core/utils/__init__.py +159 -0
- pyglove/core/{object_utils → utils}/common_traits_test.py +1 -3
- 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_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/{object_utils → utils}/thread_local_test.py +1 -3
- pyglove/core/{object_utils → utils}/timing.py +3 -3
- pyglove/core/{object_utils → utils}/timing_test.py +2 -3
- 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 +14 -15
- pyglove/core/views/html/controls/base.py +5 -5
- pyglove/core/views/html/controls/progress_bar.py +3 -5
- pyglove/core/views/html/tree_view.py +37 -35
- {pyglove-0.4.5.dev202501030808.dist-info → pyglove-0.4.5.dev202501060809.dist-info}/METADATA +1 -1
- {pyglove-0.4.5.dev202501030808.dist-info → pyglove-0.4.5.dev202501060809.dist-info}/RECORD +90 -90
- {pyglove-0.4.5.dev202501030808.dist-info → pyglove-0.4.5.dev202501060809.dist-info}/WHEEL +1 -1
- pyglove/core/object_utils/__init__.py +0 -161
- /pyglove/core/{object_utils → utils}/common_traits.py +0 -0
- /pyglove/core/{object_utils → utils}/docstr_utils.py +0 -0
- /pyglove/core/{object_utils → utils}/json_conversion.py +0 -0
- /pyglove/core/{object_utils → utils}/thread_local.py +0 -0
- {pyglove-0.4.5.dev202501030808.dist-info → pyglove-0.4.5.dev202501060809.dist-info}/LICENSE +0 -0
- {pyglove-0.4.5.dev202501030808.dist-info → pyglove-0.4.5.dev202501060809.dist-info}/top_level.txt +0 -0
pyglove/core/symbolic/object.py
CHANGED
@@ -20,8 +20,8 @@ import typing
|
|
20
20
|
from typing import Any, Dict, Iterator, List, Optional, Sequence, Union
|
21
21
|
|
22
22
|
from pyglove.core import coding
|
23
|
-
from pyglove.core import object_utils
|
24
23
|
from pyglove.core import typing as pg_typing
|
24
|
+
from pyglove.core import utils
|
25
25
|
from pyglove.core.symbolic import base
|
26
26
|
from pyglove.core.symbolic import dict as pg_dict
|
27
27
|
from pyglove.core.symbolic import flags
|
@@ -116,7 +116,7 @@ class ObjectMeta(abc.ABCMeta):
|
|
116
116
|
|
117
117
|
# Register class with 'type' property.
|
118
118
|
for key in serialization_keys:
|
119
|
-
|
119
|
+
utils.JSONConvertible.register(
|
120
120
|
key, cls, flags.is_repeated_class_registration_allowed()
|
121
121
|
)
|
122
122
|
|
@@ -309,7 +309,7 @@ class Object(base.Symbolic, metaclass=ObjectMeta):
|
|
309
309
|
Args:
|
310
310
|
user_cls: The source class that calls this class method.
|
311
311
|
"""
|
312
|
-
|
312
|
+
utils.ensure_explicit_method_override(
|
313
313
|
cls.__init__,
|
314
314
|
(
|
315
315
|
'`pg.Object.__init__` is a PyGlove managed method. For setting up '
|
@@ -317,7 +317,8 @@ class Object(base.Symbolic, metaclass=ObjectMeta):
|
|
317
317
|
'`_on_init()`. If you do have a need to override `__init__` and '
|
318
318
|
'know the implications, please decorate your overridden method '
|
319
319
|
'with `@pg.explicit_method_override`.'
|
320
|
-
)
|
320
|
+
),
|
321
|
+
)
|
321
322
|
|
322
323
|
# Set `__serialization_key__` before JSONConvertible.__init_subclass__
|
323
324
|
# is called.
|
@@ -363,11 +364,11 @@ class Object(base.Symbolic, metaclass=ObjectMeta):
|
|
363
364
|
"""Normalizes the schema before applying it."""
|
364
365
|
|
365
366
|
schema.set_name(cls.__type_name__)
|
366
|
-
docstr =
|
367
|
+
docstr = utils.docstr(cls)
|
367
368
|
if docstr:
|
368
369
|
schema.set_description(docstr.description)
|
369
370
|
|
370
|
-
def _formalize_field(path:
|
371
|
+
def _formalize_field(path: utils.KeyPath, node: Any) -> bool:
|
371
372
|
"""Formalize field."""
|
372
373
|
if isinstance(node, pg_typing.Field):
|
373
374
|
field = node
|
@@ -385,27 +386,29 @@ class Object(base.Symbolic, metaclass=ObjectMeta):
|
|
385
386
|
if isinstance(field.value, pg_typing.Dict):
|
386
387
|
if field.value.schema is not None:
|
387
388
|
field.value.schema.set_name(f'{schema.name}.{path.path}')
|
388
|
-
|
389
|
-
|
389
|
+
utils.traverse(
|
390
|
+
field.value.schema.fields, _formalize_field, None, path
|
391
|
+
)
|
390
392
|
elif isinstance(field.value, pg_typing.List):
|
391
|
-
_formalize_field(
|
393
|
+
_formalize_field(utils.KeyPath(0, path), field.value.element)
|
392
394
|
elif isinstance(field.value, pg_typing.Tuple):
|
393
395
|
for i, elem in enumerate(field.value.elements):
|
394
|
-
_formalize_field(
|
396
|
+
_formalize_field(utils.KeyPath(i, path), elem)
|
395
397
|
elif isinstance(field.value, pg_typing.Union):
|
396
398
|
for i, c in enumerate(field.value.candidates):
|
397
399
|
_formalize_field(
|
398
|
-
|
399
|
-
pg_typing.Field(field.key, c, 'Union sub-type.')
|
400
|
+
utils.KeyPath(i, path),
|
401
|
+
pg_typing.Field(field.key, c, 'Union sub-type.'),
|
402
|
+
)
|
400
403
|
return True
|
401
404
|
|
402
|
-
|
405
|
+
utils.traverse(schema.fields, _formalize_field)
|
403
406
|
return schema
|
404
407
|
|
405
408
|
@classmethod
|
406
409
|
def _finalize_init_arg_list(cls) -> List[str]:
|
407
410
|
"""Finalizes init_arg_list based on schema."""
|
408
|
-
|
411
|
+
# Update `init_arg_list`` based on the updated schema.
|
409
412
|
init_arg_list = cls.__schema__.metadata.get('init_arg_list', None)
|
410
413
|
if init_arg_list is None:
|
411
414
|
# Inherit from the first non-empty base if they have the same signature.
|
@@ -476,7 +479,7 @@ class Object(base.Symbolic, metaclass=ObjectMeta):
|
|
476
479
|
# Create a new `__init__` that passes through all the arguments to
|
477
480
|
# in `pg.Object.__init__`. This is needed for each class to use different
|
478
481
|
# signature.
|
479
|
-
@
|
482
|
+
@utils.explicit_method_override
|
480
483
|
@functools.wraps(pseudo_init)
|
481
484
|
def _init(self, *args, **kwargs):
|
482
485
|
# We pass through the arguments to `Object.__init__` instead of
|
@@ -539,8 +542,8 @@ class Object(base.Symbolic, metaclass=ObjectMeta):
|
|
539
542
|
json_value: Any,
|
540
543
|
*,
|
541
544
|
allow_partial: bool = False,
|
542
|
-
root_path: Optional[
|
543
|
-
**kwargs
|
545
|
+
root_path: Optional[utils.KeyPath] = None,
|
546
|
+
**kwargs,
|
544
547
|
) -> 'Object':
|
545
548
|
"""Class method that load an symbolic Object from a JSON value.
|
546
549
|
|
@@ -588,15 +591,16 @@ class Object(base.Symbolic, metaclass=ObjectMeta):
|
|
588
591
|
for k, v in json_value.items()
|
589
592
|
})
|
590
593
|
|
591
|
-
@
|
594
|
+
@utils.explicit_method_override
|
592
595
|
def __init__(
|
593
596
|
self,
|
594
597
|
*args,
|
595
598
|
allow_partial: bool = False,
|
596
599
|
sealed: Optional[bool] = None,
|
597
|
-
root_path: Optional[
|
600
|
+
root_path: Optional[utils.KeyPath] = None,
|
598
601
|
explicit_init: bool = False,
|
599
|
-
**kwargs
|
602
|
+
**kwargs,
|
603
|
+
):
|
600
604
|
"""Create an Object instance.
|
601
605
|
|
602
606
|
Args:
|
@@ -638,8 +642,8 @@ class Object(base.Symbolic, metaclass=ObjectMeta):
|
|
638
642
|
# Fill field_args and init_args from **kwargs.
|
639
643
|
_, unmatched_keys = self.__class__.__schema__.resolve(list(kwargs.keys()))
|
640
644
|
if unmatched_keys:
|
641
|
-
arg_phrase =
|
642
|
-
keys_str =
|
645
|
+
arg_phrase = utils.auto_plural(len(unmatched_keys), 'argument')
|
646
|
+
keys_str = utils.comma_delimited_str(unmatched_keys)
|
643
647
|
raise TypeError(
|
644
648
|
f'{self.__class__.__name__}.__init__() got unexpected '
|
645
649
|
f'keyword {arg_phrase}: {keys_str}')
|
@@ -659,8 +663,8 @@ class Object(base.Symbolic, metaclass=ObjectMeta):
|
|
659
663
|
field_args[vararg_name] = list(args[num_named_args:])
|
660
664
|
args = args[:num_named_args]
|
661
665
|
elif len(args) > len(init_arg_names):
|
662
|
-
arg_phrase =
|
663
|
-
was_phrase =
|
666
|
+
arg_phrase = utils.auto_plural(len(init_arg_names), 'argument')
|
667
|
+
was_phrase = utils.auto_plural(len(args), 'was', 'were')
|
664
668
|
raise TypeError(
|
665
669
|
f'{self.__class__.__name__}.__init__() takes '
|
666
670
|
f'{len(init_arg_names)} positional {arg_phrase} but {len(args)} '
|
@@ -672,7 +676,7 @@ class Object(base.Symbolic, metaclass=ObjectMeta):
|
|
672
676
|
|
673
677
|
for k, v in kwargs.items():
|
674
678
|
if k in field_args:
|
675
|
-
values_str =
|
679
|
+
values_str = utils.comma_delimited_str([field_args[k], v])
|
676
680
|
raise TypeError(
|
677
681
|
f'{self.__class__.__name__}.__init__() got multiple values for '
|
678
682
|
f'argument \'{k}\': {values_str}.')
|
@@ -687,8 +691,8 @@ class Object(base.Symbolic, metaclass=ObjectMeta):
|
|
687
691
|
and field.key not in field_args):
|
688
692
|
missing_args.append(str(field.key))
|
689
693
|
if missing_args:
|
690
|
-
arg_phrase =
|
691
|
-
keys_str =
|
694
|
+
arg_phrase = utils.auto_plural(len(missing_args), 'argument')
|
695
|
+
keys_str = utils.comma_delimited_str(missing_args)
|
692
696
|
raise TypeError(
|
693
697
|
f'{self.__class__.__name__}.__init__() missing {len(missing_args)} '
|
694
698
|
f'required {arg_phrase}: {keys_str}.')
|
@@ -738,8 +742,7 @@ class Object(base.Symbolic, metaclass=ObjectMeta):
|
|
738
742
|
and during __init__.
|
739
743
|
"""
|
740
744
|
|
741
|
-
def _on_change(self,
|
742
|
-
field_updates: Dict[object_utils.KeyPath, base.FieldUpdate]):
|
745
|
+
def _on_change(self, field_updates: Dict[utils.KeyPath, base.FieldUpdate]):
|
743
746
|
"""Event that is triggered when field values in the subtree are updated.
|
744
747
|
|
745
748
|
This event will be called
|
@@ -759,8 +762,7 @@ class Object(base.Symbolic, metaclass=ObjectMeta):
|
|
759
762
|
del field_updates
|
760
763
|
return self._on_bound()
|
761
764
|
|
762
|
-
def _on_path_change(
|
763
|
-
self, old_path: object_utils.KeyPath, new_path: object_utils.KeyPath):
|
765
|
+
def _on_path_change(self, old_path: utils.KeyPath, new_path: utils.KeyPath):
|
764
766
|
"""Event that is triggered after the symbolic path changes."""
|
765
767
|
del old_path, new_path
|
766
768
|
|
@@ -839,8 +841,8 @@ class Object(base.Symbolic, metaclass=ObjectMeta):
|
|
839
841
|
return self._sym_attributes.sym_getattr(key)
|
840
842
|
|
841
843
|
def _sym_rebind(
|
842
|
-
self, path_value_pairs: Dict[
|
843
|
-
|
844
|
+
self, path_value_pairs: Dict[utils.KeyPath, Any]
|
845
|
+
) -> List[base.FieldUpdate]:
|
844
846
|
"""Rebind current object using object-form members."""
|
845
847
|
if base.treats_as_sealed(self):
|
846
848
|
raise base.WritePermissionError(
|
@@ -879,9 +881,8 @@ class Object(base.Symbolic, metaclass=ObjectMeta):
|
|
879
881
|
return self
|
880
882
|
|
881
883
|
def _update_children_paths(
|
882
|
-
self,
|
883
|
-
|
884
|
-
new_path: object_utils.KeyPath) -> None:
|
884
|
+
self, old_path: utils.KeyPath, new_path: utils.KeyPath
|
885
|
+
) -> None:
|
885
886
|
"""Update children paths according to root_path of current node."""
|
886
887
|
self._sym_attributes.sym_setpath(new_path)
|
887
888
|
self._on_path_change(old_path, new_path)
|
@@ -965,10 +966,10 @@ class Object(base.Symbolic, metaclass=ObjectMeta):
|
|
965
966
|
return self.sym_hash()
|
966
967
|
return super().__hash__()
|
967
968
|
|
968
|
-
def sym_jsonify(self, **kwargs) ->
|
969
|
+
def sym_jsonify(self, **kwargs) -> utils.JSONValueType:
|
969
970
|
"""Converts current object to a dict of plain Python objects."""
|
970
971
|
json_dict = {
|
971
|
-
|
972
|
+
utils.JSONConvertible.TYPE_NAME_KEY: (
|
972
973
|
self.__class__.__serialization_key__
|
973
974
|
)
|
974
975
|
}
|
@@ -987,8 +988,9 @@ class Object(base.Symbolic, metaclass=ObjectMeta):
|
|
987
988
|
root_indent,
|
988
989
|
cls_name=self.__class__.__name__,
|
989
990
|
key_as_attribute=True,
|
990
|
-
bracket_type=
|
991
|
-
**kwargs
|
991
|
+
bracket_type=utils.BracketType.ROUND,
|
992
|
+
**kwargs,
|
993
|
+
)
|
992
994
|
|
993
995
|
|
994
996
|
base.Symbolic.ObjectType = Object
|
@@ -11,8 +11,6 @@
|
|
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.Object."""
|
15
|
-
|
16
14
|
import copy
|
17
15
|
import inspect
|
18
16
|
import io
|
@@ -23,8 +21,8 @@ import typing
|
|
23
21
|
from typing import Any
|
24
22
|
import unittest
|
25
23
|
|
26
|
-
from pyglove.core import object_utils
|
27
24
|
from pyglove.core import typing as pg_typing
|
25
|
+
from pyglove.core import utils
|
28
26
|
from pyglove.core.symbolic import base
|
29
27
|
from pyglove.core.symbolic import flags
|
30
28
|
from pyglove.core.symbolic import inferred
|
@@ -42,7 +40,7 @@ from pyglove.core.symbolic.pure_symbolic import PureSymbolic
|
|
42
40
|
from pyglove.core.views.html import tree_view # pylint: disable=unused-import
|
43
41
|
|
44
42
|
|
45
|
-
MISSING_VALUE =
|
43
|
+
MISSING_VALUE = utils.MISSING_VALUE
|
46
44
|
|
47
45
|
|
48
46
|
class ObjectMetaTest(unittest.TestCase):
|
@@ -205,7 +203,7 @@ class ObjectTest(unittest.TestCase):
|
|
205
203
|
])
|
206
204
|
class A(Object):
|
207
205
|
|
208
|
-
@
|
206
|
+
@utils.explicit_method_override
|
209
207
|
def __init__(self, x):
|
210
208
|
super().__init__(int(x))
|
211
209
|
|
@@ -214,7 +212,7 @@ class ObjectTest(unittest.TestCase):
|
|
214
212
|
|
215
213
|
class B(A):
|
216
214
|
|
217
|
-
@
|
215
|
+
@utils.explicit_method_override
|
218
216
|
def __init__(self, x): # pylint: disable=super-init-not-called
|
219
217
|
# Forgot to call super().__init__ will trigger error.
|
220
218
|
self.x = x
|
@@ -802,8 +800,8 @@ class ObjectTest(unittest.TestCase):
|
|
802
800
|
a = A(A(dict(y=A(1))))
|
803
801
|
self.assertTrue(a.sym_has('x'))
|
804
802
|
self.assertTrue(a.sym_has('x.x'))
|
805
|
-
self.assertTrue(a.sym_has(
|
806
|
-
self.assertTrue(a.sym_has(
|
803
|
+
self.assertTrue(a.sym_has(utils.KeyPath.parse('x.x.y')))
|
804
|
+
self.assertTrue(a.sym_has(utils.KeyPath.parse('x.x.y.x')))
|
807
805
|
self.assertFalse(a.sym_has('y')) # `y` is not a symbolic field.
|
808
806
|
|
809
807
|
def test_sym_get(self):
|
@@ -828,10 +826,10 @@ class ObjectTest(unittest.TestCase):
|
|
828
826
|
self.assertIs(a.sym_get('x'), a.x)
|
829
827
|
self.assertIs(a.sym_get('p'), a.sym_getattr('p'))
|
830
828
|
self.assertIs(a.sym_get('x.x'), a.x.x)
|
831
|
-
self.assertIs(a.sym_get(
|
832
|
-
self.assertIs(a.sym_get(
|
829
|
+
self.assertIs(a.sym_get(utils.KeyPath.parse('x.x.y')), a.x.x.y)
|
830
|
+
self.assertIs(a.sym_get(utils.KeyPath.parse('x.x.y.x')), a.x.x.y.x)
|
833
831
|
self.assertIs(
|
834
|
-
a.sym_get(
|
832
|
+
a.sym_get(utils.KeyPath.parse('x.x.y.p')),
|
835
833
|
a.x.x.y.sym_getattr('p'),
|
836
834
|
)
|
837
835
|
self.assertIsNone(a.sym_get('x.x.y.q', use_inferred=True))
|
@@ -1595,7 +1593,7 @@ class ObjectTest(unittest.TestCase):
|
|
1595
1593
|
self.assertEqual(a.x.x.x.sym_path, 'x.x.x')
|
1596
1594
|
self.assertEqual(a.x.x.x[0].sym_path, 'x.x.x[0]')
|
1597
1595
|
|
1598
|
-
a.sym_setpath(
|
1596
|
+
a.sym_setpath(utils.KeyPath('a'))
|
1599
1597
|
self.assertEqual(a.sym_path, 'a')
|
1600
1598
|
self.assertEqual(a.x.sym_path, 'a.x')
|
1601
1599
|
self.assertEqual(a.x.x.sym_path, 'a.x.x')
|
@@ -2075,7 +2073,7 @@ class InitSignatureTest(unittest.TestCase):
|
|
2075
2073
|
class C(B):
|
2076
2074
|
"""Custom __init__."""
|
2077
2075
|
|
2078
|
-
@
|
2076
|
+
@utils.explicit_method_override
|
2079
2077
|
def __init__(self, a, b):
|
2080
2078
|
super().__init__(b, x=a)
|
2081
2079
|
|
@@ -2450,44 +2448,51 @@ class EventsTest(unittest.TestCase):
|
|
2450
2448
|
[
|
2451
2449
|
# Set default value from outer space (parent List) for field d1.
|
2452
2450
|
{
|
2453
|
-
'd1':
|
2454
|
-
|
2455
|
-
|
2456
|
-
|
2457
|
-
|
2458
|
-
|
2459
|
-
|
2451
|
+
'd1': base.FieldUpdate(
|
2452
|
+
path=utils.KeyPath.parse('a2.b1.c1[0].d1'),
|
2453
|
+
target=sd.a2.b1.c1[0],
|
2454
|
+
field=sd.a2.b1.c1[0].value_spec.schema['d1'],
|
2455
|
+
old_value=MISSING_VALUE,
|
2456
|
+
new_value='foo',
|
2457
|
+
)
|
2460
2458
|
},
|
2461
2459
|
# Set default value from outer space (parent List) for field d2.
|
2462
2460
|
{
|
2463
|
-
'd2':
|
2464
|
-
|
2465
|
-
|
2466
|
-
|
2467
|
-
|
2468
|
-
|
2469
|
-
|
2470
|
-
}
|
2471
|
-
]
|
2461
|
+
'd2': base.FieldUpdate(
|
2462
|
+
path=utils.KeyPath.parse('a2.b1.c1[0].d2'),
|
2463
|
+
target=sd.a2.b1.c1[0],
|
2464
|
+
field=sd.a2.b1.c1[0].value_spec.schema['d2'],
|
2465
|
+
old_value=MISSING_VALUE,
|
2466
|
+
new_value=True,
|
2467
|
+
)
|
2468
|
+
},
|
2469
|
+
],
|
2470
|
+
)
|
2472
2471
|
|
2473
2472
|
# list get updated after bind with parent structures.
|
2474
|
-
self.assertEqual(
|
2475
|
-
|
2476
|
-
|
2477
|
-
|
2478
|
-
|
2479
|
-
|
2480
|
-
|
2481
|
-
|
2482
|
-
|
2483
|
-
|
2484
|
-
|
2485
|
-
|
2486
|
-
|
2487
|
-
|
2488
|
-
|
2489
|
-
|
2490
|
-
|
2473
|
+
self.assertEqual(
|
2474
|
+
list_updates,
|
2475
|
+
[
|
2476
|
+
{
|
2477
|
+
'[0].d1': base.FieldUpdate(
|
2478
|
+
path=utils.KeyPath.parse('a2.b1.c1[0].d1'),
|
2479
|
+
target=sd.a2.b1.c1[0],
|
2480
|
+
field=sd.a2.b1.c1[0].value_spec.schema['d1'],
|
2481
|
+
old_value=MISSING_VALUE,
|
2482
|
+
new_value='foo',
|
2483
|
+
)
|
2484
|
+
},
|
2485
|
+
{
|
2486
|
+
'[0].d2': base.FieldUpdate(
|
2487
|
+
path=utils.KeyPath.parse('a2.b1.c1[0].d2'),
|
2488
|
+
target=sd.a2.b1.c1[0],
|
2489
|
+
field=sd.a2.b1.c1[0].value_spec.schema['d2'],
|
2490
|
+
old_value=MISSING_VALUE,
|
2491
|
+
new_value=True,
|
2492
|
+
)
|
2493
|
+
},
|
2494
|
+
],
|
2495
|
+
)
|
2491
2496
|
|
2492
2497
|
# There are no updates in root.
|
2493
2498
|
self.assertEqual(root_updates, [])
|
@@ -2510,28 +2515,28 @@ class EventsTest(unittest.TestCase):
|
|
2510
2515
|
root_updates[0],
|
2511
2516
|
{
|
2512
2517
|
'a1': base.FieldUpdate(
|
2513
|
-
path=
|
2518
|
+
path=utils.KeyPath.parse('a1'),
|
2514
2519
|
target=sd,
|
2515
2520
|
field=sd.value_spec.schema['a1'],
|
2516
2521
|
old_value=MISSING_VALUE,
|
2517
2522
|
new_value=1,
|
2518
2523
|
),
|
2519
2524
|
'a2.b1.c1[0].d1': base.FieldUpdate(
|
2520
|
-
path=
|
2525
|
+
path=utils.KeyPath.parse('a2.b1.c1[0].d1'),
|
2521
2526
|
target=sd.a2.b1.c1[0],
|
2522
2527
|
field=sd.a2.b1.c1[0].value_spec.schema['d1'],
|
2523
2528
|
old_value='foo',
|
2524
2529
|
new_value='bar',
|
2525
2530
|
),
|
2526
2531
|
'a2.b1.c1[0].d2': base.FieldUpdate(
|
2527
|
-
path=
|
2532
|
+
path=utils.KeyPath.parse('a2.b1.c1[0].d2'),
|
2528
2533
|
target=sd.a2.b1.c1[0],
|
2529
2534
|
field=sd.a2.b1.c1[0].value_spec.schema['d2'],
|
2530
2535
|
old_value=True,
|
2531
2536
|
new_value=False,
|
2532
2537
|
),
|
2533
2538
|
'a2.b1.c1[0].d3.z': base.FieldUpdate(
|
2534
|
-
path=
|
2539
|
+
path=utils.KeyPath.parse('a2.b1.c1[0].d3.z'),
|
2535
2540
|
target=sd.a2.b1.c1[0].d3,
|
2536
2541
|
field=sd.a2.b1.c1[0].d3.__class__.__schema__['z'],
|
2537
2542
|
old_value=MISSING_VALUE,
|
@@ -2547,21 +2552,21 @@ class EventsTest(unittest.TestCase):
|
|
2547
2552
|
# Root object rebind.
|
2548
2553
|
{
|
2549
2554
|
'[0].d1': base.FieldUpdate(
|
2550
|
-
path=
|
2555
|
+
path=utils.KeyPath.parse('a2.b1.c1[0].d1'),
|
2551
2556
|
target=sd.a2.b1.c1[0],
|
2552
2557
|
field=sd.a2.b1.c1[0].value_spec.schema['d1'],
|
2553
2558
|
old_value='foo',
|
2554
2559
|
new_value='bar',
|
2555
2560
|
),
|
2556
2561
|
'[0].d2': base.FieldUpdate(
|
2557
|
-
path=
|
2562
|
+
path=utils.KeyPath.parse('a2.b1.c1[0].d2'),
|
2558
2563
|
target=sd.a2.b1.c1[0],
|
2559
2564
|
field=sd.a2.b1.c1[0].value_spec.schema['d2'],
|
2560
2565
|
old_value=True,
|
2561
2566
|
new_value=False,
|
2562
2567
|
),
|
2563
2568
|
'[0].d3.z': base.FieldUpdate(
|
2564
|
-
path=
|
2569
|
+
path=utils.KeyPath.parse('a2.b1.c1[0].d3.z'),
|
2565
2570
|
target=sd.a2.b1.c1[0].d3,
|
2566
2571
|
field=sd.a2.b1.c1[0].d3.__class__.__schema__['z'],
|
2567
2572
|
old_value=MISSING_VALUE,
|
@@ -2577,29 +2582,30 @@ class EventsTest(unittest.TestCase):
|
|
2577
2582
|
[
|
2578
2583
|
# Root object rebind.
|
2579
2584
|
{
|
2580
|
-
'd1':
|
2581
|
-
|
2582
|
-
|
2583
|
-
|
2584
|
-
|
2585
|
-
|
2586
|
-
|
2587
|
-
'd2':
|
2588
|
-
|
2589
|
-
|
2590
|
-
|
2591
|
-
|
2592
|
-
|
2593
|
-
|
2594
|
-
'd3.z':
|
2595
|
-
|
2596
|
-
|
2597
|
-
|
2598
|
-
|
2599
|
-
|
2600
|
-
|
2585
|
+
'd1': base.FieldUpdate(
|
2586
|
+
path=utils.KeyPath.parse('a2.b1.c1[0].d1'),
|
2587
|
+
target=sd.a2.b1.c1[0],
|
2588
|
+
field=sd.a2.b1.c1[0].value_spec.schema['d1'],
|
2589
|
+
old_value='foo',
|
2590
|
+
new_value='bar',
|
2591
|
+
),
|
2592
|
+
'd2': base.FieldUpdate(
|
2593
|
+
path=utils.KeyPath.parse('a2.b1.c1[0].d2'),
|
2594
|
+
target=sd.a2.b1.c1[0],
|
2595
|
+
field=sd.a2.b1.c1[0].value_spec.schema['d2'],
|
2596
|
+
old_value=True,
|
2597
|
+
new_value=False,
|
2598
|
+
),
|
2599
|
+
'd3.z': base.FieldUpdate(
|
2600
|
+
path=utils.KeyPath.parse('a2.b1.c1[0].d3.z'),
|
2601
|
+
target=sd.a2.b1.c1[0].d3,
|
2602
|
+
field=sd.a2.b1.c1[0].d3.__class__.__schema__['z'],
|
2603
|
+
old_value=MISSING_VALUE,
|
2604
|
+
new_value='foo',
|
2605
|
+
),
|
2601
2606
|
}
|
2602
|
-
]
|
2607
|
+
],
|
2608
|
+
)
|
2603
2609
|
|
2604
2610
|
def test_on_change_notification_order(self):
|
2605
2611
|
change_order = []
|
@@ -2645,7 +2651,7 @@ class EventsTest(unittest.TestCase):
|
|
2645
2651
|
y.x = A()
|
2646
2652
|
self.assertIs(x.old_parent, y)
|
2647
2653
|
self.assertIsNone(x.new_parent)
|
2648
|
-
self.assertEqual(x.sym_path,
|
2654
|
+
self.assertEqual(x.sym_path, utils.KeyPath())
|
2649
2655
|
|
2650
2656
|
def test_on_path_change(self):
|
2651
2657
|
|
@@ -2656,8 +2662,8 @@ class EventsTest(unittest.TestCase):
|
|
2656
2662
|
self.new_path = new_path
|
2657
2663
|
|
2658
2664
|
x = A()
|
2659
|
-
x.sym_setpath(
|
2660
|
-
self.assertEqual(x.old_path,
|
2665
|
+
x.sym_setpath(utils.KeyPath('a'))
|
2666
|
+
self.assertEqual(x.old_path, utils.KeyPath())
|
2661
2667
|
self.assertEqual(x.new_path, 'a')
|
2662
2668
|
|
2663
2669
|
y = Dict(x=x)
|
@@ -3059,7 +3065,7 @@ class SerializationTest(unittest.TestCase):
|
|
3059
3065
|
|
3060
3066
|
def test_serialization_with_json_convertible(self):
|
3061
3067
|
|
3062
|
-
class Y(
|
3068
|
+
class Y(utils.JSONConvertible):
|
3063
3069
|
|
3064
3070
|
TYPE_NAME = 'Y'
|
3065
3071
|
|
@@ -3079,7 +3085,7 @@ class SerializationTest(unittest.TestCase):
|
|
3079
3085
|
def from_json(cls, json_dict, *args, **kwargs):
|
3080
3086
|
return cls(json_dict.pop('value'))
|
3081
3087
|
|
3082
|
-
|
3088
|
+
utils.JSONConvertible.register(Y.TYPE_NAME, Y)
|
3083
3089
|
|
3084
3090
|
a = self._A(Y(1), y=True)
|
3085
3091
|
self.assertEqual(base.from_json_str(a.to_json_str()), a)
|
@@ -3286,9 +3292,7 @@ class FormatTest(unittest.TestCase):
|
|
3286
3292
|
|
3287
3293
|
def test_compact_python_format(self):
|
3288
3294
|
self.assertEqual(
|
3289
|
-
|
3290
|
-
self._a, compact=True, python_format=True, markdown=True
|
3291
|
-
),
|
3295
|
+
utils.format(self._a, compact=True, python_format=True, markdown=True),
|
3292
3296
|
"`A(x=[A(x=1, y=None), A(x='foo', y={'a': A(x=True, y=1.0)})], "
|
3293
3297
|
'y=MISSING_VALUE)`',
|
3294
3298
|
)
|
@@ -3333,9 +3337,12 @@ class FormatTest(unittest.TestCase):
|
|
3333
3337
|
|
3334
3338
|
def test_noncompact_python_format(self):
|
3335
3339
|
self.assertEqual(
|
3336
|
-
|
3337
|
-
self._a,
|
3338
|
-
|
3340
|
+
utils.format(
|
3341
|
+
self._a,
|
3342
|
+
compact=False,
|
3343
|
+
verbose=False,
|
3344
|
+
python_format=True,
|
3345
|
+
markdown=True,
|
3339
3346
|
),
|
3340
3347
|
inspect.cleandoc("""
|
3341
3348
|
```
|
@@ -3502,10 +3509,10 @@ class FormatTest(unittest.TestCase):
|
|
3502
3509
|
return f() if f is not None else None
|
3503
3510
|
return fn
|
3504
3511
|
|
3505
|
-
with
|
3512
|
+
with utils.str_format(custom_format=_method('_repr_xml_')):
|
3506
3513
|
self.assertEqual(str(Bar(Foo())), 'Bar(\n foo = Foo()\n)')
|
3507
3514
|
|
3508
|
-
with
|
3515
|
+
with utils.str_format(custom_format=_method('_repr_html_')):
|
3509
3516
|
self.assertIn('<html>', str(Bar(Foo())))
|
3510
3517
|
|
3511
3518
|
|
pyglove/core/symbolic/origin.py
CHANGED
@@ -16,11 +16,11 @@
|
|
16
16
|
import traceback
|
17
17
|
from typing import Any, Callable, List, Optional
|
18
18
|
|
19
|
-
from pyglove.core import
|
19
|
+
from pyglove.core import utils
|
20
20
|
from pyglove.core.symbolic import flags
|
21
21
|
|
22
22
|
|
23
|
-
class Origin(
|
23
|
+
class Origin(utils.Formattable):
|
24
24
|
"""Class that represents the origin of a symbolic value.
|
25
25
|
|
26
26
|
Origin is used for debugging the creation chain of a symbolic value, as
|
@@ -158,14 +158,12 @@ class Origin(object_utils.Formattable):
|
|
158
158
|
if isinstance(self._source, (str, type(None))):
|
159
159
|
source_str = self._source
|
160
160
|
else:
|
161
|
-
source_info =
|
161
|
+
source_info = utils.format(
|
162
162
|
self._source, compact, verbose, root_indent + 1, **kwargs
|
163
163
|
)
|
164
|
-
source_str =
|
165
|
-
f'{source_info} at 0x{id(self._source):8x}'
|
166
|
-
)
|
164
|
+
source_str = utils.RawText(f'{source_info} at 0x{id(self._source):8x}')
|
167
165
|
|
168
|
-
return
|
166
|
+
return utils.kvlist_str(
|
169
167
|
[
|
170
168
|
('tag', self._tag, None),
|
171
169
|
('source', source_str, None),
|
@@ -14,8 +14,8 @@
|
|
14
14
|
"""Interfaces for pure symbolic objects."""
|
15
15
|
|
16
16
|
from typing import Any, Callable, Optional, Tuple
|
17
|
-
from pyglove.core import object_utils
|
18
17
|
from pyglove.core import typing as pg_typing
|
18
|
+
from pyglove.core import utils
|
19
19
|
|
20
20
|
|
21
21
|
class PureSymbolic(pg_typing.CustomTyping):
|
@@ -37,11 +37,12 @@ class PureSymbolic(pg_typing.CustomTyping):
|
|
37
37
|
|
38
38
|
def custom_apply(
|
39
39
|
self,
|
40
|
-
path:
|
40
|
+
path: utils.KeyPath,
|
41
41
|
value_spec: pg_typing.ValueSpec,
|
42
42
|
allow_partial: bool,
|
43
43
|
child_transform: Optional[
|
44
|
-
Callable[[
|
44
|
+
Callable[[utils.KeyPath, pg_typing.Field, Any], Any]
|
45
|
+
] = None,
|
45
46
|
) -> Tuple[bool, Any]:
|
46
47
|
"""Custom apply on a value based on its original value spec.
|
47
48
|
|