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/base.py
CHANGED
@@ -25,8 +25,8 @@ import typing
|
|
25
25
|
from typing import Any, Callable, Dict, Iterator, List, Literal, Optional, Tuple, Type, Union
|
26
26
|
|
27
27
|
from pyglove.core import io as pg_io
|
28
|
-
from pyglove.core import object_utils
|
29
28
|
from pyglove.core import typing as pg_typing
|
29
|
+
from pyglove.core import utils
|
30
30
|
from pyglove.core.symbolic import flags
|
31
31
|
from pyglove.core.symbolic.origin import Origin
|
32
32
|
from pyglove.core.symbolic.pure_symbolic import NonDeterministic
|
@@ -38,15 +38,17 @@ class WritePermissionError(Exception):
|
|
38
38
|
"""Exception raisen when write access to object fields is not allowed."""
|
39
39
|
|
40
40
|
|
41
|
-
class FieldUpdate(
|
41
|
+
class FieldUpdate(utils.Formattable):
|
42
42
|
"""Class that describes an update to a field in an object tree."""
|
43
43
|
|
44
|
-
def __init__(
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
44
|
+
def __init__(
|
45
|
+
self,
|
46
|
+
path: utils.KeyPath,
|
47
|
+
target: 'Symbolic',
|
48
|
+
field: Optional[pg_typing.Field],
|
49
|
+
old_value: Any,
|
50
|
+
new_value: Any,
|
51
|
+
):
|
50
52
|
"""Constructor.
|
51
53
|
|
52
54
|
Args:
|
@@ -70,18 +72,18 @@ class FieldUpdate(object_utils.Formattable):
|
|
70
72
|
**kwargs,
|
71
73
|
) -> str:
|
72
74
|
"""Formats this object."""
|
73
|
-
return
|
75
|
+
return utils.kvlist_str(
|
74
76
|
[
|
75
77
|
('parent_path', self.target.sym_path, None),
|
76
78
|
('path', self.path, None),
|
77
|
-
('old_value', self.old_value,
|
78
|
-
('new_value', self.new_value,
|
79
|
+
('old_value', self.old_value, utils.MISSING_VALUE),
|
80
|
+
('new_value', self.new_value, utils.MISSING_VALUE),
|
79
81
|
],
|
80
82
|
label=self.__class__.__name__,
|
81
83
|
compact=compact,
|
82
84
|
verbose=verbose,
|
83
85
|
root_indent=root_indent,
|
84
|
-
**kwargs
|
86
|
+
**kwargs,
|
85
87
|
)
|
86
88
|
|
87
89
|
def __eq__(self, other: Any) -> bool:
|
@@ -124,11 +126,11 @@ class TopologyAware(metaclass=abc.ABCMeta):
|
|
124
126
|
|
125
127
|
@property
|
126
128
|
@abc.abstractmethod
|
127
|
-
def sym_path(self) ->
|
129
|
+
def sym_path(self) -> utils.KeyPath:
|
128
130
|
"""Returns the path of this object under its topology."""
|
129
131
|
|
130
132
|
@abc.abstractmethod
|
131
|
-
def sym_setpath(self, path:
|
133
|
+
def sym_setpath(self, path: utils.KeyPath) -> None:
|
132
134
|
"""Sets the path of this object under its topology."""
|
133
135
|
|
134
136
|
|
@@ -172,9 +174,9 @@ RAISE_IF_NOT_FOUND = (pg_typing.MISSING_VALUE,)
|
|
172
174
|
|
173
175
|
class Symbolic(
|
174
176
|
TopologyAware,
|
175
|
-
|
176
|
-
|
177
|
-
|
177
|
+
utils.Formattable,
|
178
|
+
utils.JSONConvertible,
|
179
|
+
utils.MaybePartial,
|
178
180
|
HtmlConvertible,
|
179
181
|
):
|
180
182
|
"""Base for all symbolic types.
|
@@ -203,13 +205,15 @@ class Symbolic(
|
|
203
205
|
|
204
206
|
# pylint: enable=invalid-name
|
205
207
|
|
206
|
-
def __init__(
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
208
|
+
def __init__(
|
209
|
+
self,
|
210
|
+
*,
|
211
|
+
allow_partial: bool,
|
212
|
+
accessor_writable: bool,
|
213
|
+
sealed: bool,
|
214
|
+
root_path: Optional[utils.KeyPath],
|
215
|
+
init_super: bool = True,
|
216
|
+
):
|
213
217
|
"""Constructor.
|
214
218
|
|
215
219
|
Args:
|
@@ -237,7 +241,7 @@ class Symbolic(
|
|
237
241
|
# NOTE(daiyip): parent is used for rebind call to notify their ancestors
|
238
242
|
# for updates, not for external usage.
|
239
243
|
self._set_raw_attr('_sym_parent', None)
|
240
|
-
self._set_raw_attr('_sym_path', root_path or
|
244
|
+
self._set_raw_attr('_sym_path', root_path or utils.KeyPath())
|
241
245
|
self._set_raw_attr('_sym_puresymbolic', None)
|
242
246
|
self._set_raw_attr('_sym_missing_values', None)
|
243
247
|
self._set_raw_attr('_sym_nondefault_values', None)
|
@@ -317,7 +321,7 @@ class Symbolic(
|
|
317
321
|
missing = self._sym_missing()
|
318
322
|
self._set_raw_attr('_sym_missing_values', missing)
|
319
323
|
if flatten:
|
320
|
-
missing =
|
324
|
+
missing = utils.flatten(missing)
|
321
325
|
return missing
|
322
326
|
|
323
327
|
def sym_nondefault(self, flatten: bool = True) -> Dict[Union[int, str], Any]:
|
@@ -327,7 +331,7 @@ class Symbolic(
|
|
327
331
|
nondefault = self._sym_nondefault()
|
328
332
|
self._set_raw_attr('_sym_nondefault_values', nondefault)
|
329
333
|
if flatten:
|
330
|
-
nondefault =
|
334
|
+
nondefault = utils.flatten(nondefault)
|
331
335
|
return nondefault
|
332
336
|
|
333
337
|
@property
|
@@ -405,7 +409,7 @@ class Symbolic(
|
|
405
409
|
def sym_attr_field(self, key: Union[str, int]) -> Optional[pg_typing.Field]:
|
406
410
|
"""Returns the field definition for a symbolic attribute."""
|
407
411
|
|
408
|
-
def sym_has(self, path: Union[
|
412
|
+
def sym_has(self, path: Union[utils.KeyPath, str, int]) -> bool:
|
409
413
|
"""Returns True if a path exists in the sub-tree.
|
410
414
|
|
411
415
|
Args:
|
@@ -414,13 +418,14 @@ class Symbolic(
|
|
414
418
|
Returns:
|
415
419
|
True if the path exists in current sub-tree, otherwise False.
|
416
420
|
"""
|
417
|
-
return
|
421
|
+
return utils.KeyPath.from_value(path).exists(self)
|
418
422
|
|
419
423
|
def sym_get(
|
420
424
|
self,
|
421
|
-
path: Union[
|
425
|
+
path: Union[utils.KeyPath, str, int],
|
422
426
|
default: Any = RAISE_IF_NOT_FOUND,
|
423
|
-
use_inferred: bool = False
|
427
|
+
use_inferred: bool = False,
|
428
|
+
) -> Any:
|
424
429
|
"""Returns a sub-node by path.
|
425
430
|
|
426
431
|
NOTE: there is no `sym_set`, use `sym_rebind`.
|
@@ -439,7 +444,7 @@ class Symbolic(
|
|
439
444
|
Raises:
|
440
445
|
KeyError if `path` does not exist and `default` is not specified.
|
441
446
|
"""
|
442
|
-
path =
|
447
|
+
path = utils.KeyPath.from_value(path)
|
443
448
|
if default is RAISE_IF_NOT_FOUND:
|
444
449
|
return path.query(self, use_inferred=use_inferred)
|
445
450
|
else:
|
@@ -533,12 +538,11 @@ class Symbolic(
|
|
533
538
|
return contains(self, value, type)
|
534
539
|
|
535
540
|
@property
|
536
|
-
def sym_path(self) ->
|
541
|
+
def sym_path(self) -> utils.KeyPath:
|
537
542
|
"""Returns the path of current object from the root of its symbolic tree."""
|
538
543
|
return getattr(self, '_sym_path')
|
539
544
|
|
540
|
-
def sym_setpath(
|
541
|
-
self, path: Optional[Union[str, object_utils.KeyPath]]) -> None:
|
545
|
+
def sym_setpath(self, path: Optional[Union[str, utils.KeyPath]]) -> None:
|
542
546
|
"""Sets the path of current node in its symbolic tree."""
|
543
547
|
if self.sym_path != path:
|
544
548
|
old_path = self.sym_path
|
@@ -547,11 +551,9 @@ class Symbolic(
|
|
547
551
|
|
548
552
|
def sym_rebind(
|
549
553
|
self,
|
550
|
-
path_value_pairs: Optional[
|
551
|
-
Dict[
|
552
|
-
|
553
|
-
Any],
|
554
|
-
Callable]] = None, # pylint: disable=g-bare-generic
|
554
|
+
path_value_pairs: Optional[
|
555
|
+
Union[Dict[Union[utils.KeyPath, str, int], Any], Callable[..., Any]]
|
556
|
+
] = None, # pylint: disable=g-bare-generic
|
555
557
|
*,
|
556
558
|
raise_on_no_change: bool = True,
|
557
559
|
notify_parents: bool = True,
|
@@ -575,8 +577,9 @@ class Symbolic(
|
|
575
577
|
f'Argument \'path_value_pairs\' should be a dict. '
|
576
578
|
f'Encountered {path_value_pairs}'))
|
577
579
|
path_value_pairs.update(kwargs)
|
578
|
-
path_value_pairs = {
|
579
|
-
|
580
|
+
path_value_pairs = {
|
581
|
+
utils.KeyPath.from_value(k): v for k, v in path_value_pairs.items()
|
582
|
+
}
|
580
583
|
|
581
584
|
if not path_value_pairs and raise_on_no_change:
|
582
585
|
raise ValueError(self._error_message('There are no values to rebind.'))
|
@@ -601,10 +604,9 @@ class Symbolic(
|
|
601
604
|
return new_value
|
602
605
|
|
603
606
|
@abc.abstractmethod
|
604
|
-
def sym_jsonify(
|
605
|
-
|
606
|
-
|
607
|
-
**kwargs) -> object_utils.JSONValueType:
|
607
|
+
def sym_jsonify(
|
608
|
+
self, *, hide_default_values: bool = False, **kwargs
|
609
|
+
) -> utils.JSONValueType:
|
608
610
|
"""Converts representation of current object to a plain Python object."""
|
609
611
|
|
610
612
|
def sym_ne(self, other: Any) -> bool:
|
@@ -749,16 +751,15 @@ class Symbolic(
|
|
749
751
|
|
750
752
|
def rebind(
|
751
753
|
self,
|
752
|
-
path_value_pairs: Optional[
|
753
|
-
Dict[
|
754
|
-
|
755
|
-
Any],
|
756
|
-
Callable]] = None, # pylint: disable=g-bare-generic
|
754
|
+
path_value_pairs: Optional[
|
755
|
+
Union[Dict[Union[utils.KeyPath, str, int], Any], Callable[..., Any]]
|
756
|
+
] = None, # pylint: disable=g-bare-generic
|
757
757
|
*,
|
758
758
|
raise_on_no_change: bool = True,
|
759
759
|
notify_parents: bool = True,
|
760
760
|
skip_notification: Optional[bool] = None,
|
761
|
-
**kwargs
|
761
|
+
**kwargs,
|
762
|
+
) -> 'Symbolic':
|
762
763
|
"""Alias for `sym_rebind`.
|
763
764
|
|
764
765
|
Alias for `sym_rebind`. `rebind` is the recommended way for mutating
|
@@ -941,7 +942,7 @@ class Symbolic(
|
|
941
942
|
"""
|
942
943
|
return self.sym_clone(deep, memo, override)
|
943
944
|
|
944
|
-
def to_json(self, **kwargs) ->
|
945
|
+
def to_json(self, **kwargs) -> utils.JSONValueType:
|
945
946
|
"""Alias for `sym_jsonify`."""
|
946
947
|
return to_json(self, **kwargs)
|
947
948
|
|
@@ -964,13 +965,18 @@ class Symbolic(
|
|
964
965
|
def inspect(
|
965
966
|
self,
|
966
967
|
path_regex: Optional[str] = None,
|
967
|
-
where: Optional[
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
968
|
+
where: Optional[
|
969
|
+
Union[Callable[[Any], bool], Callable[[Any, Any], bool]]
|
970
|
+
] = None,
|
971
|
+
custom_selector: Optional[
|
972
|
+
Union[
|
973
|
+
Callable[[utils.KeyPath, Any], bool],
|
974
|
+
Callable[[utils.KeyPath, Any, Any], bool],
|
975
|
+
]
|
976
|
+
] = None,
|
972
977
|
file=sys.stdout, # pylint: disable=redefined-builtin
|
973
|
-
**kwargs
|
978
|
+
**kwargs,
|
979
|
+
) -> None:
|
974
980
|
"""Inspects current object by printing out selected values.
|
975
981
|
|
976
982
|
Example::
|
@@ -1058,7 +1064,7 @@ class Symbolic(
|
|
1058
1064
|
v = self
|
1059
1065
|
else:
|
1060
1066
|
v = query(self, path_regex, where, False, custom_selector)
|
1061
|
-
|
1067
|
+
utils.print(v, file=file, **kwargs)
|
1062
1068
|
|
1063
1069
|
def __copy__(self) -> 'Symbolic':
|
1064
1070
|
"""Overridden shallow copy."""
|
@@ -1074,8 +1080,8 @@ class Symbolic(
|
|
1074
1080
|
|
1075
1081
|
@abc.abstractmethod
|
1076
1082
|
def _sym_rebind(
|
1077
|
-
self, path_value_pairs: Dict[
|
1078
|
-
|
1083
|
+
self, path_value_pairs: Dict[utils.KeyPath, Any]
|
1084
|
+
) -> List[FieldUpdate]:
|
1079
1085
|
"""Subclass specific rebind implementation.
|
1080
1086
|
|
1081
1087
|
Args:
|
@@ -1111,9 +1117,8 @@ class Symbolic(
|
|
1111
1117
|
|
1112
1118
|
@abc.abstractmethod
|
1113
1119
|
def _update_children_paths(
|
1114
|
-
self,
|
1115
|
-
|
1116
|
-
new_path: object_utils.KeyPath) -> None:
|
1120
|
+
self, old_path: utils.KeyPath, new_path: utils.KeyPath
|
1121
|
+
) -> None:
|
1117
1122
|
"""Update children paths according to root_path of current node."""
|
1118
1123
|
|
1119
1124
|
@abc.abstractmethod
|
@@ -1122,7 +1127,7 @@ class Symbolic(
|
|
1122
1127
|
"""Child should implement: set an item without permission check."""
|
1123
1128
|
|
1124
1129
|
@abc.abstractmethod
|
1125
|
-
def _on_change(self, field_updates: Dict[
|
1130
|
+
def _on_change(self, field_updates: Dict[utils.KeyPath, FieldUpdate]):
|
1126
1131
|
"""Event that is triggered when field values in the subtree are updated.
|
1127
1132
|
|
1128
1133
|
This event will be called
|
@@ -1175,14 +1180,14 @@ class Symbolic(
|
|
1175
1180
|
# NOTE(daiyip): make a copy of symbolic object if it belongs to another
|
1176
1181
|
# object tree, this prevents it from having multiple parents. See
|
1177
1182
|
# List._formalized_value for similar logic.
|
1178
|
-
root_path =
|
1183
|
+
root_path = utils.KeyPath(key, self.sym_path)
|
1179
1184
|
if (value.sym_parent is not None and
|
1180
1185
|
(value.sym_parent is not self
|
1181
1186
|
or root_path != value.sym_path)):
|
1182
1187
|
value = value.clone()
|
1183
1188
|
|
1184
1189
|
if isinstance(value, TopologyAware):
|
1185
|
-
value.sym_setpath(
|
1190
|
+
value.sym_setpath(utils.KeyPath(key, self.sym_path))
|
1186
1191
|
value.sym_setparent(self._sym_parent_for_children())
|
1187
1192
|
return value
|
1188
1193
|
|
@@ -1191,9 +1196,10 @@ class Symbolic(
|
|
1191
1196
|
return self
|
1192
1197
|
|
1193
1198
|
def _set_item_of_current_tree(
|
1194
|
-
self, path:
|
1199
|
+
self, path: utils.KeyPath, value: Any
|
1200
|
+
) -> Optional[FieldUpdate]:
|
1195
1201
|
"""Set a field of current tree by key path and return its parent."""
|
1196
|
-
assert isinstance(path,
|
1202
|
+
assert isinstance(path, utils.KeyPath), path
|
1197
1203
|
if not path:
|
1198
1204
|
raise KeyError(
|
1199
1205
|
self._error_message(
|
@@ -1222,8 +1228,8 @@ class Symbolic(
|
|
1222
1228
|
per_target_updates = dict()
|
1223
1229
|
|
1224
1230
|
def _get_target_updates(
|
1225
|
-
target: 'Symbolic'
|
1226
|
-
) -> Dict[
|
1231
|
+
target: 'Symbolic',
|
1232
|
+
) -> Dict[utils.KeyPath, FieldUpdate]:
|
1227
1233
|
target_id = id(target)
|
1228
1234
|
if target_id not in per_target_updates:
|
1229
1235
|
per_target_updates[target_id] = (target, dict())
|
@@ -1256,7 +1262,7 @@ class Symbolic(
|
|
1256
1262
|
|
1257
1263
|
def _error_message(self, message: str) -> str:
|
1258
1264
|
"""Create error message to include path information."""
|
1259
|
-
return
|
1265
|
+
return utils.message_on_path(message, self.sym_path)
|
1260
1266
|
|
1261
1267
|
|
1262
1268
|
#
|
@@ -1271,12 +1277,11 @@ def get_rebind_dict(
|
|
1271
1277
|
"""Generate rebind dict using rebinder on target value.
|
1272
1278
|
|
1273
1279
|
Args:
|
1274
|
-
rebinder: A callable object with signature:
|
1275
|
-
(key_path:
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
`Symbolic.rebind` for more details.
|
1280
|
+
rebinder: A callable object with signature: (key_path: utils.KeyPath, value:
|
1281
|
+
Any) -> Any or (key_path: utils.KeyPath, value: Any, parent: Any) -> Any.
|
1282
|
+
If rebinder returns the same value from input, the value is considered
|
1283
|
+
unchanged. Otherwise it will be put into the returning rebind dict. See
|
1284
|
+
`Symbolic.rebind` for more details.
|
1280
1285
|
target: Upon which value the rebind dict is computed.
|
1281
1286
|
|
1282
1287
|
Returns:
|
@@ -1329,15 +1334,17 @@ class TraverseAction(enum.Enum):
|
|
1329
1334
|
CONTINUE = 2
|
1330
1335
|
|
1331
1336
|
|
1332
|
-
def traverse(
|
1333
|
-
|
1334
|
-
|
1335
|
-
|
1336
|
-
|
1337
|
-
|
1338
|
-
|
1339
|
-
|
1340
|
-
|
1337
|
+
def traverse(
|
1338
|
+
x: Any,
|
1339
|
+
preorder_visitor_fn: Optional[
|
1340
|
+
Callable[[utils.KeyPath, Any, Any], Optional[TraverseAction]]
|
1341
|
+
] = None,
|
1342
|
+
postorder_visitor_fn: Optional[
|
1343
|
+
Callable[[utils.KeyPath, Any, Any], Optional[TraverseAction]]
|
1344
|
+
] = None,
|
1345
|
+
root_path: Optional[utils.KeyPath] = None,
|
1346
|
+
parent: Optional[Any] = None,
|
1347
|
+
) -> bool:
|
1341
1348
|
"""Traverse a (maybe) symbolic value using visitor functions.
|
1342
1349
|
|
1343
1350
|
Example::
|
@@ -1372,7 +1379,7 @@ def traverse(x: Any,
|
|
1372
1379
|
either `TraverseAction.ENTER` or `TraverseAction.CONTINUE` for all nodes.
|
1373
1380
|
Otherwise False.
|
1374
1381
|
"""
|
1375
|
-
root_path = root_path or
|
1382
|
+
root_path = root_path or utils.KeyPath()
|
1376
1383
|
|
1377
1384
|
def no_op_visitor(path, value, parent):
|
1378
1385
|
del path, value, parent
|
@@ -1387,20 +1394,35 @@ def traverse(x: Any,
|
|
1387
1394
|
if preorder_action is None or preorder_action == TraverseAction.ENTER:
|
1388
1395
|
if isinstance(x, dict):
|
1389
1396
|
for k, v in x.items():
|
1390
|
-
if not traverse(
|
1391
|
-
|
1397
|
+
if not traverse(
|
1398
|
+
v,
|
1399
|
+
preorder_visitor_fn,
|
1400
|
+
postorder_visitor_fn,
|
1401
|
+
utils.KeyPath(k, root_path),
|
1402
|
+
x,
|
1403
|
+
):
|
1392
1404
|
preorder_action = TraverseAction.STOP
|
1393
1405
|
break
|
1394
1406
|
elif isinstance(x, list):
|
1395
1407
|
for i, v in enumerate(x):
|
1396
|
-
if not traverse(
|
1397
|
-
|
1408
|
+
if not traverse(
|
1409
|
+
v,
|
1410
|
+
preorder_visitor_fn,
|
1411
|
+
postorder_visitor_fn,
|
1412
|
+
utils.KeyPath(i, root_path),
|
1413
|
+
x,
|
1414
|
+
):
|
1398
1415
|
preorder_action = TraverseAction.STOP
|
1399
1416
|
break
|
1400
1417
|
elif isinstance(x, Symbolic.ObjectType): # pytype: disable=wrong-arg-types
|
1401
1418
|
for k, v in x.sym_items():
|
1402
|
-
if not traverse(
|
1403
|
-
|
1419
|
+
if not traverse(
|
1420
|
+
v,
|
1421
|
+
preorder_visitor_fn,
|
1422
|
+
postorder_visitor_fn,
|
1423
|
+
utils.KeyPath(k, root_path),
|
1424
|
+
x,
|
1425
|
+
):
|
1404
1426
|
preorder_action = TraverseAction.STOP
|
1405
1427
|
break
|
1406
1428
|
postorder_action = postorder_visitor_fn(root_path, x, parent)
|
@@ -1413,12 +1435,16 @@ def traverse(x: Any,
|
|
1413
1435
|
def query(
|
1414
1436
|
x: Any,
|
1415
1437
|
path_regex: Optional[str] = None,
|
1416
|
-
where: Optional[
|
1417
|
-
|
1438
|
+
where: Optional[
|
1439
|
+
Union[Callable[[Any], bool], Callable[[Any, Any], bool]]
|
1440
|
+
] = None,
|
1418
1441
|
enter_selected: bool = False,
|
1419
|
-
custom_selector: Optional[
|
1420
|
-
|
1421
|
-
|
1442
|
+
custom_selector: Optional[
|
1443
|
+
Union[
|
1444
|
+
Callable[[utils.KeyPath, Any], bool],
|
1445
|
+
Callable[[utils.KeyPath, Any, Any], bool],
|
1446
|
+
]
|
1447
|
+
] = None,
|
1422
1448
|
) -> Dict[str, Any]:
|
1423
1449
|
"""Queries a (maybe) symbolic value.
|
1424
1450
|
|
@@ -1521,8 +1547,9 @@ def query(
|
|
1521
1547
|
|
1522
1548
|
results = {}
|
1523
1549
|
|
1524
|
-
def _preorder_visitor(
|
1525
|
-
|
1550
|
+
def _preorder_visitor(
|
1551
|
+
path: utils.KeyPath, v: Any, parent: Any
|
1552
|
+
) -> TraverseAction:
|
1526
1553
|
if select_fn(path, v, parent): # pytype: disable=wrong-arg-count
|
1527
1554
|
results[str(path)] = v
|
1528
1555
|
return TraverseAction.ENTER if enter_selected else TraverseAction.CONTINUE
|
@@ -1752,7 +1779,7 @@ def gt(left: Any, right: Any) -> bool:
|
|
1752
1779
|
|
1753
1780
|
def _type_order(value: Any) -> str:
|
1754
1781
|
"""Returns the ordering string of value's type."""
|
1755
|
-
if isinstance(value,
|
1782
|
+
if isinstance(value, utils.MissingValue):
|
1756
1783
|
type_order = 0
|
1757
1784
|
elif value is None:
|
1758
1785
|
type_order = 1
|
@@ -1950,7 +1977,7 @@ def is_abstract(x: Any) -> bool:
|
|
1950
1977
|
True if value itself is partial/PureSymbolic or its child and nested
|
1951
1978
|
child fields contain partial/PureSymbolic values.
|
1952
1979
|
"""
|
1953
|
-
return
|
1980
|
+
return utils.is_partial(x) or is_pure_symbolic(x)
|
1954
1981
|
|
1955
1982
|
|
1956
1983
|
def contains(
|
@@ -2009,11 +2036,11 @@ def from_json(
|
|
2009
2036
|
json_value: Any,
|
2010
2037
|
*,
|
2011
2038
|
allow_partial: bool = False,
|
2012
|
-
root_path: Optional[
|
2039
|
+
root_path: Optional[utils.KeyPath] = None,
|
2013
2040
|
auto_import: bool = True,
|
2014
2041
|
auto_dict: bool = False,
|
2015
2042
|
value_spec: Optional[pg_typing.ValueSpec] = None,
|
2016
|
-
**kwargs
|
2043
|
+
**kwargs,
|
2017
2044
|
) -> Any:
|
2018
2045
|
"""Deserializes a (maybe) symbolic value from JSON value.
|
2019
2046
|
|
@@ -2057,28 +2084,30 @@ def from_json(
|
|
2057
2084
|
|
2058
2085
|
typename_resolved = kwargs.pop('_typename_resolved', False)
|
2059
2086
|
if not typename_resolved:
|
2060
|
-
json_value =
|
2087
|
+
json_value = utils.json_conversion.resolve_typenames(
|
2061
2088
|
json_value, auto_import=auto_import, auto_dict=auto_dict
|
2062
2089
|
)
|
2063
2090
|
|
2064
2091
|
def _load_child(k, v):
|
2065
2092
|
return from_json(
|
2066
2093
|
v,
|
2067
|
-
root_path=
|
2094
|
+
root_path=utils.KeyPath(k, root_path),
|
2068
2095
|
_typename_resolved=True,
|
2069
2096
|
allow_partial=allow_partial,
|
2070
|
-
**kwargs
|
2097
|
+
**kwargs,
|
2071
2098
|
)
|
2072
2099
|
|
2073
2100
|
if isinstance(json_value, list):
|
2074
|
-
if
|
2075
|
-
and json_value[0] == object_utils.JSONConvertible.TUPLE_MARKER):
|
2101
|
+
if json_value and json_value[0] == utils.JSONConvertible.TUPLE_MARKER:
|
2076
2102
|
if len(json_value) < 2:
|
2077
2103
|
raise ValueError(
|
2078
|
-
|
2079
|
-
|
2080
|
-
f
|
2081
|
-
f'Encountered: {json_value}',
|
2104
|
+
utils.message_on_path(
|
2105
|
+
'Tuple should have at least one element '
|
2106
|
+
f"besides '{utils.JSONConvertible.TUPLE_MARKER}'. "
|
2107
|
+
f'Encountered: {json_value}',
|
2108
|
+
root_path,
|
2109
|
+
)
|
2110
|
+
)
|
2082
2111
|
return tuple(_load_child(i, v) for i, v in enumerate(json_value[1:]))
|
2083
2112
|
return Symbolic.ListType.from_json( # pytype: disable=attribute-error
|
2084
2113
|
json_value,
|
@@ -2088,7 +2117,7 @@ def from_json(
|
|
2088
2117
|
**kwargs,
|
2089
2118
|
)
|
2090
2119
|
elif isinstance(json_value, dict):
|
2091
|
-
if
|
2120
|
+
if utils.JSONConvertible.TYPE_NAME_KEY not in json_value:
|
2092
2121
|
return Symbolic.DictType.from_json( # pytype: disable=attribute-error
|
2093
2122
|
json_value,
|
2094
2123
|
value_spec=value_spec,
|
@@ -2096,20 +2125,25 @@ def from_json(
|
|
2096
2125
|
allow_partial=allow_partial,
|
2097
2126
|
**kwargs,
|
2098
2127
|
)
|
2099
|
-
return
|
2100
|
-
json_value,
|
2101
|
-
|
2128
|
+
return utils.from_json(
|
2129
|
+
json_value,
|
2130
|
+
_typename_resolved=True,
|
2131
|
+
root_path=root_path,
|
2132
|
+
allow_partial=allow_partial,
|
2133
|
+
**kwargs,
|
2102
2134
|
)
|
2103
2135
|
return json_value
|
2104
2136
|
|
2105
2137
|
|
2106
|
-
def from_json_str(
|
2107
|
-
|
2108
|
-
|
2109
|
-
|
2110
|
-
|
2111
|
-
|
2112
|
-
|
2138
|
+
def from_json_str(
|
2139
|
+
json_str: str,
|
2140
|
+
*,
|
2141
|
+
allow_partial: bool = False,
|
2142
|
+
root_path: Optional[utils.KeyPath] = None,
|
2143
|
+
auto_import: bool = True,
|
2144
|
+
auto_dict: bool = False,
|
2145
|
+
**kwargs,
|
2146
|
+
) -> Any:
|
2113
2147
|
"""Deserialize (maybe) symbolic object from JSON string.
|
2114
2148
|
|
2115
2149
|
Example::
|
@@ -2202,7 +2236,7 @@ def to_json(value: Any, **kwargs) -> Any:
|
|
2202
2236
|
# classes may have conflicting `to_json` method in their existing classes.
|
2203
2237
|
if isinstance(value, Symbolic):
|
2204
2238
|
return value.sym_jsonify(**kwargs)
|
2205
|
-
return
|
2239
|
+
return utils.to_json(value, **kwargs)
|
2206
2240
|
|
2207
2241
|
|
2208
2242
|
def to_json_str(value: Any,
|
@@ -2378,8 +2412,11 @@ def default_save_handler(
|
|
2378
2412
|
if file_format == 'json':
|
2379
2413
|
content = to_json_str(value, json_indent=indent, **kwargs)
|
2380
2414
|
elif file_format == 'txt':
|
2381
|
-
content =
|
2382
|
-
value
|
2415
|
+
content = (
|
2416
|
+
value
|
2417
|
+
if isinstance(value, str)
|
2418
|
+
else utils.format(value, compact=False, verbose=True)
|
2419
|
+
)
|
2383
2420
|
else:
|
2384
2421
|
raise ValueError(f'Unsupported `file_format`: {file_format!r}.')
|
2385
2422
|
|
@@ -2415,8 +2452,7 @@ def treats_as_sealed(value: Symbolic) -> bool:
|
|
2415
2452
|
def symbolic_transform_fn(allow_partial: bool):
|
2416
2453
|
"""Symbolic object transform function builder."""
|
2417
2454
|
|
2418
|
-
def _fn(
|
2419
|
-
path: object_utils.KeyPath, field: pg_typing.Field, value: Any) -> Any:
|
2455
|
+
def _fn(path: utils.KeyPath, field: pg_typing.Field, value: Any) -> Any:
|
2420
2456
|
"""Transform schema-less List and Dict to symbolic."""
|
2421
2457
|
if isinstance(value, Symbolic):
|
2422
2458
|
return value
|