pyglove 0.4.5.dev20240319__py3-none-any.whl → 0.4.5.dev202501132210__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 +54 -20
- 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 +309 -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 +54 -41
- pyglove/core/geno/base_test.py +2 -4
- pyglove/core/geno/categorical.py +37 -28
- pyglove/core/geno/custom.py +19 -16
- pyglove/core/geno/numerical.py +20 -17
- pyglove/core/geno/space.py +4 -5
- pyglove/core/hyper/base.py +6 -6
- pyglove/core/hyper/categorical.py +94 -55
- 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 +5 -6
- 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/io/__init__.py +1 -0
- pyglove/core/io/file_system.py +17 -7
- pyglove/core/io/file_system_test.py +2 -0
- pyglove/core/io/sequence.py +299 -0
- pyglove/core/io/sequence_test.py +124 -0
- 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 +17 -5
- pyglove/core/patching/rule_based_test.py +27 -4
- pyglove/core/symbolic/__init__.py +2 -7
- pyglove/core/symbolic/base.py +320 -183
- pyglove/core/symbolic/base_test.py +123 -19
- pyglove/core/symbolic/boilerplate.py +7 -13
- pyglove/core/symbolic/boilerplate_test.py +25 -23
- pyglove/core/symbolic/class_wrapper.py +48 -45
- pyglove/core/symbolic/class_wrapper_test.py +2 -2
- pyglove/core/symbolic/compounding.py +9 -15
- pyglove/core/symbolic/compounding_test.py +2 -4
- pyglove/core/symbolic/dict.py +154 -110
- pyglove/core/symbolic/dict_test.py +238 -130
- pyglove/core/symbolic/diff.py +199 -10
- pyglove/core/symbolic/diff_test.py +226 -0
- pyglove/core/symbolic/flags.py +1 -1
- pyglove/core/symbolic/functor.py +29 -26
- pyglove/core/symbolic/functor_test.py +102 -50
- pyglove/core/symbolic/inferred.py +2 -2
- pyglove/core/symbolic/list.py +81 -50
- pyglove/core/symbolic/list_test.py +119 -97
- pyglove/core/symbolic/object.py +225 -113
- pyglove/core/symbolic/object_test.py +320 -108
- pyglove/core/symbolic/origin.py +17 -14
- pyglove/core/symbolic/origin_test.py +4 -2
- pyglove/core/symbolic/pure_symbolic.py +4 -3
- pyglove/core/symbolic/ref.py +108 -21
- pyglove/core/symbolic/ref_test.py +93 -0
- pyglove/core/symbolic/symbolize_test.py +10 -2
- pyglove/core/tuning/local_backend.py +2 -2
- pyglove/core/tuning/protocols.py +3 -3
- pyglove/core/tuning/sample_test.py +3 -3
- pyglove/core/typing/__init__.py +14 -5
- pyglove/core/typing/annotation_conversion.py +43 -27
- pyglove/core/typing/annotation_conversion_test.py +23 -0
- pyglove/core/typing/callable_ext.py +241 -3
- pyglove/core/typing/callable_ext_test.py +255 -0
- pyglove/core/typing/callable_signature.py +510 -66
- pyglove/core/typing/callable_signature_test.py +619 -99
- pyglove/core/typing/class_schema.py +229 -154
- pyglove/core/typing/class_schema_test.py +149 -95
- pyglove/core/typing/custom_typing.py +5 -4
- pyglove/core/typing/inspect.py +63 -0
- pyglove/core/typing/inspect_test.py +39 -0
- pyglove/core/typing/key_specs.py +10 -11
- pyglove/core/typing/key_specs_test.py +7 -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 +604 -362
- pyglove/core/typing/value_specs_test.py +328 -90
- pyglove/core/utils/__init__.py +164 -0
- pyglove/core/{object_utils → utils}/common_traits.py +3 -67
- pyglove/core/utils/common_traits_test.py +36 -0
- pyglove/core/{object_utils → utils}/docstr_utils.py +23 -0
- pyglove/core/{object_utils → utils}/docstr_utils_test.py +36 -4
- pyglove/core/{object_utils → utils}/error_utils.py +78 -9
- pyglove/core/{object_utils → utils}/error_utils_test.py +61 -5
- pyglove/core/utils/formatting.py +464 -0
- pyglove/core/utils/formatting_test.py +453 -0
- 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 +177 -52
- pyglove/core/{object_utils → utils}/json_conversion_test.py +97 -16
- pyglove/core/{object_utils → utils}/missing.py +3 -3
- 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/utils/timing.py +236 -0
- pyglove/core/utils/timing_test.py +154 -0
- pyglove/core/{object_utils → utils}/value_location.py +275 -6
- pyglove/core/utils/value_location_test.py +707 -0
- pyglove/core/views/__init__.py +32 -0
- pyglove/core/views/base.py +804 -0
- pyglove/core/views/base_test.py +580 -0
- pyglove/core/views/html/__init__.py +27 -0
- pyglove/core/views/html/base.py +547 -0
- pyglove/core/views/html/base_test.py +830 -0
- pyglove/core/views/html/controls/__init__.py +35 -0
- pyglove/core/views/html/controls/base.py +275 -0
- pyglove/core/views/html/controls/label.py +207 -0
- pyglove/core/views/html/controls/label_test.py +157 -0
- pyglove/core/views/html/controls/progress_bar.py +183 -0
- pyglove/core/views/html/controls/progress_bar_test.py +97 -0
- pyglove/core/views/html/controls/tab.py +320 -0
- pyglove/core/views/html/controls/tab_test.py +87 -0
- pyglove/core/views/html/controls/tooltip.py +99 -0
- pyglove/core/views/html/controls/tooltip_test.py +99 -0
- pyglove/core/views/html/tree_view.py +1517 -0
- pyglove/core/views/html/tree_view_test.py +1461 -0
- {pyglove-0.4.5.dev20240319.dist-info → pyglove-0.4.5.dev202501132210.dist-info}/METADATA +18 -4
- pyglove-0.4.5.dev202501132210.dist-info/RECORD +214 -0
- {pyglove-0.4.5.dev20240319.dist-info → pyglove-0.4.5.dev202501132210.dist-info}/WHEEL +1 -1
- pyglove/core/object_utils/__init__.py +0 -154
- pyglove/core/object_utils/common_traits_test.py +0 -82
- pyglove/core/object_utils/formatting.py +0 -234
- pyglove/core/object_utils/formatting_test.py +0 -223
- pyglove/core/object_utils/value_location_test.py +0 -385
- pyglove/core/symbolic/schema_utils.py +0 -327
- pyglove/core/symbolic/schema_utils_test.py +0 -57
- pyglove/core/typing/class_schema_utils.py +0 -202
- pyglove/core/typing/class_schema_utils_test.py +0 -194
- pyglove-0.4.5.dev20240319.dist-info/RECORD +0 -185
- /pyglove/core/{object_utils → utils}/thread_local.py +0 -0
- {pyglove-0.4.5.dev20240319.dist-info → pyglove-0.4.5.dev202501132210.dist-info}/LICENSE +0 -0
- {pyglove-0.4.5.dev20240319.dist-info → pyglove-0.4.5.dev202501132210.dist-info}/top_level.txt +0 -0
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::
|
@@ -132,22 +137,34 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
132
137
|
Returns:
|
133
138
|
A schema-less symbolic list, but its items maybe symbolic.
|
134
139
|
"""
|
135
|
-
return cls(
|
136
|
-
|
137
|
-
|
138
|
-
|
140
|
+
return cls(
|
141
|
+
[
|
142
|
+
base.from_json(
|
143
|
+
v,
|
144
|
+
root_path=utils.KeyPath(i, root_path),
|
145
|
+
allow_partial=allow_partial,
|
146
|
+
**kwargs,
|
147
|
+
)
|
148
|
+
for i, v in enumerate(json_value)
|
149
|
+
],
|
150
|
+
value_spec=value_spec,
|
151
|
+
root_path=root_path,
|
152
|
+
allow_partial=allow_partial,
|
153
|
+
)
|
139
154
|
|
140
155
|
def __init__(
|
141
156
|
self,
|
142
157
|
items: Optional[Iterable[Any]] = None,
|
143
158
|
*,
|
144
159
|
value_spec: Optional[pg_typing.List] = None,
|
145
|
-
onchange_callback: Optional[
|
146
|
-
[Dict[
|
160
|
+
onchange_callback: Optional[
|
161
|
+
Callable[[Dict[utils.KeyPath, base.FieldUpdate]], None]
|
162
|
+
] = None,
|
147
163
|
allow_partial: bool = False,
|
148
164
|
accessor_writable: bool = True,
|
149
165
|
sealed: bool = False,
|
150
|
-
root_path: Optional[
|
166
|
+
root_path: Optional[utils.KeyPath] = None,
|
167
|
+
):
|
151
168
|
"""Constructor.
|
152
169
|
|
153
170
|
Args:
|
@@ -328,8 +345,8 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
328
345
|
return missing
|
329
346
|
|
330
347
|
def _sym_rebind(
|
331
|
-
self, path_value_pairs: typing.Dict[
|
332
|
-
|
348
|
+
self, path_value_pairs: typing.Dict[utils.KeyPath, Any]
|
349
|
+
) -> typing.List[base.FieldUpdate]:
|
333
350
|
"""Subclass specific rebind implementation."""
|
334
351
|
updates = []
|
335
352
|
|
@@ -369,14 +386,13 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
369
386
|
return self
|
370
387
|
|
371
388
|
def _update_children_paths(
|
372
|
-
self,
|
373
|
-
|
374
|
-
new_path: object_utils.KeyPath) -> None:
|
389
|
+
self, old_path: utils.KeyPath, new_path: utils.KeyPath
|
390
|
+
) -> None:
|
375
391
|
"""Update children paths according to root_path of current node."""
|
376
392
|
del old_path
|
377
393
|
for idx, item in self.sym_items():
|
378
394
|
if isinstance(item, base.TopologyAware):
|
379
|
-
item.sym_setpath(
|
395
|
+
item.sym_setpath(utils.KeyPath(idx, new_path))
|
380
396
|
|
381
397
|
def _set_item_without_permission_check( # pytype: disable=signature-mismatch # overriding-parameter-type-checks
|
382
398
|
self, key: int, value: Any) -> Optional[base.FieldUpdate]:
|
@@ -423,13 +439,15 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
423
439
|
value = base.from_json(
|
424
440
|
value,
|
425
441
|
allow_partial=allow_partial,
|
426
|
-
root_path=
|
442
|
+
root_path=utils.KeyPath(idx, self.sym_path),
|
443
|
+
)
|
427
444
|
if self._value_spec and flags.is_type_check_enabled():
|
428
445
|
value = self._value_spec.element.apply(
|
429
446
|
value,
|
430
447
|
allow_partial=allow_partial,
|
431
448
|
transform_fn=base.symbolic_transform_fn(self._allow_partial),
|
432
|
-
root_path=
|
449
|
+
root_path=utils.KeyPath(idx, self.sym_path),
|
450
|
+
)
|
433
451
|
return self._relocate_if_symbolic(idx, value)
|
434
452
|
|
435
453
|
@property
|
@@ -437,8 +455,7 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
437
455
|
"""Returns True if current list subscribes field updates."""
|
438
456
|
return self._onchange_callback is not None
|
439
457
|
|
440
|
-
def _on_change(self,
|
441
|
-
field_updates: Dict[object_utils.KeyPath, base.FieldUpdate]):
|
458
|
+
def _on_change(self, field_updates: Dict[utils.KeyPath, base.FieldUpdate]):
|
442
459
|
"""On change event of List."""
|
443
460
|
# Do nothing for now to handle changes of List.
|
444
461
|
|
@@ -454,7 +471,7 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
454
471
|
# Update paths for children.
|
455
472
|
for idx, item in self.sym_items():
|
456
473
|
if isinstance(item, base.TopologyAware) and item.sym_path.key != idx:
|
457
|
-
item.sym_setpath(
|
474
|
+
item.sym_setpath(utils.KeyPath(idx, self.sym_path))
|
458
475
|
|
459
476
|
if self._onchange_callback is not None:
|
460
477
|
self._onchange_callback(field_updates)
|
@@ -714,11 +731,12 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
714
731
|
|
715
732
|
def custom_apply(
|
716
733
|
self,
|
717
|
-
path:
|
734
|
+
path: utils.KeyPath,
|
718
735
|
value_spec: pg_typing.ValueSpec,
|
719
736
|
allow_partial: bool,
|
720
737
|
child_transform: Optional[
|
721
|
-
Callable[[
|
738
|
+
Callable[[utils.KeyPath, pg_typing.Field, Any], Any]
|
739
|
+
] = None,
|
722
740
|
) -> Tuple[bool, 'List']:
|
723
741
|
"""Implement pg.typing.CustomTyping interface.
|
724
742
|
|
@@ -737,9 +755,12 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
737
755
|
if self._value_spec:
|
738
756
|
if value_spec and not value_spec.is_compatible(self._value_spec):
|
739
757
|
raise ValueError(
|
740
|
-
|
758
|
+
utils.message_on_path(
|
741
759
|
f'List (spec={self._value_spec!r}) cannot be assigned to an '
|
742
|
-
f'incompatible field (spec={value_spec!r}).',
|
760
|
+
f'incompatible field (spec={value_spec!r}).',
|
761
|
+
path,
|
762
|
+
)
|
763
|
+
)
|
743
764
|
if self._allow_partial == allow_partial:
|
744
765
|
proceed_with_standard_apply = False
|
745
766
|
else:
|
@@ -749,9 +770,8 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
749
770
|
return (proceed_with_standard_apply, self)
|
750
771
|
|
751
772
|
def sym_jsonify(
|
752
|
-
self,
|
753
|
-
|
754
|
-
**kwargs) -> object_utils.JSONValueType:
|
773
|
+
self, use_inferred: bool = False, **kwargs
|
774
|
+
) -> utils.JSONValueType:
|
755
775
|
"""Converts current list to a list of plain Python objects."""
|
756
776
|
def json_item(idx):
|
757
777
|
v = self.sym_getattr(idx)
|
@@ -767,10 +787,9 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
767
787
|
root_indent: int = 0,
|
768
788
|
*,
|
769
789
|
python_format: bool = False,
|
770
|
-
markdown: bool = False,
|
771
790
|
use_inferred: bool = False,
|
772
791
|
cls_name: Optional[str] = None,
|
773
|
-
bracket_type:
|
792
|
+
bracket_type: utils.BracketType = utils.BracketType.SQUARE,
|
774
793
|
**kwargs,
|
775
794
|
) -> str:
|
776
795
|
"""Formats this List."""
|
@@ -779,16 +798,22 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
779
798
|
return ' ' * 2 * indent + text
|
780
799
|
|
781
800
|
cls_name = cls_name or ''
|
782
|
-
open_bracket, close_bracket =
|
801
|
+
open_bracket, close_bracket = utils.bracket_chars(bracket_type)
|
783
802
|
s = [f'{cls_name}{open_bracket}']
|
784
803
|
if compact:
|
785
804
|
kv_strs = []
|
786
805
|
for idx, elem in self.sym_items():
|
787
806
|
if use_inferred and isinstance(elem, base.Inferential):
|
788
807
|
elem = self.sym_inferred(idx, default=elem)
|
789
|
-
v_str =
|
790
|
-
elem,
|
791
|
-
|
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
|
+
)
|
792
817
|
if python_format:
|
793
818
|
kv_strs.append(v_str)
|
794
819
|
else:
|
@@ -804,9 +829,15 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
804
829
|
s.append('\n')
|
805
830
|
else:
|
806
831
|
s.append(',\n')
|
807
|
-
v_str =
|
808
|
-
elem,
|
809
|
-
|
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
|
+
)
|
810
841
|
if python_format:
|
811
842
|
s.append(_indent(v_str, root_indent + 1))
|
812
843
|
else:
|
@@ -815,7 +846,7 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
815
846
|
s.append(_indent(close_bracket, root_indent))
|
816
847
|
else:
|
817
848
|
s.append(close_bracket)
|
818
|
-
return
|
849
|
+
return ''.join(s)
|
819
850
|
|
820
851
|
def __copy__(self) -> 'List':
|
821
852
|
"""List.copy."""
|
@@ -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,9 @@ class FormatTest(unittest.TestCase):
|
|
1716
1732
|
|
1717
1733
|
def test_compact_python_format(self):
|
1718
1734
|
self.assertEqual(
|
1719
|
-
|
1735
|
+
utils.format(
|
1736
|
+
self._list, compact=True, python_format=True, markdown=True
|
1737
|
+
),
|
1720
1738
|
"`[{'a1': 1, 'a2': {'b1': {'c1': [{'d1': MISSING_VALUE, "
|
1721
1739
|
"'d2': True, 'd3': A(x=2, y=MISSING_VALUE, z={'p': [None, True], "
|
1722
1740
|
"'q': 'foo', 't': 'foo'})}]}}}]`",
|
@@ -1724,8 +1742,12 @@ class FormatTest(unittest.TestCase):
|
|
1724
1742
|
|
1725
1743
|
def test_noncompact_python_format(self):
|
1726
1744
|
self.assertEqual(
|
1727
|
-
|
1728
|
-
|
1745
|
+
utils.format(
|
1746
|
+
self._list,
|
1747
|
+
compact=False,
|
1748
|
+
verbose=False,
|
1749
|
+
python_format=True,
|
1750
|
+
markdown=True,
|
1729
1751
|
),
|
1730
1752
|
inspect.cleandoc("""
|
1731
1753
|
```
|