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
@@ -22,7 +22,7 @@ import re
|
|
22
22
|
import sys
|
23
23
|
import typing
|
24
24
|
import __main__
|
25
|
-
from pyglove.core import
|
25
|
+
from pyglove.core import utils
|
26
26
|
from pyglove.core.typing import callable_signature
|
27
27
|
from pyglove.core.typing import class_schema
|
28
28
|
from pyglove.core.typing import inspect as pg_inspect
|
@@ -35,7 +35,7 @@ from pyglove.core.typing.class_schema import ValueSpec
|
|
35
35
|
from pyglove.core.typing.custom_typing import CustomTyping
|
36
36
|
|
37
37
|
|
38
|
-
MISSING_VALUE =
|
38
|
+
MISSING_VALUE = utils.MISSING_VALUE
|
39
39
|
|
40
40
|
|
41
41
|
class _FrozenValuePlaceholder(CustomTyping):
|
@@ -169,7 +169,7 @@ class ValueSpecBase(ValueSpec):
|
|
169
169
|
self,
|
170
170
|
default: typing.Any,
|
171
171
|
use_default_apply: bool = True,
|
172
|
-
root_path: typing.Optional[
|
172
|
+
root_path: typing.Optional[utils.KeyPath] = None,
|
173
173
|
) -> ValueSpec:
|
174
174
|
"""Set default value and returns `self`."""
|
175
175
|
# NOTE(daiyip): Default can be schema.MissingValue types, all are
|
@@ -246,13 +246,13 @@ class ValueSpecBase(ValueSpec):
|
|
246
246
|
self,
|
247
247
|
value: typing.Any,
|
248
248
|
allow_partial: bool = False,
|
249
|
-
child_transform: typing.Optional[
|
250
|
-
[
|
251
|
-
|
252
|
-
]
|
253
|
-
|
249
|
+
child_transform: typing.Optional[
|
250
|
+
typing.Callable[[utils.KeyPath, Field, typing.Any], typing.Any]
|
251
|
+
] = None,
|
252
|
+
root_path: typing.Optional[utils.KeyPath] = None,
|
253
|
+
) -> typing.Any: # pyformat: disable pylint: disable=line-too-long
|
254
254
|
"""Apply spec to validate and complete value."""
|
255
|
-
root_path = root_path or
|
255
|
+
root_path = root_path or utils.KeyPath()
|
256
256
|
if self.frozen and self.default is not _FROZEN_VALUE_PLACEHOLDER:
|
257
257
|
# Always return the default value if a field is frozen.
|
258
258
|
if MISSING_VALUE != value and self.default != value:
|
@@ -291,9 +291,8 @@ class ValueSpecBase(ValueSpec):
|
|
291
291
|
value = self._transform(value)
|
292
292
|
except Exception as e: # pylint: disable=broad-except
|
293
293
|
raise e.__class__(
|
294
|
-
|
295
|
-
|
296
|
-
).with_traceback(sys.exc_info()[2])
|
294
|
+
utils.message_on_path(str(e), root_path)
|
295
|
+
).with_traceback(sys.exc_info()[2])
|
297
296
|
|
298
297
|
return self.skip_user_transform.apply(
|
299
298
|
value,
|
@@ -309,9 +308,12 @@ class ValueSpecBase(ValueSpec):
|
|
309
308
|
converter = type_conversion.get_converter(type(value), self.value_type)
|
310
309
|
if converter is None:
|
311
310
|
raise TypeError(
|
312
|
-
|
311
|
+
utils.message_on_path(
|
313
312
|
f'Expect {self.value_type} '
|
314
|
-
f'but encountered {type(value)!r}: {value}.',
|
313
|
+
f'but encountered {type(value)!r}: {value}.',
|
314
|
+
root_path,
|
315
|
+
)
|
316
|
+
)
|
315
317
|
value = converter(value)
|
316
318
|
|
317
319
|
# NOTE(daiyip): child nodes validation and transformation is done before
|
@@ -325,15 +327,18 @@ class ValueSpecBase(ValueSpec):
|
|
325
327
|
self._validate(root_path, value)
|
326
328
|
return value
|
327
329
|
|
328
|
-
def _validate(self, path:
|
330
|
+
def _validate(self, path: utils.KeyPath, value: typing.Any):
|
329
331
|
"""Validation on applied value. Child class can override."""
|
330
332
|
|
331
|
-
def _apply(
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
333
|
+
def _apply(
|
334
|
+
self,
|
335
|
+
value: typing.Any,
|
336
|
+
allow_partial: bool,
|
337
|
+
child_transform: typing.Callable[
|
338
|
+
[utils.KeyPath, Field, typing.Any], typing.Any
|
339
|
+
],
|
340
|
+
root_path: utils.KeyPath,
|
341
|
+
) -> typing.Any:
|
337
342
|
"""Customized apply so each subclass can override."""
|
338
343
|
del allow_partial
|
339
344
|
del child_transform
|
@@ -401,17 +406,17 @@ class ValueSpecBase(ValueSpec):
|
|
401
406
|
**kwargs
|
402
407
|
) -> str:
|
403
408
|
"""Format this object."""
|
404
|
-
return
|
409
|
+
return utils.kvlist_str(
|
405
410
|
[
|
406
411
|
('default', self._default, MISSING_VALUE),
|
407
412
|
('noneable', self._is_noneable, False),
|
408
|
-
('frozen', self._frozen, False)
|
413
|
+
('frozen', self._frozen, False),
|
409
414
|
],
|
410
415
|
label=self.__class__.__name__,
|
411
416
|
compact=compact,
|
412
417
|
verbose=verbose,
|
413
418
|
root_indent=root_indent,
|
414
|
-
**kwargs
|
419
|
+
**kwargs,
|
415
420
|
)
|
416
421
|
|
417
422
|
|
@@ -552,15 +557,18 @@ class Str(Generic, PrimitiveType):
|
|
552
557
|
**kwargs,
|
553
558
|
)
|
554
559
|
|
555
|
-
def _validate(self, path:
|
560
|
+
def _validate(self, path: utils.KeyPath, value: str) -> None:
|
556
561
|
"""Validates applied value."""
|
557
562
|
if not self._regex:
|
558
563
|
return
|
559
564
|
if not self._regex.match(value):
|
560
565
|
raise ValueError(
|
561
|
-
|
566
|
+
utils.message_on_path(
|
562
567
|
f'String {value!r} does not match '
|
563
|
-
f'regular expression {self._regex.pattern!r}.',
|
568
|
+
f'regular expression {self._regex.pattern!r}.',
|
569
|
+
path,
|
570
|
+
)
|
571
|
+
)
|
564
572
|
|
565
573
|
@property
|
566
574
|
def regex(self):
|
@@ -595,7 +603,7 @@ class Str(Generic, PrimitiveType):
|
|
595
603
|
) -> str:
|
596
604
|
"""Format this object."""
|
597
605
|
regex_pattern = self._regex.pattern if self._regex else None
|
598
|
-
return
|
606
|
+
return utils.kvlist_str(
|
599
607
|
[
|
600
608
|
('default', self._default, MISSING_VALUE),
|
601
609
|
('regex', regex_pattern, None),
|
@@ -606,7 +614,7 @@ class Str(Generic, PrimitiveType):
|
|
606
614
|
compact=compact,
|
607
615
|
verbose=verbose,
|
608
616
|
root_indent=root_indent,
|
609
|
-
**kwargs
|
617
|
+
**kwargs,
|
610
618
|
)
|
611
619
|
|
612
620
|
def _eq(self, other: 'Str') -> bool:
|
@@ -665,15 +673,17 @@ class Number(Generic, PrimitiveType):
|
|
665
673
|
"""Returns maximum value of acceptable values."""
|
666
674
|
return self._max_value
|
667
675
|
|
668
|
-
def _validate(self, path:
|
669
|
-
value: numbers.Number) -> None:
|
676
|
+
def _validate(self, path: utils.KeyPath, value: numbers.Number) -> None:
|
670
677
|
"""Validates applied value."""
|
671
678
|
if ((self._min_value is not None and value < self._min_value) or
|
672
679
|
(self._max_value is not None and value > self._max_value)):
|
673
680
|
raise ValueError(
|
674
|
-
|
681
|
+
utils.message_on_path(
|
675
682
|
f'Value {value} is out of range '
|
676
|
-
f'(min={self._min_value}, max={self._max_value}).',
|
683
|
+
f'(min={self._min_value}, max={self._max_value}).',
|
684
|
+
path,
|
685
|
+
)
|
686
|
+
)
|
677
687
|
|
678
688
|
def _extend(self, base: 'Number') -> None:
|
679
689
|
"""Number specific extend."""
|
@@ -726,19 +736,19 @@ class Number(Generic, PrimitiveType):
|
|
726
736
|
**kwargs
|
727
737
|
) -> str:
|
728
738
|
"""Format this object."""
|
729
|
-
return
|
739
|
+
return utils.kvlist_str(
|
730
740
|
[
|
731
741
|
('default', self._default, MISSING_VALUE),
|
732
742
|
('min', self._min_value, None),
|
733
743
|
('max', self._max_value, None),
|
734
744
|
('noneable', self._is_noneable, False),
|
735
|
-
('frozen', self._frozen, False)
|
745
|
+
('frozen', self._frozen, False),
|
736
746
|
],
|
737
747
|
label=self.__class__.__name__,
|
738
748
|
compact=compact,
|
739
749
|
verbose=verbose,
|
740
750
|
root_indent=root_indent,
|
741
|
-
**kwargs
|
751
|
+
**kwargs,
|
742
752
|
)
|
743
753
|
|
744
754
|
def to_json(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]:
|
@@ -944,13 +954,14 @@ class Enum(Generic, PrimitiveType):
|
|
944
954
|
"""Returns all acceptable values of this spec."""
|
945
955
|
return self._values
|
946
956
|
|
947
|
-
def _validate(self, path:
|
957
|
+
def _validate(self, path: utils.KeyPath, value: typing.Any) -> None:
|
948
958
|
"""Validates applied value."""
|
949
959
|
if value not in self._values:
|
950
960
|
raise ValueError(
|
951
|
-
|
952
|
-
f'Value {value!r} is not in candidate list {self._values}.',
|
953
|
-
|
961
|
+
utils.message_on_path(
|
962
|
+
f'Value {value!r} is not in candidate list {self._values}.', path
|
963
|
+
)
|
964
|
+
)
|
954
965
|
|
955
966
|
def _extend(self, base: 'Enum') -> None:
|
956
967
|
"""Enum specific extend."""
|
@@ -995,7 +1006,7 @@ class Enum(Generic, PrimitiveType):
|
|
995
1006
|
**kwargs
|
996
1007
|
) -> str:
|
997
1008
|
"""Format this object."""
|
998
|
-
return
|
1009
|
+
return utils.kvlist_str(
|
999
1010
|
[
|
1000
1011
|
('default', self._default, MISSING_VALUE),
|
1001
1012
|
('values', self._values, None),
|
@@ -1005,7 +1016,7 @@ class Enum(Generic, PrimitiveType):
|
|
1005
1016
|
compact=compact,
|
1006
1017
|
verbose=verbose,
|
1007
1018
|
root_indent=root_indent,
|
1008
|
-
**kwargs
|
1019
|
+
**kwargs,
|
1009
1020
|
)
|
1010
1021
|
|
1011
1022
|
def to_json(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]:
|
@@ -1133,12 +1144,15 @@ class List(Generic, ValueSpecBase):
|
|
1133
1144
|
"""Returns max size of the list."""
|
1134
1145
|
return self._element.key.max_value # pytype: disable=attribute-error # bind-properties
|
1135
1146
|
|
1136
|
-
def _apply(
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1147
|
+
def _apply(
|
1148
|
+
self,
|
1149
|
+
value: typing.List[typing.Any],
|
1150
|
+
allow_partial: bool,
|
1151
|
+
child_transform: typing.Callable[
|
1152
|
+
[utils.KeyPath, Field, typing.Any], typing.Any
|
1153
|
+
],
|
1154
|
+
root_path: utils.KeyPath,
|
1155
|
+
) -> typing.Any:
|
1142
1156
|
"""List specific apply."""
|
1143
1157
|
# NOTE(daiyip): for symbolic List, write access using `__setitem__` will
|
1144
1158
|
# trigger permission error when `accessor_writable` is set to False.
|
@@ -1155,27 +1169,35 @@ class List(Generic, ValueSpecBase):
|
|
1155
1169
|
getitem = getattr(value, 'sym_getattr', value.__getitem__)
|
1156
1170
|
for i in range(len(value)):
|
1157
1171
|
v = self._element.apply(
|
1158
|
-
getitem(i),
|
1159
|
-
|
1172
|
+
getitem(i),
|
1173
|
+
allow_partial=allow_partial,
|
1174
|
+
transform_fn=child_transform,
|
1175
|
+
root_path=utils.KeyPath(i, root_path),
|
1176
|
+
)
|
1160
1177
|
if getitem(i) is not v:
|
1161
1178
|
set_item(i, v)
|
1162
1179
|
return value
|
1163
1180
|
|
1164
|
-
def _validate(
|
1165
|
-
self, path: object_utils.KeyPath, value: typing.List[typing.Any]):
|
1181
|
+
def _validate(self, path: utils.KeyPath, value: typing.List[typing.Any]):
|
1166
1182
|
"""Validates applied value."""
|
1167
1183
|
if len(value) < self.min_size:
|
1168
1184
|
raise ValueError(
|
1169
|
-
|
1185
|
+
utils.message_on_path(
|
1170
1186
|
f'Length of list {value!r} is less than '
|
1171
|
-
f'min size ({self.min_size}).',
|
1187
|
+
f'min size ({self.min_size}).',
|
1188
|
+
path,
|
1189
|
+
)
|
1190
|
+
)
|
1172
1191
|
|
1173
1192
|
if self.max_size is not None:
|
1174
1193
|
if len(value) > self.max_size:
|
1175
1194
|
raise ValueError(
|
1176
|
-
|
1195
|
+
utils.message_on_path(
|
1177
1196
|
f'Length of list {value!r} is greater than '
|
1178
|
-
f'max size ({self.max_size}).',
|
1197
|
+
f'max size ({self.max_size}).',
|
1198
|
+
path,
|
1199
|
+
)
|
1200
|
+
)
|
1179
1201
|
|
1180
1202
|
def _extend(self, base: 'List') -> None:
|
1181
1203
|
"""List specific extend."""
|
@@ -1203,7 +1225,7 @@ class List(Generic, ValueSpecBase):
|
|
1203
1225
|
**kwargs,
|
1204
1226
|
) -> str:
|
1205
1227
|
"""Format this object."""
|
1206
|
-
return
|
1228
|
+
return utils.kvlist_str(
|
1207
1229
|
[
|
1208
1230
|
('', self._element.value, None),
|
1209
1231
|
('min_size', self.min_size, 0),
|
@@ -1216,7 +1238,7 @@ class List(Generic, ValueSpecBase):
|
|
1216
1238
|
compact=compact,
|
1217
1239
|
verbose=verbose,
|
1218
1240
|
root_indent=root_indent,
|
1219
|
-
**kwargs
|
1241
|
+
**kwargs,
|
1220
1242
|
)
|
1221
1243
|
|
1222
1244
|
def to_json(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]:
|
@@ -1409,35 +1431,50 @@ class Tuple(Generic, ValueSpecBase):
|
|
1409
1431
|
"""Returns length of this tuple."""
|
1410
1432
|
return len(self._elements) if self.fixed_length else 0
|
1411
1433
|
|
1412
|
-
def _apply(
|
1413
|
-
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1417
|
-
|
1434
|
+
def _apply(
|
1435
|
+
self,
|
1436
|
+
value: typing.Tuple[typing.Any, ...],
|
1437
|
+
allow_partial: bool,
|
1438
|
+
child_transform: typing.Callable[
|
1439
|
+
[utils.KeyPath, Field, typing.Any], typing.Any
|
1440
|
+
],
|
1441
|
+
root_path: utils.KeyPath,
|
1442
|
+
) -> typing.Any:
|
1418
1443
|
"""Tuple specific apply."""
|
1419
1444
|
if self.fixed_length:
|
1420
1445
|
if len(value) != len(self.elements):
|
1421
1446
|
raise ValueError(
|
1422
|
-
|
1447
|
+
utils.message_on_path(
|
1423
1448
|
f'Length of input tuple ({len(value)}) does not match the '
|
1424
1449
|
f'length of spec ({len(self.elements)}). '
|
1425
|
-
f'Input: {value}, Spec: {self!r}',
|
1450
|
+
f'Input: {value}, Spec: {self!r}',
|
1451
|
+
root_path,
|
1452
|
+
)
|
1453
|
+
)
|
1426
1454
|
else:
|
1427
1455
|
if len(value) < self.min_size:
|
1428
1456
|
raise ValueError(
|
1429
|
-
|
1457
|
+
utils.message_on_path(
|
1430
1458
|
f'Length of tuple {value} is less than '
|
1431
|
-
f'min size ({self.min_size}).',
|
1459
|
+
f'min size ({self.min_size}).',
|
1460
|
+
root_path,
|
1461
|
+
)
|
1462
|
+
)
|
1432
1463
|
if self.max_size is not None and len(value) > self.max_size:
|
1433
1464
|
raise ValueError(
|
1434
|
-
|
1465
|
+
utils.message_on_path(
|
1435
1466
|
f'Length of tuple {value} is greater than '
|
1436
|
-
f'max size ({self.max_size}).',
|
1467
|
+
f'max size ({self.max_size}).',
|
1468
|
+
root_path,
|
1469
|
+
)
|
1470
|
+
)
|
1437
1471
|
return tuple([
|
1438
1472
|
self._elements[i if self.fixed_length else 0].apply( # pylint: disable=g-complex-comprehension
|
1439
|
-
v,
|
1440
|
-
|
1473
|
+
v,
|
1474
|
+
allow_partial=allow_partial,
|
1475
|
+
transform_fn=child_transform,
|
1476
|
+
root_path=utils.KeyPath(i, root_path),
|
1477
|
+
)
|
1441
1478
|
for i, v in enumerate(value)
|
1442
1479
|
])
|
1443
1480
|
|
@@ -1527,7 +1564,7 @@ class Tuple(Generic, ValueSpecBase):
|
|
1527
1564
|
else:
|
1528
1565
|
value = self._elements[0].value
|
1529
1566
|
default_min, default_max = 0, None
|
1530
|
-
return
|
1567
|
+
return utils.kvlist_str(
|
1531
1568
|
[
|
1532
1569
|
('', value, None),
|
1533
1570
|
('default', self._default, MISSING_VALUE),
|
@@ -1540,7 +1577,7 @@ class Tuple(Generic, ValueSpecBase):
|
|
1540
1577
|
compact=compact,
|
1541
1578
|
verbose=verbose,
|
1542
1579
|
root_indent=root_indent,
|
1543
|
-
**kwargs
|
1580
|
+
**kwargs,
|
1544
1581
|
)
|
1545
1582
|
|
1546
1583
|
def to_json(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]:
|
@@ -1687,7 +1724,7 @@ class Dict(Generic, ValueSpecBase):
|
|
1687
1724
|
self,
|
1688
1725
|
default: typing.Any,
|
1689
1726
|
use_default_apply: bool = True,
|
1690
|
-
root_path: typing.Optional[
|
1727
|
+
root_path: typing.Optional[utils.KeyPath] = None,
|
1691
1728
|
) -> ValueSpec:
|
1692
1729
|
if MISSING_VALUE == default and self._schema:
|
1693
1730
|
self._use_generated_default = True
|
@@ -1707,12 +1744,15 @@ class Dict(Generic, ValueSpecBase):
|
|
1707
1744
|
forward_refs.update(field.value.forward_refs)
|
1708
1745
|
return forward_refs
|
1709
1746
|
|
1710
|
-
def _apply(
|
1711
|
-
|
1712
|
-
|
1713
|
-
|
1714
|
-
|
1715
|
-
|
1747
|
+
def _apply(
|
1748
|
+
self,
|
1749
|
+
value: typing.Dict[typing.Any, typing.Any],
|
1750
|
+
allow_partial: bool,
|
1751
|
+
child_transform: typing.Callable[
|
1752
|
+
[utils.KeyPath, Field, typing.Any], typing.Any
|
1753
|
+
],
|
1754
|
+
root_path: utils.KeyPath,
|
1755
|
+
) -> typing.Any:
|
1716
1756
|
"""Dict specific apply."""
|
1717
1757
|
if not self._schema:
|
1718
1758
|
return value
|
@@ -1756,11 +1796,13 @@ class Dict(Generic, ValueSpecBase):
|
|
1756
1796
|
**kwargs,
|
1757
1797
|
) -> str:
|
1758
1798
|
"""Format this object."""
|
1759
|
-
return
|
1799
|
+
return utils.kvlist_str(
|
1760
1800
|
[
|
1761
|
-
(
|
1762
|
-
|
1763
|
-
|
1801
|
+
(
|
1802
|
+
'fields',
|
1803
|
+
list(self._schema.values()) if self._schema else None,
|
1804
|
+
None,
|
1805
|
+
),
|
1764
1806
|
('noneable', self._is_noneable, False),
|
1765
1807
|
('frozen', self._frozen, False),
|
1766
1808
|
],
|
@@ -1892,19 +1934,24 @@ class Object(Generic, ValueSpecBase):
|
|
1892
1934
|
def value_type(self) -> typing.Type[typing.Any]:
|
1893
1935
|
return self.cls
|
1894
1936
|
|
1895
|
-
def _apply(
|
1896
|
-
|
1897
|
-
|
1898
|
-
|
1899
|
-
|
1900
|
-
|
1937
|
+
def _apply(
|
1938
|
+
self,
|
1939
|
+
value: typing.Any,
|
1940
|
+
allow_partial: bool,
|
1941
|
+
child_transform: typing.Callable[
|
1942
|
+
[utils.KeyPath, Field, typing.Any], typing.Any
|
1943
|
+
],
|
1944
|
+
root_path: utils.KeyPath,
|
1945
|
+
) -> typing.Any:
|
1901
1946
|
"""Object specific apply."""
|
1902
1947
|
del child_transform
|
1903
|
-
if isinstance(value,
|
1948
|
+
if isinstance(value, utils.MaybePartial):
|
1904
1949
|
if not allow_partial and value.is_partial:
|
1905
1950
|
raise ValueError(
|
1906
|
-
|
1907
|
-
f'Object {value} is not fully bound.', root_path
|
1951
|
+
utils.message_on_path(
|
1952
|
+
f'Object {value} is not fully bound.', root_path
|
1953
|
+
)
|
1954
|
+
)
|
1908
1955
|
return value
|
1909
1956
|
|
1910
1957
|
def extend(self, base: ValueSpec) -> ValueSpec:
|
@@ -1955,9 +2002,9 @@ class Object(Generic, ValueSpecBase):
|
|
1955
2002
|
name = self._forward_ref.name
|
1956
2003
|
else:
|
1957
2004
|
name = self._value_type.__name__
|
1958
|
-
return
|
2005
|
+
return utils.kvlist_str(
|
1959
2006
|
[
|
1960
|
-
('',
|
2007
|
+
('', utils.RawText(name), None),
|
1961
2008
|
('default', self._default, MISSING_VALUE),
|
1962
2009
|
('noneable', self._is_noneable, False),
|
1963
2010
|
('frozen', self._frozen, False),
|
@@ -1966,7 +2013,7 @@ class Object(Generic, ValueSpecBase):
|
|
1966
2013
|
compact=compact,
|
1967
2014
|
verbose=verbose,
|
1968
2015
|
root_indent=root_indent,
|
1969
|
-
**kwargs
|
2016
|
+
**kwargs,
|
1970
2017
|
)
|
1971
2018
|
|
1972
2019
|
def to_json(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]:
|
@@ -2103,12 +2150,12 @@ class Callable(Generic, ValueSpecBase):
|
|
2103
2150
|
"""Value spec for return value."""
|
2104
2151
|
return self._return_value
|
2105
2152
|
|
2106
|
-
def _validate(self, path:
|
2153
|
+
def _validate(self, path: utils.KeyPath, value: typing.Any) -> None:
|
2107
2154
|
"""Validate applied value."""
|
2108
2155
|
if not callable(value):
|
2109
2156
|
raise TypeError(
|
2110
|
-
|
2111
|
-
|
2157
|
+
utils.message_on_path(f'Value is not callable: {value!r}.', path)
|
2158
|
+
)
|
2112
2159
|
|
2113
2160
|
# Shortcircuit if there is no signature to check.
|
2114
2161
|
if not (self._args or self._kw or self._return_value):
|
@@ -2120,10 +2167,12 @@ class Callable(Generic, ValueSpecBase):
|
|
2120
2167
|
|
2121
2168
|
if len(self._args) > len(signature.args) and not signature.has_varargs:
|
2122
2169
|
raise TypeError(
|
2123
|
-
|
2170
|
+
utils.message_on_path(
|
2124
2171
|
f'{signature.id} only take {len(signature.args)} positional '
|
2125
2172
|
f'arguments, while {len(self._args)} is required by {self!r}.',
|
2126
|
-
path
|
2173
|
+
path,
|
2174
|
+
)
|
2175
|
+
)
|
2127
2176
|
|
2128
2177
|
# Check positional arguments.
|
2129
2178
|
for i in range(min(len(self._args), len(signature.args))):
|
@@ -2131,10 +2180,12 @@ class Callable(Generic, ValueSpecBase):
|
|
2131
2180
|
dest_spec = signature.args[i].value_spec
|
2132
2181
|
if not dest_spec.is_compatible(src_spec):
|
2133
2182
|
raise TypeError(
|
2134
|
-
|
2183
|
+
utils.message_on_path(
|
2135
2184
|
f'Value spec of positional argument {i} is not compatible. '
|
2136
2185
|
f'Expected: {dest_spec!r}, Actual: {src_spec!r}.',
|
2137
|
-
path
|
2186
|
+
path,
|
2187
|
+
)
|
2188
|
+
)
|
2138
2189
|
if len(self._args) > len(signature.args):
|
2139
2190
|
assert signature.varargs
|
2140
2191
|
assert isinstance(signature.varargs.value_spec, List), signature.varargs
|
@@ -2143,10 +2194,13 @@ class Callable(Generic, ValueSpecBase):
|
|
2143
2194
|
src_spec = self._args[i]
|
2144
2195
|
if not dest_spec.is_compatible(src_spec):
|
2145
2196
|
raise TypeError(
|
2146
|
-
|
2197
|
+
utils.message_on_path(
|
2147
2198
|
f'Value spec of positional argument {i} is not compatible '
|
2148
2199
|
f'with the value spec of *{signature.varargs.name}. '
|
2149
|
-
f'Expected: {dest_spec!r}, Actual: {src_spec!r}.',
|
2200
|
+
f'Expected: {dest_spec!r}, Actual: {src_spec!r}.',
|
2201
|
+
path,
|
2202
|
+
)
|
2203
|
+
)
|
2150
2204
|
|
2151
2205
|
# Check keyword arguments.
|
2152
2206
|
dest_args = signature.args + signature.kwonlyargs
|
@@ -2159,37 +2213,46 @@ class Callable(Generic, ValueSpecBase):
|
|
2159
2213
|
if dest_spec is not None:
|
2160
2214
|
if not dest_spec.is_compatible(src_spec):
|
2161
2215
|
raise TypeError(
|
2162
|
-
|
2216
|
+
utils.message_on_path(
|
2163
2217
|
f'Value spec of keyword argument {arg_name!r} is not '
|
2164
2218
|
f'compatible. Expected: {src_spec!r}, Actual: {dest_spec!r}.',
|
2165
|
-
path
|
2219
|
+
path,
|
2220
|
+
)
|
2221
|
+
)
|
2166
2222
|
elif signature.varkw:
|
2167
2223
|
assert isinstance(signature.varkw.value_spec, Dict), signature.varkw
|
2168
2224
|
varkw_value_spec = signature.varkw.value_spec.schema.dynamic_field.value # pytype: disable=attribute-error
|
2169
2225
|
if not varkw_value_spec.is_compatible(src_spec):
|
2170
2226
|
raise TypeError(
|
2171
|
-
|
2227
|
+
utils.message_on_path(
|
2172
2228
|
f'Value spec of keyword argument {arg_name!r} is not '
|
2173
|
-
|
2229
|
+
'compatible with the value spec of '
|
2174
2230
|
f'**{signature.varkw.name}. '
|
2175
2231
|
f'Expected: {varkw_value_spec!r}, '
|
2176
|
-
f'Actual: {src_spec!r}.',
|
2232
|
+
f'Actual: {src_spec!r}.',
|
2233
|
+
path,
|
2234
|
+
)
|
2235
|
+
)
|
2177
2236
|
else:
|
2178
2237
|
raise TypeError(
|
2179
|
-
|
2238
|
+
utils.message_on_path(
|
2180
2239
|
f'Keyword argument {arg_name!r} does not exist in {value!r}.',
|
2181
|
-
path
|
2240
|
+
path,
|
2241
|
+
)
|
2242
|
+
)
|
2182
2243
|
|
2183
2244
|
# Check return value
|
2184
2245
|
if (self._return_value and signature.return_value
|
2185
2246
|
and not isinstance(signature.return_value, Any)
|
2186
2247
|
and not self._return_value.is_compatible(signature.return_value)):
|
2187
2248
|
raise TypeError(
|
2188
|
-
|
2189
|
-
|
2249
|
+
utils.message_on_path(
|
2250
|
+
'Value spec for return value is not compatible. '
|
2190
2251
|
f'Expected: {self._return_value!r}, '
|
2191
2252
|
f'Actual: {signature.return_value!r} ({value!r}).',
|
2192
|
-
path
|
2253
|
+
path,
|
2254
|
+
)
|
2255
|
+
)
|
2193
2256
|
|
2194
2257
|
def _extend(self, base: 'Callable') -> None:
|
2195
2258
|
"""Callable specific extension."""
|
@@ -2260,14 +2323,14 @@ class Callable(Generic, ValueSpecBase):
|
|
2260
2323
|
**kwargs,
|
2261
2324
|
) -> str:
|
2262
2325
|
"""Format this spec."""
|
2263
|
-
return
|
2326
|
+
return utils.kvlist_str(
|
2264
2327
|
[
|
2265
2328
|
('args', self._args, []),
|
2266
2329
|
('kw', self._kw, []),
|
2267
2330
|
('returns', self._return_value, None),
|
2268
2331
|
('default', self._default, MISSING_VALUE),
|
2269
2332
|
('noneable', self._is_noneable, False),
|
2270
|
-
('frozen', self._frozen, False)
|
2333
|
+
('frozen', self._frozen, False),
|
2271
2334
|
],
|
2272
2335
|
label=self.__class__.__name__,
|
2273
2336
|
compact=compact,
|
@@ -2359,14 +2422,14 @@ class Functor(Callable):
|
|
2359
2422
|
returns=returns,
|
2360
2423
|
default=default,
|
2361
2424
|
transform=transform,
|
2362
|
-
callable_type=
|
2425
|
+
callable_type=utils.Functor,
|
2363
2426
|
is_noneable=is_noneable,
|
2364
2427
|
frozen=frozen,
|
2365
2428
|
)
|
2366
2429
|
|
2367
2430
|
def _annotate(self) -> typing.Any:
|
2368
2431
|
"""Annotate with PyType annotation."""
|
2369
|
-
return
|
2432
|
+
return utils.Functor
|
2370
2433
|
|
2371
2434
|
def to_json(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]:
|
2372
2435
|
exclude_keys = kwargs.pop('exclude_keys', set())
|
@@ -2431,12 +2494,14 @@ class Type(Generic, ValueSpecBase):
|
|
2431
2494
|
return set()
|
2432
2495
|
return set([self._forward_ref])
|
2433
2496
|
|
2434
|
-
def _validate(self, path:
|
2497
|
+
def _validate(self, path: utils.KeyPath, value: typing.Type) -> None: # pylint: disable=g-bare-generic
|
2435
2498
|
"""Validate applied value."""
|
2436
2499
|
if self.type_resolved and not pg_inspect.is_subclass(value, self.type):
|
2437
2500
|
raise ValueError(
|
2438
|
-
|
2439
|
-
f'{value!r} is not a subclass of {self.type!r}', path
|
2501
|
+
utils.message_on_path(
|
2502
|
+
f'{value!r} is not a subclass of {self.type!r}', path
|
2503
|
+
)
|
2504
|
+
)
|
2440
2505
|
|
2441
2506
|
def _is_compatible(self, other: 'Type') -> bool:
|
2442
2507
|
"""Type specific compatiblity check."""
|
@@ -2472,7 +2537,7 @@ class Type(Generic, ValueSpecBase):
|
|
2472
2537
|
**kwargs,
|
2473
2538
|
) -> str:
|
2474
2539
|
"""Format this object."""
|
2475
|
-
return
|
2540
|
+
return utils.kvlist_str(
|
2476
2541
|
[
|
2477
2542
|
('', self._expected_type, None),
|
2478
2543
|
('default', self._default, MISSING_VALUE),
|
@@ -2483,7 +2548,7 @@ class Type(Generic, ValueSpecBase):
|
|
2483
2548
|
compact=compact,
|
2484
2549
|
verbose=verbose,
|
2485
2550
|
root_indent=root_indent,
|
2486
|
-
**kwargs
|
2551
|
+
**kwargs,
|
2487
2552
|
)
|
2488
2553
|
|
2489
2554
|
def to_json(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]:
|
@@ -2673,14 +2738,15 @@ class Union(Generic, ValueSpecBase):
|
|
2673
2738
|
return c
|
2674
2739
|
return None
|
2675
2740
|
|
2676
|
-
def _apply(
|
2677
|
-
|
2678
|
-
|
2679
|
-
|
2680
|
-
|
2681
|
-
|
2682
|
-
|
2683
|
-
|
2741
|
+
def _apply(
|
2742
|
+
self,
|
2743
|
+
value: typing.Any,
|
2744
|
+
allow_partial: bool,
|
2745
|
+
child_transform: typing.Callable[
|
2746
|
+
[utils.KeyPath, Field, typing.Any], typing.Any
|
2747
|
+
],
|
2748
|
+
root_path: utils.KeyPath,
|
2749
|
+
) -> typing.Any:
|
2684
2750
|
"""Union specific apply."""
|
2685
2751
|
# Match strong-typed candidates first.
|
2686
2752
|
if not self.type_resolved:
|
@@ -2782,7 +2848,7 @@ class Union(Generic, ValueSpecBase):
|
|
2782
2848
|
**kwargs,
|
2783
2849
|
) -> str:
|
2784
2850
|
"""Format this object."""
|
2785
|
-
return
|
2851
|
+
return utils.kvlist_str(
|
2786
2852
|
[
|
2787
2853
|
('', self._candidates, None),
|
2788
2854
|
('default', self._default, MISSING_VALUE),
|
@@ -2794,7 +2860,7 @@ class Union(Generic, ValueSpecBase):
|
|
2794
2860
|
verbose=verbose,
|
2795
2861
|
root_indent=root_indent,
|
2796
2862
|
list_wrap_threshold=kwargs.pop('list_wrap_threshold', 20),
|
2797
|
-
**kwargs
|
2863
|
+
**kwargs,
|
2798
2864
|
)
|
2799
2865
|
|
2800
2866
|
def to_json(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]:
|
@@ -2942,17 +3008,17 @@ class Any(ValueSpecBase):
|
|
2942
3008
|
**kwargs,
|
2943
3009
|
) -> str:
|
2944
3010
|
"""Format this object."""
|
2945
|
-
return
|
3011
|
+
return utils.kvlist_str(
|
2946
3012
|
[
|
2947
3013
|
('default', self._default, MISSING_VALUE),
|
2948
3014
|
('frozen', self._frozen, False),
|
2949
|
-
('annotation', self._annotation, MISSING_VALUE)
|
3015
|
+
('annotation', self._annotation, MISSING_VALUE),
|
2950
3016
|
],
|
2951
3017
|
label=self.__class__.__name__,
|
2952
3018
|
compact=compact,
|
2953
3019
|
verbose=verbose,
|
2954
3020
|
root_indent=root_indent,
|
2955
|
-
**kwargs
|
3021
|
+
**kwargs,
|
2956
3022
|
)
|
2957
3023
|
|
2958
3024
|
def annotate(self, annotation: typing.Any) -> 'Any':
|
@@ -3022,7 +3088,7 @@ ValueSpec.ObjectType = Object
|
|
3022
3088
|
def ensure_value_spec(
|
3023
3089
|
value_spec: class_schema.ValueSpec,
|
3024
3090
|
src_spec: class_schema.ValueSpec,
|
3025
|
-
root_path: typing.Optional[
|
3091
|
+
root_path: typing.Optional[utils.KeyPath] = None,
|
3026
3092
|
) -> typing.Optional[class_schema.ValueSpec]:
|
3027
3093
|
"""Extract counter part from value spec that matches dest spec type.
|
3028
3094
|
|
@@ -3043,7 +3109,10 @@ def ensure_value_spec(
|
|
3043
3109
|
return None
|
3044
3110
|
if not src_spec.is_compatible(value_spec):
|
3045
3111
|
raise TypeError(
|
3046
|
-
|
3112
|
+
utils.message_on_path(
|
3047
3113
|
f'Source spec {src_spec} is not compatible with destination '
|
3048
|
-
f'spec {value_spec}.',
|
3114
|
+
f'spec {value_spec}.',
|
3115
|
+
root_path,
|
3116
|
+
)
|
3117
|
+
)
|
3049
3118
|
return value_spec
|