pyglove 0.4.5.dev202412100720__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 +14 -15
- pyglove/core/views/html/controls/base.py +5 -5
- pyglove/core/views/html/controls/label.py +1 -1
- pyglove/core/views/html/controls/label_test.py +6 -6
- 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 +80 -2
- pyglove/core/views/html/controls/tab_test.py +34 -1
- pyglove/core/views/html/tree_view.py +39 -37
- {pyglove-0.4.5.dev202412100720.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.dev202412100720.dist-info → pyglove-0.4.5.dev202501250807.dist-info}/WHEEL +1 -1
- pyglove/core/object_utils/__init__.py +0 -164
- pyglove-0.4.5.dev202412100720.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.dev202412100720.dist-info → pyglove-0.4.5.dev202501250807.dist-info}/LICENSE +0 -0
- {pyglove-0.4.5.dev202412100720.dist-info → pyglove-0.4.5.dev202501250807.dist-info}/top_level.txt +0 -0
pyglove/core/symbolic/functor.py
CHANGED
@@ -22,14 +22,14 @@ import types
|
|
22
22
|
import typing
|
23
23
|
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type, Union
|
24
24
|
|
25
|
-
from pyglove.core import object_utils
|
26
25
|
from pyglove.core import typing as pg_typing
|
26
|
+
from pyglove.core import utils
|
27
27
|
from pyglove.core.symbolic import base
|
28
28
|
from pyglove.core.symbolic import flags
|
29
29
|
from pyglove.core.symbolic import object as pg_object
|
30
30
|
|
31
31
|
|
32
|
-
class Functor(pg_object.Object,
|
32
|
+
class Functor(pg_object.Object, utils.Functor):
|
33
33
|
"""Symbolic functions (Functors).
|
34
34
|
|
35
35
|
A symbolic function is a symbolic class with a ``__call__`` method, whose
|
@@ -124,7 +124,7 @@ class Functor(pg_object.Object, object_utils.Functor):
|
|
124
124
|
if not hasattr(cls, '__orig_init__'):
|
125
125
|
setattr(cls, '__orig_init__', cls.__init__)
|
126
126
|
|
127
|
-
@
|
127
|
+
@utils.explicit_method_override
|
128
128
|
@functools.wraps(pseudo_init)
|
129
129
|
def _init(self, *args, **kwargs):
|
130
130
|
self.__class__.__orig_init__(self, *args, **kwargs)
|
@@ -148,14 +148,15 @@ class Functor(pg_object.Object, object_utils.Functor):
|
|
148
148
|
return instance()
|
149
149
|
return instance
|
150
150
|
|
151
|
-
@
|
151
|
+
@utils.explicit_method_override
|
152
152
|
def __init__(
|
153
153
|
self,
|
154
154
|
*args,
|
155
|
-
root_path: Optional[
|
155
|
+
root_path: Optional[utils.KeyPath] = None,
|
156
156
|
override_args: bool = False,
|
157
157
|
ignore_extra_args: bool = False,
|
158
|
-
**kwargs
|
158
|
+
**kwargs,
|
159
|
+
):
|
159
160
|
"""Constructor.
|
160
161
|
|
161
162
|
Args:
|
@@ -182,8 +183,8 @@ class Functor(pg_object.Object, object_utils.Functor):
|
|
182
183
|
varargs = list(args[len(signature.args) :])
|
183
184
|
args = args[: len(signature.args)]
|
184
185
|
else:
|
185
|
-
arg_phrase =
|
186
|
-
was_phrase =
|
186
|
+
arg_phrase = utils.auto_plural(len(signature.args), 'argument')
|
187
|
+
was_phrase = utils.auto_plural(len(args), 'was', 'were')
|
187
188
|
raise TypeError(
|
188
189
|
f'{signature.id}() takes {len(signature.args)} '
|
189
190
|
f'positional {arg_phrase} but {len(args)} {was_phrase} given.'
|
@@ -257,8 +258,7 @@ class Functor(pg_object.Object, object_utils.Functor):
|
|
257
258
|
# pylint: enable=protected-access
|
258
259
|
return typing.cast(Functor, other)
|
259
260
|
|
260
|
-
def _on_change(
|
261
|
-
self, field_updates: Dict[object_utils.KeyPath, base.FieldUpdate]):
|
261
|
+
def _on_change(self, field_updates: Dict[utils.KeyPath, base.FieldUpdate]):
|
262
262
|
"""Custom handling field change to update bound args."""
|
263
263
|
for relative_path, update in field_updates.items():
|
264
264
|
assert relative_path
|
@@ -406,8 +406,8 @@ class Functor(pg_object.Object, object_utils.Functor):
|
|
406
406
|
if ignore_extra_args:
|
407
407
|
args = args[: len(signature.args)]
|
408
408
|
else:
|
409
|
-
arg_phrase =
|
410
|
-
was_phrase =
|
409
|
+
arg_phrase = utils.auto_plural(len(signature.args), 'argument')
|
410
|
+
was_phrase = utils.auto_plural(len(args), 'was', 'were')
|
411
411
|
raise TypeError(
|
412
412
|
f'{signature.id}() takes {len(signature.args)} '
|
413
413
|
f'positional {arg_phrase} but {len(args)} {was_phrase} given.'
|
@@ -483,9 +483,10 @@ class Functor(pg_object.Object, object_utils.Functor):
|
|
483
483
|
missing_required_arg_names.append(arg.name)
|
484
484
|
|
485
485
|
if missing_required_arg_names:
|
486
|
-
arg_phrase =
|
487
|
-
len(missing_required_arg_names), 'argument'
|
488
|
-
|
486
|
+
arg_phrase = utils.auto_plural(
|
487
|
+
len(missing_required_arg_names), 'argument'
|
488
|
+
)
|
489
|
+
args_str = utils.comma_delimited_str(missing_required_arg_names)
|
489
490
|
raise TypeError(
|
490
491
|
f'{signature.id}() missing {len(missing_required_arg_names)} '
|
491
492
|
f'required positional {arg_phrase}: {args_str}.'
|
@@ -11,15 +11,13 @@
|
|
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.Functor."""
|
15
|
-
|
16
14
|
import inspect
|
17
15
|
import io
|
18
16
|
import typing
|
19
17
|
import unittest
|
20
18
|
|
21
|
-
from pyglove.core import object_utils
|
22
19
|
from pyglove.core import typing as pg_typing
|
20
|
+
from pyglove.core import utils
|
23
21
|
from pyglove.core.symbolic import flags
|
24
22
|
from pyglove.core.symbolic.base import from_json_str as pg_from_json_str
|
25
23
|
from pyglove.core.symbolic.dict import Dict
|
@@ -31,7 +29,7 @@ from pyglove.core.symbolic.object import members as pg_members
|
|
31
29
|
from pyglove.core.symbolic.object import Object
|
32
30
|
|
33
31
|
|
34
|
-
MISSING_VALUE =
|
32
|
+
MISSING_VALUE = utils.MISSING_VALUE
|
35
33
|
|
36
34
|
|
37
35
|
class FunctorTest(unittest.TestCase):
|
@@ -14,8 +14,8 @@
|
|
14
14
|
"""Common inferential values."""
|
15
15
|
|
16
16
|
from typing import Any, 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
|
from pyglove.core.symbolic import base
|
20
20
|
from pyglove.core.symbolic.object import Object
|
21
21
|
|
@@ -65,7 +65,7 @@ class ValueFromParentChain(InferredValue):
|
|
65
65
|
if v == pg_typing.MISSING_VALUE:
|
66
66
|
if parent is None:
|
67
67
|
raise AttributeError(
|
68
|
-
|
68
|
+
utils.message_on_path(
|
69
69
|
(
|
70
70
|
f'`{self.inference_key}` is not found under its context '
|
71
71
|
'(along its symbolic parent chain).'
|
pyglove/core/symbolic/list.py
CHANGED
@@ -18,8 +18,8 @@ import math
|
|
18
18
|
import numbers
|
19
19
|
import typing
|
20
20
|
from typing import Any, Callable, Dict, Iterable, Iterator, Optional, Tuple, Union
|
21
|
-
from pyglove.core import object_utils
|
22
21
|
from pyglove.core import typing as pg_typing
|
22
|
+
from pyglove.core import utils
|
23
23
|
from pyglove.core.symbolic import base
|
24
24
|
from pyglove.core.symbolic import flags
|
25
25
|
|
@@ -74,13 +74,16 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
74
74
|
"""
|
75
75
|
|
76
76
|
@classmethod
|
77
|
-
def partial(
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
77
|
+
def partial(
|
78
|
+
cls,
|
79
|
+
items: Optional[Iterable[Any]] = None,
|
80
|
+
*,
|
81
|
+
value_spec: Optional[pg_typing.List] = None,
|
82
|
+
onchange_callback: Optional[
|
83
|
+
Callable[[Dict[utils.KeyPath, base.FieldUpdate]], None]
|
84
|
+
] = None,
|
85
|
+
**kwargs,
|
86
|
+
) -> 'List':
|
84
87
|
"""Class method that creates a partial List object."""
|
85
88
|
return cls(items,
|
86
89
|
value_spec=value_spec,
|
@@ -89,13 +92,15 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
89
92
|
**kwargs)
|
90
93
|
|
91
94
|
@classmethod
|
92
|
-
def from_json(
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
95
|
+
def from_json(
|
96
|
+
cls,
|
97
|
+
json_value: Any,
|
98
|
+
*,
|
99
|
+
value_spec: Optional[pg_typing.List] = None,
|
100
|
+
allow_partial: bool = False,
|
101
|
+
root_path: Optional[utils.KeyPath] = None,
|
102
|
+
**kwargs,
|
103
|
+
) -> 'List':
|
99
104
|
"""Class method that load an symbolic List from a JSON value.
|
100
105
|
|
101
106
|
Example::
|
@@ -136,10 +141,11 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
136
141
|
[
|
137
142
|
base.from_json(
|
138
143
|
v,
|
139
|
-
root_path=
|
144
|
+
root_path=utils.KeyPath(i, root_path),
|
140
145
|
allow_partial=allow_partial,
|
141
|
-
**kwargs
|
142
|
-
)
|
146
|
+
**kwargs,
|
147
|
+
)
|
148
|
+
for i, v in enumerate(json_value)
|
143
149
|
],
|
144
150
|
value_spec=value_spec,
|
145
151
|
root_path=root_path,
|
@@ -151,12 +157,14 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
151
157
|
items: Optional[Iterable[Any]] = None,
|
152
158
|
*,
|
153
159
|
value_spec: Optional[pg_typing.List] = None,
|
154
|
-
onchange_callback: Optional[
|
155
|
-
[Dict[
|
160
|
+
onchange_callback: Optional[
|
161
|
+
Callable[[Dict[utils.KeyPath, base.FieldUpdate]], None]
|
162
|
+
] = None,
|
156
163
|
allow_partial: bool = False,
|
157
164
|
accessor_writable: bool = True,
|
158
165
|
sealed: bool = False,
|
159
|
-
root_path: Optional[
|
166
|
+
root_path: Optional[utils.KeyPath] = None,
|
167
|
+
):
|
160
168
|
"""Constructor.
|
161
169
|
|
162
170
|
Args:
|
@@ -337,8 +345,8 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
337
345
|
return missing
|
338
346
|
|
339
347
|
def _sym_rebind(
|
340
|
-
self, path_value_pairs: typing.Dict[
|
341
|
-
|
348
|
+
self, path_value_pairs: typing.Dict[utils.KeyPath, Any]
|
349
|
+
) -> typing.List[base.FieldUpdate]:
|
342
350
|
"""Subclass specific rebind implementation."""
|
343
351
|
updates = []
|
344
352
|
|
@@ -378,14 +386,13 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
378
386
|
return self
|
379
387
|
|
380
388
|
def _update_children_paths(
|
381
|
-
self,
|
382
|
-
|
383
|
-
new_path: object_utils.KeyPath) -> None:
|
389
|
+
self, old_path: utils.KeyPath, new_path: utils.KeyPath
|
390
|
+
) -> None:
|
384
391
|
"""Update children paths according to root_path of current node."""
|
385
392
|
del old_path
|
386
393
|
for idx, item in self.sym_items():
|
387
394
|
if isinstance(item, base.TopologyAware):
|
388
|
-
item.sym_setpath(
|
395
|
+
item.sym_setpath(utils.KeyPath(idx, new_path))
|
389
396
|
|
390
397
|
def _set_item_without_permission_check( # pytype: disable=signature-mismatch # overriding-parameter-type-checks
|
391
398
|
self, key: int, value: Any) -> Optional[base.FieldUpdate]:
|
@@ -432,13 +439,15 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
432
439
|
value = base.from_json(
|
433
440
|
value,
|
434
441
|
allow_partial=allow_partial,
|
435
|
-
root_path=
|
442
|
+
root_path=utils.KeyPath(idx, self.sym_path),
|
443
|
+
)
|
436
444
|
if self._value_spec and flags.is_type_check_enabled():
|
437
445
|
value = self._value_spec.element.apply(
|
438
446
|
value,
|
439
447
|
allow_partial=allow_partial,
|
440
448
|
transform_fn=base.symbolic_transform_fn(self._allow_partial),
|
441
|
-
root_path=
|
449
|
+
root_path=utils.KeyPath(idx, self.sym_path),
|
450
|
+
)
|
442
451
|
return self._relocate_if_symbolic(idx, value)
|
443
452
|
|
444
453
|
@property
|
@@ -446,8 +455,7 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
446
455
|
"""Returns True if current list subscribes field updates."""
|
447
456
|
return self._onchange_callback is not None
|
448
457
|
|
449
|
-
def _on_change(self,
|
450
|
-
field_updates: Dict[object_utils.KeyPath, base.FieldUpdate]):
|
458
|
+
def _on_change(self, field_updates: Dict[utils.KeyPath, base.FieldUpdate]):
|
451
459
|
"""On change event of List."""
|
452
460
|
# Do nothing for now to handle changes of List.
|
453
461
|
|
@@ -463,7 +471,7 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
463
471
|
# Update paths for children.
|
464
472
|
for idx, item in self.sym_items():
|
465
473
|
if isinstance(item, base.TopologyAware) and item.sym_path.key != idx:
|
466
|
-
item.sym_setpath(
|
474
|
+
item.sym_setpath(utils.KeyPath(idx, self.sym_path))
|
467
475
|
|
468
476
|
if self._onchange_callback is not None:
|
469
477
|
self._onchange_callback(field_updates)
|
@@ -723,11 +731,12 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
723
731
|
|
724
732
|
def custom_apply(
|
725
733
|
self,
|
726
|
-
path:
|
734
|
+
path: utils.KeyPath,
|
727
735
|
value_spec: pg_typing.ValueSpec,
|
728
736
|
allow_partial: bool,
|
729
737
|
child_transform: Optional[
|
730
|
-
Callable[[
|
738
|
+
Callable[[utils.KeyPath, pg_typing.Field, Any], Any]
|
739
|
+
] = None,
|
731
740
|
) -> Tuple[bool, 'List']:
|
732
741
|
"""Implement pg.typing.CustomTyping interface.
|
733
742
|
|
@@ -746,9 +755,12 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
746
755
|
if self._value_spec:
|
747
756
|
if value_spec and not value_spec.is_compatible(self._value_spec):
|
748
757
|
raise ValueError(
|
749
|
-
|
758
|
+
utils.message_on_path(
|
750
759
|
f'List (spec={self._value_spec!r}) cannot be assigned to an '
|
751
|
-
f'incompatible field (spec={value_spec!r}).',
|
760
|
+
f'incompatible field (spec={value_spec!r}).',
|
761
|
+
path,
|
762
|
+
)
|
763
|
+
)
|
752
764
|
if self._allow_partial == allow_partial:
|
753
765
|
proceed_with_standard_apply = False
|
754
766
|
else:
|
@@ -758,9 +770,8 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
758
770
|
return (proceed_with_standard_apply, self)
|
759
771
|
|
760
772
|
def sym_jsonify(
|
761
|
-
self,
|
762
|
-
|
763
|
-
**kwargs) -> object_utils.JSONValueType:
|
773
|
+
self, use_inferred: bool = False, **kwargs
|
774
|
+
) -> utils.JSONValueType:
|
764
775
|
"""Converts current list to a list of plain Python objects."""
|
765
776
|
def json_item(idx):
|
766
777
|
v = self.sym_getattr(idx)
|
@@ -778,7 +789,7 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
778
789
|
python_format: bool = False,
|
779
790
|
use_inferred: bool = False,
|
780
791
|
cls_name: Optional[str] = None,
|
781
|
-
bracket_type:
|
792
|
+
bracket_type: utils.BracketType = utils.BracketType.SQUARE,
|
782
793
|
**kwargs,
|
783
794
|
) -> str:
|
784
795
|
"""Formats this List."""
|
@@ -787,16 +798,22 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
787
798
|
return ' ' * 2 * indent + text
|
788
799
|
|
789
800
|
cls_name = cls_name or ''
|
790
|
-
open_bracket, close_bracket =
|
801
|
+
open_bracket, close_bracket = utils.bracket_chars(bracket_type)
|
791
802
|
s = [f'{cls_name}{open_bracket}']
|
792
803
|
if compact:
|
793
804
|
kv_strs = []
|
794
805
|
for idx, elem in self.sym_items():
|
795
806
|
if use_inferred and isinstance(elem, base.Inferential):
|
796
807
|
elem = self.sym_inferred(idx, default=elem)
|
797
|
-
v_str =
|
798
|
-
elem,
|
799
|
-
|
808
|
+
v_str = utils.format(
|
809
|
+
elem,
|
810
|
+
compact,
|
811
|
+
verbose,
|
812
|
+
root_indent + 1,
|
813
|
+
python_format=python_format,
|
814
|
+
use_inferred=use_inferred,
|
815
|
+
**kwargs,
|
816
|
+
)
|
800
817
|
if python_format:
|
801
818
|
kv_strs.append(v_str)
|
802
819
|
else:
|
@@ -812,9 +829,15 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
812
829
|
s.append('\n')
|
813
830
|
else:
|
814
831
|
s.append(',\n')
|
815
|
-
v_str =
|
816
|
-
elem,
|
817
|
-
|
832
|
+
v_str = utils.format(
|
833
|
+
elem,
|
834
|
+
compact,
|
835
|
+
verbose,
|
836
|
+
root_indent + 1,
|
837
|
+
python_format=python_format,
|
838
|
+
use_inferred=use_inferred,
|
839
|
+
**kwargs,
|
840
|
+
)
|
818
841
|
if python_format:
|
819
842
|
s.append(_indent(v_str, root_indent + 1))
|
820
843
|
else:
|
@@ -20,8 +20,8 @@ import pickle
|
|
20
20
|
from typing import Any
|
21
21
|
import unittest
|
22
22
|
|
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 flags
|
27
27
|
from pyglove.core.symbolic import inferred
|
@@ -34,7 +34,7 @@ from pyglove.core.symbolic.pure_symbolic import NonDeterministic
|
|
34
34
|
from pyglove.core.symbolic.pure_symbolic import PureSymbolic
|
35
35
|
|
36
36
|
|
37
|
-
MISSING_VALUE =
|
37
|
+
MISSING_VALUE = utils.MISSING_VALUE
|
38
38
|
|
39
39
|
|
40
40
|
class ListTest(unittest.TestCase):
|
@@ -685,7 +685,7 @@ class ListTest(unittest.TestCase):
|
|
685
685
|
self.assertTrue(sl.sym_has('[0].x'))
|
686
686
|
self.assertTrue(sl.sym_has('[0].x[0]'))
|
687
687
|
self.assertTrue(sl.sym_has('[0].x[0].y'))
|
688
|
-
self.assertTrue(sl.sym_has(
|
688
|
+
self.assertTrue(sl.sym_has(utils.KeyPath.parse('[0].x[0].y')))
|
689
689
|
|
690
690
|
def test_sym_get(self):
|
691
691
|
sl = List([dict(x=[dict(y=1)])])
|
@@ -1100,7 +1100,7 @@ class ListTest(unittest.TestCase):
|
|
1100
1100
|
self.assertEqual(sl[1].sym_path, '[1]')
|
1101
1101
|
self.assertEqual(sl[1][0].b.sym_path, '[1][0].b')
|
1102
1102
|
|
1103
|
-
sl.sym_setpath(
|
1103
|
+
sl.sym_setpath(utils.KeyPath('a'))
|
1104
1104
|
self.assertEqual(sl.sym_path, 'a')
|
1105
1105
|
self.assertEqual(sl[0].sym_path, 'a[0]')
|
1106
1106
|
self.assertEqual(sl[0].a.sym_path, 'a[0].a')
|
@@ -1412,96 +1412,112 @@ class RebindTest(unittest.TestCase):
|
|
1412
1412
|
'[3]': 'foo', # Unchanged.
|
1413
1413
|
'[4]': Insertion('bar')
|
1414
1414
|
})
|
1415
|
-
self.assertEqual(
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1419
|
-
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1426
|
-
|
1427
|
-
|
1428
|
-
|
1429
|
-
|
1430
|
-
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1444
|
-
|
1445
|
-
|
1446
|
-
|
1447
|
-
|
1448
|
-
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
|
1453
|
-
|
1454
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
|
1463
|
-
|
1464
|
-
|
1465
|
-
|
1466
|
-
|
1467
|
-
|
1468
|
-
|
1469
|
-
|
1470
|
-
|
1471
|
-
|
1472
|
-
|
1473
|
-
|
1474
|
-
|
1475
|
-
|
1476
|
-
|
1477
|
-
|
1478
|
-
|
1479
|
-
|
1480
|
-
|
1481
|
-
|
1482
|
-
|
1483
|
-
|
1484
|
-
|
1485
|
-
|
1486
|
-
|
1487
|
-
|
1488
|
-
|
1489
|
-
|
1490
|
-
|
1491
|
-
|
1492
|
-
|
1493
|
-
|
1494
|
-
|
1495
|
-
|
1496
|
-
|
1497
|
-
|
1498
|
-
|
1499
|
-
|
1500
|
-
|
1501
|
-
|
1502
|
-
|
1503
|
-
|
1504
|
-
|
1415
|
+
self.assertEqual(
|
1416
|
+
updates,
|
1417
|
+
[
|
1418
|
+
{ # Notification to `sl[2][0]`.
|
1419
|
+
'p': base.FieldUpdate(
|
1420
|
+
utils.KeyPath.parse('[2][0].p'),
|
1421
|
+
target=sl[2][0],
|
1422
|
+
field=None,
|
1423
|
+
old_value=1,
|
1424
|
+
new_value=MISSING_VALUE,
|
1425
|
+
),
|
1426
|
+
'q': base.FieldUpdate(
|
1427
|
+
utils.KeyPath.parse('[2][0].q'),
|
1428
|
+
target=sl[2][0],
|
1429
|
+
field=None,
|
1430
|
+
old_value=MISSING_VALUE,
|
1431
|
+
new_value=2,
|
1432
|
+
),
|
1433
|
+
},
|
1434
|
+
{ # Notification to `sl.c`.
|
1435
|
+
'[0].p': base.FieldUpdate(
|
1436
|
+
utils.KeyPath.parse('[2][0].p'),
|
1437
|
+
target=sl[2][0],
|
1438
|
+
field=None,
|
1439
|
+
old_value=1,
|
1440
|
+
new_value=MISSING_VALUE,
|
1441
|
+
),
|
1442
|
+
'[0].q': base.FieldUpdate(
|
1443
|
+
utils.KeyPath.parse('[2][0].q'),
|
1444
|
+
target=sl[2][0],
|
1445
|
+
field=None,
|
1446
|
+
old_value=MISSING_VALUE,
|
1447
|
+
new_value=2,
|
1448
|
+
),
|
1449
|
+
},
|
1450
|
+
{ # Notification to `sl[1].y`.
|
1451
|
+
'z': base.FieldUpdate(
|
1452
|
+
utils.KeyPath.parse('[1].y.z'),
|
1453
|
+
target=sl[1].y,
|
1454
|
+
field=None,
|
1455
|
+
old_value=MISSING_VALUE,
|
1456
|
+
new_value=1,
|
1457
|
+
),
|
1458
|
+
},
|
1459
|
+
{ # Notification to `sl.b`.
|
1460
|
+
'x': base.FieldUpdate(
|
1461
|
+
utils.KeyPath.parse('[1].x'),
|
1462
|
+
target=sl[1],
|
1463
|
+
field=None,
|
1464
|
+
old_value=1,
|
1465
|
+
new_value=2,
|
1466
|
+
),
|
1467
|
+
'y.z': base.FieldUpdate(
|
1468
|
+
utils.KeyPath.parse('[1].y.z'),
|
1469
|
+
target=sl[1].y,
|
1470
|
+
field=None,
|
1471
|
+
old_value=MISSING_VALUE,
|
1472
|
+
new_value=1,
|
1473
|
+
),
|
1474
|
+
},
|
1475
|
+
{ # Notification to `sl`.
|
1476
|
+
'[0]': base.FieldUpdate(
|
1477
|
+
utils.KeyPath.parse('[0]'),
|
1478
|
+
target=sl,
|
1479
|
+
field=None,
|
1480
|
+
old_value=1,
|
1481
|
+
new_value=2,
|
1482
|
+
),
|
1483
|
+
'[1].x': base.FieldUpdate(
|
1484
|
+
utils.KeyPath.parse('[1].x'),
|
1485
|
+
target=sl[1],
|
1486
|
+
field=None,
|
1487
|
+
old_value=1,
|
1488
|
+
new_value=2,
|
1489
|
+
),
|
1490
|
+
'[1].y.z': base.FieldUpdate(
|
1491
|
+
utils.KeyPath.parse('[1].y.z'),
|
1492
|
+
target=sl[1].y,
|
1493
|
+
field=None,
|
1494
|
+
old_value=MISSING_VALUE,
|
1495
|
+
new_value=1,
|
1496
|
+
),
|
1497
|
+
'[2][0].p': base.FieldUpdate(
|
1498
|
+
utils.KeyPath.parse('[2][0].p'),
|
1499
|
+
target=sl[2][0],
|
1500
|
+
field=None,
|
1501
|
+
old_value=1,
|
1502
|
+
new_value=MISSING_VALUE,
|
1503
|
+
),
|
1504
|
+
'[2][0].q': base.FieldUpdate(
|
1505
|
+
utils.KeyPath.parse('[2][0].q'),
|
1506
|
+
target=sl[2][0],
|
1507
|
+
field=None,
|
1508
|
+
old_value=MISSING_VALUE,
|
1509
|
+
new_value=2,
|
1510
|
+
),
|
1511
|
+
'[4]': base.FieldUpdate(
|
1512
|
+
utils.KeyPath.parse('[4]'),
|
1513
|
+
target=sl,
|
1514
|
+
field=None,
|
1515
|
+
old_value=MISSING_VALUE,
|
1516
|
+
new_value='bar',
|
1517
|
+
),
|
1518
|
+
},
|
1519
|
+
],
|
1520
|
+
)
|
1505
1521
|
|
1506
1522
|
def test_rebind_with_fn(self):
|
1507
1523
|
sl = List([0, dict(x=1, y='foo', z=[2, 3, 4])])
|
@@ -1716,7 +1732,7 @@ class FormatTest(unittest.TestCase):
|
|
1716
1732
|
|
1717
1733
|
def test_compact_python_format(self):
|
1718
1734
|
self.assertEqual(
|
1719
|
-
|
1735
|
+
utils.format(
|
1720
1736
|
self._list, compact=True, python_format=True, markdown=True
|
1721
1737
|
),
|
1722
1738
|
"`[{'a1': 1, 'a2': {'b1': {'c1': [{'d1': MISSING_VALUE, "
|
@@ -1726,9 +1742,12 @@ class FormatTest(unittest.TestCase):
|
|
1726
1742
|
|
1727
1743
|
def test_noncompact_python_format(self):
|
1728
1744
|
self.assertEqual(
|
1729
|
-
|
1730
|
-
self._list,
|
1731
|
-
|
1745
|
+
utils.format(
|
1746
|
+
self._list,
|
1747
|
+
compact=False,
|
1748
|
+
verbose=False,
|
1749
|
+
python_format=True,
|
1750
|
+
markdown=True,
|
1732
1751
|
),
|
1733
1752
|
inspect.cleandoc("""
|
1734
1753
|
```
|