pyglove 0.4.5.dev202411132359__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 +15 -16
- pyglove/core/views/html/controls/base.py +46 -9
- pyglove/core/views/html/controls/label.py +13 -2
- pyglove/core/views/html/controls/label_test.py +27 -8
- 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 +217 -66
- pyglove/core/views/html/controls/tab_test.py +46 -15
- pyglove/core/views/html/tree_view.py +39 -37
- {pyglove-0.4.5.dev202411132359.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.dev202411132359.dist-info → pyglove-0.4.5.dev202501250807.dist-info}/WHEEL +1 -1
- pyglove/core/object_utils/__init__.py +0 -164
- pyglove-0.4.5.dev202411132359.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.dev202411132359.dist-info → pyglove-0.4.5.dev202501250807.dist-info}/LICENSE +0 -0
- {pyglove-0.4.5.dev202411132359.dist-info → pyglove-0.4.5.dev202501250807.dist-info}/top_level.txt +0 -0
@@ -18,7 +18,7 @@ import sys
|
|
18
18
|
import typing
|
19
19
|
import unittest
|
20
20
|
|
21
|
-
from pyglove.core import
|
21
|
+
from pyglove.core import utils
|
22
22
|
from pyglove.core.typing import annotation_conversion # pylint: disable=unused-import
|
23
23
|
from pyglove.core.typing import callable_signature
|
24
24
|
from pyglove.core.typing import class_schema
|
@@ -36,11 +36,10 @@ class ValueSpecTest(unittest.TestCase):
|
|
36
36
|
"""Base class for value spec test."""
|
37
37
|
|
38
38
|
def assert_json_conversion(self, v):
|
39
|
-
self.assertEqual(
|
39
|
+
self.assertEqual(utils.from_json(v.to_json()), v)
|
40
40
|
|
41
41
|
def assert_json_conversion_key(self, v, key):
|
42
|
-
self.assertEqual(
|
43
|
-
v.to_json()[object_utils.JSONConvertible.TYPE_NAME_KEY], key)
|
42
|
+
self.assertEqual(v.to_json()[utils.JSONConvertible.TYPE_NAME_KEY], key)
|
44
43
|
|
45
44
|
|
46
45
|
class BoolTest(ValueSpecTest):
|
@@ -99,6 +98,13 @@ class BoolTest(ValueSpecTest):
|
|
99
98
|
with self.assertRaisesRegex(ValueError, 'Value cannot be None'):
|
100
99
|
vs.Bool().apply(None)
|
101
100
|
|
101
|
+
def test_instantiation(self):
|
102
|
+
self.assertTrue(vs.Bool()(True))
|
103
|
+
self.assertFalse(vs.Bool()(False))
|
104
|
+
self.assertIsNone(vs.Bool().noneable()())
|
105
|
+
self.assertFalse(vs.Bool()())
|
106
|
+
self.assertTrue(vs.Bool().freeze(True)(False))
|
107
|
+
|
102
108
|
def test_is_compatible(self):
|
103
109
|
v = vs.Bool()
|
104
110
|
self.assertTrue(v.is_compatible(v))
|
@@ -117,6 +123,12 @@ class BoolTest(ValueSpecTest):
|
|
117
123
|
# Child may extend a noneable base into non-noneable.
|
118
124
|
self.assertFalse(vs.Bool().extend(vs.Bool().noneable()).is_noneable)
|
119
125
|
|
126
|
+
# A frozen child may extend a enum with its value as candidate.
|
127
|
+
self.assertEqual(
|
128
|
+
vs.Bool().freeze(True).extend(vs.Enum(2, [2, True])),
|
129
|
+
vs.Enum(True, [2, True]).freeze(),
|
130
|
+
)
|
131
|
+
|
120
132
|
# Child cannot extend a base with different type.
|
121
133
|
with self.assertRaisesRegex(
|
122
134
|
TypeError, '.* cannot extend .*: incompatible type.'):
|
@@ -127,6 +139,11 @@ class BoolTest(ValueSpecTest):
|
|
127
139
|
TypeError, '.* cannot extend .*: None is not allowed in base spec.'):
|
128
140
|
vs.Bool().noneable().extend(vs.Bool())
|
129
141
|
|
142
|
+
# Child cannot extend a non-noneable base to noneable.
|
143
|
+
with self.assertRaisesRegex(
|
144
|
+
TypeError, '.* cannot extend .* with incompatible frozen value'):
|
145
|
+
vs.Bool().freeze(True).extend(vs.Enum(False, [2, False]))
|
146
|
+
|
130
147
|
def test_freeze(self):
|
131
148
|
self.assertFalse(vs.Bool().frozen)
|
132
149
|
|
@@ -144,7 +161,7 @@ class BoolTest(ValueSpecTest):
|
|
144
161
|
self.assertTrue(v.default)
|
145
162
|
|
146
163
|
with self.assertRaisesRegex(
|
147
|
-
TypeError, '
|
164
|
+
TypeError, '.* cannot extend a frozen value spec'):
|
148
165
|
vs.Bool().extend(v)
|
149
166
|
|
150
167
|
with self.assertRaisesRegex(
|
@@ -224,6 +241,12 @@ class StrTest(ValueSpecTest):
|
|
224
241
|
self.assertNotEqual(vs.Str(), vs.Str(regex='.*'))
|
225
242
|
self.assertNotEqual(vs.Str(regex='a'), vs.Str(regex='.*'))
|
226
243
|
|
244
|
+
def test_instantiation(self):
|
245
|
+
self.assertEqual(vs.Str()('abc'), 'abc')
|
246
|
+
self.assertIsNone(vs.Str().noneable()())
|
247
|
+
self.assertEqual(vs.Str()(), '')
|
248
|
+
self.assertEqual(vs.Str().freeze('abc')('def'), 'abc')
|
249
|
+
|
227
250
|
def test_apply(self):
|
228
251
|
self.assertEqual(vs.Str().apply('a'), 'a')
|
229
252
|
self.assertEqual(vs.Str(regex='a.*').apply('a1'), 'a1')
|
@@ -266,6 +289,12 @@ class StrTest(ValueSpecTest):
|
|
266
289
|
# Child may extend a noneable base into non-noneable.
|
267
290
|
self.assertFalse(vs.Str().extend(vs.Str().noneable()).is_noneable)
|
268
291
|
|
292
|
+
# A frozen child may extend a enum with its value as candidate.
|
293
|
+
self.assertEqual(
|
294
|
+
vs.Str().freeze('a').extend(vs.Enum(2, [2, 'a'])),
|
295
|
+
vs.Enum('a', [2, 'a']).freeze(),
|
296
|
+
)
|
297
|
+
|
269
298
|
# Child cannot extend a base of different type.
|
270
299
|
with self.assertRaisesRegex(
|
271
300
|
TypeError, '.* cannot extend .*: incompatible type.'):
|
@@ -276,6 +305,11 @@ class StrTest(ValueSpecTest):
|
|
276
305
|
TypeError, '.* cannot extend .*: None is not allowed in base spec.'):
|
277
306
|
vs.Str().noneable().extend(vs.Str())
|
278
307
|
|
308
|
+
# Child cannot extend a non-noneable base to noneable.
|
309
|
+
with self.assertRaisesRegex(
|
310
|
+
TypeError, '.* cannot extend .* with incompatible frozen value'):
|
311
|
+
vs.Str().freeze('b').extend(vs.Enum('a', ['a', False]))
|
312
|
+
|
279
313
|
def test_freeze(self):
|
280
314
|
self.assertFalse(vs.Str().frozen)
|
281
315
|
|
@@ -293,7 +327,7 @@ class StrTest(ValueSpecTest):
|
|
293
327
|
self.assertEqual(v.default, 'foo')
|
294
328
|
|
295
329
|
with self.assertRaisesRegex(
|
296
|
-
TypeError, '
|
330
|
+
TypeError, '.* cannot extend a frozen value spec'):
|
297
331
|
vs.Str().extend(v)
|
298
332
|
|
299
333
|
with self.assertRaisesRegex(
|
@@ -307,6 +341,7 @@ class StrTest(ValueSpecTest):
|
|
307
341
|
self.assert_json_conversion(vs.Str().noneable().freeze('abc'))
|
308
342
|
self.assert_json_conversion_key(vs.Str(), 'pyglove.typing.Str')
|
309
343
|
|
344
|
+
|
310
345
|
class IntTest(ValueSpecTest):
|
311
346
|
"""Tests for `Int`."""
|
312
347
|
|
@@ -377,6 +412,16 @@ class IntTest(ValueSpecTest):
|
|
377
412
|
ValueError, '"max_value" must be equal or greater than "min_value".'):
|
378
413
|
vs.Int(min_value=1, max_value=0)
|
379
414
|
|
415
|
+
def test_instantiation(self):
|
416
|
+
self.assertEqual(vs.Int()(1), 1)
|
417
|
+
self.assertEqual(vs.Int()(), 0)
|
418
|
+
self.assertIsNone(vs.Int().noneable()())
|
419
|
+
self.assertEqual(vs.Int().freeze(1)(0), 1)
|
420
|
+
with self.assertRaisesRegex(
|
421
|
+
ValueError, 'Value .* is out of range'
|
422
|
+
):
|
423
|
+
vs.Int(min_value=1)()
|
424
|
+
|
380
425
|
def test_apply(self):
|
381
426
|
self.assertEqual(vs.Int().apply(1), 1)
|
382
427
|
self.assertEqual(vs.Int(min_value=1, max_value=1).apply(1), 1)
|
@@ -434,6 +479,12 @@ class IntTest(ValueSpecTest):
|
|
434
479
|
vs.Int(min_value=1),
|
435
480
|
)
|
436
481
|
|
482
|
+
# A frozen child may extend a enum with its value as candidate.
|
483
|
+
self.assertEqual(
|
484
|
+
vs.Int().freeze(2).extend(vs.Enum(2, [2, 'a'])),
|
485
|
+
vs.Enum(2, [2, 'a']).freeze(),
|
486
|
+
)
|
487
|
+
|
437
488
|
with self.assertRaisesRegex(TypeError,
|
438
489
|
'.* cannot extend .*: incompatible type.'):
|
439
490
|
vs.Int().extend(vs.Bool())
|
@@ -466,6 +517,11 @@ class IntTest(ValueSpecTest):
|
|
466
517
|
TypeError, '.* cannot extend .*: no compatible type found in Union.'):
|
467
518
|
vs.Int().extend(vs.Union([vs.Bool(), vs.Str()]))
|
468
519
|
|
520
|
+
# Child cannot extend a non-noneable base to noneable.
|
521
|
+
with self.assertRaisesRegex(
|
522
|
+
TypeError, '.* cannot extend .* with incompatible frozen value'):
|
523
|
+
vs.Int().freeze(1).extend(vs.Enum('a', ['a', False]))
|
524
|
+
|
469
525
|
def test_freeze(self):
|
470
526
|
self.assertFalse(vs.Int().frozen)
|
471
527
|
|
@@ -483,7 +539,7 @@ class IntTest(ValueSpecTest):
|
|
483
539
|
self.assertEqual(v.default, 1)
|
484
540
|
|
485
541
|
with self.assertRaisesRegex(
|
486
|
-
TypeError, '
|
542
|
+
TypeError, '.* cannot extend a frozen value spec'):
|
487
543
|
vs.Int().extend(v)
|
488
544
|
|
489
545
|
with self.assertRaisesRegex(
|
@@ -571,6 +627,16 @@ class FloatTest(ValueSpecTest):
|
|
571
627
|
ValueError, '"max_value" must be equal or greater than "min_value".'):
|
572
628
|
vs.Float(min_value=1., max_value=0.)
|
573
629
|
|
630
|
+
def test_instantiation(self):
|
631
|
+
self.assertEqual(vs.Float()(1), 1.0)
|
632
|
+
self.assertEqual(vs.Float()(), 0.0)
|
633
|
+
self.assertIsNone(vs.Float().noneable()())
|
634
|
+
self.assertEqual(vs.Float().freeze(1.0)(0), 1.0)
|
635
|
+
with self.assertRaisesRegex(
|
636
|
+
ValueError, 'Value .* is out of range'
|
637
|
+
):
|
638
|
+
vs.Float(min_value=1)()
|
639
|
+
|
574
640
|
def test_apply(self):
|
575
641
|
self.assertEqual(vs.Float().apply(1.), 1.)
|
576
642
|
self.assertEqual(vs.Float().apply(1), 1.)
|
@@ -617,6 +683,12 @@ class FloatTest(ValueSpecTest):
|
|
617
683
|
# Child may extend a noneable base into non-noneable.
|
618
684
|
self.assertFalse(vs.Float().extend(vs.Float().noneable()).is_noneable)
|
619
685
|
|
686
|
+
# A frozen child may extend a enum with its value as candidate.
|
687
|
+
self.assertEqual(
|
688
|
+
vs.Float().freeze(2.5).extend(vs.Enum(2.5, [2.5, 'a'])),
|
689
|
+
vs.Enum(2.5, [2.5, 'a']).freeze(),
|
690
|
+
)
|
691
|
+
|
620
692
|
with self.assertRaisesRegex(
|
621
693
|
TypeError, '.* cannot extend .*: incompatible type.'):
|
622
694
|
vs.Float().extend(vs.Int())
|
@@ -645,6 +717,11 @@ class FloatTest(ValueSpecTest):
|
|
645
717
|
'min_value .* is greater than max_value .* after extension'):
|
646
718
|
vs.Float(min_value=1.).extend(vs.Float(max_value=0.))
|
647
719
|
|
720
|
+
# Child cannot extend a non-noneable base to noneable.
|
721
|
+
with self.assertRaisesRegex(
|
722
|
+
TypeError, '.* cannot extend .* with incompatible frozen value'):
|
723
|
+
vs.Float().freeze(1.0).extend(vs.Enum('a', ['a', False]))
|
724
|
+
|
648
725
|
def test_freeze(self):
|
649
726
|
self.assertFalse(vs.Float().frozen)
|
650
727
|
|
@@ -662,7 +739,7 @@ class FloatTest(ValueSpecTest):
|
|
662
739
|
self.assertEqual(v.default, 1.0)
|
663
740
|
|
664
741
|
with self.assertRaisesRegex(
|
665
|
-
TypeError, '
|
742
|
+
TypeError, '.* cannot extend a frozen value spec'):
|
666
743
|
vs.Float().extend(v)
|
667
744
|
|
668
745
|
with self.assertRaisesRegex(
|
@@ -768,6 +845,11 @@ class EnumTest(ValueSpecTest):
|
|
768
845
|
ValueError, 'Enum default value \'a\' is not in candidate list.'):
|
769
846
|
vs.Enum('a', ['b'])
|
770
847
|
|
848
|
+
def test_instantiation(self):
|
849
|
+
self.assertEqual(vs.Enum(1, [1, 2, 3])(), 1)
|
850
|
+
self.assertEqual(vs.Enum(1, [1, 2, 3])(2), 2)
|
851
|
+
self.assertEqual(vs.Enum(1, [1, 2, 3]).freeze(2)(3), 2)
|
852
|
+
|
771
853
|
def test_apply(self):
|
772
854
|
self.assertEqual(vs.Enum('a', ['a']).apply('a'), 'a')
|
773
855
|
self.assertIsNone(vs.Enum('a', ['a', None]).apply(None))
|
@@ -784,6 +866,8 @@ class EnumTest(ValueSpecTest):
|
|
784
866
|
self.assertTrue(
|
785
867
|
vs.Enum(0, [0, 1]).is_compatible(vs.Enum(0, [0, 1])))
|
786
868
|
self.assertTrue(vs.Enum(0, [0, 1]).is_compatible(vs.Enum(0, [0])))
|
869
|
+
self.assertTrue(vs.Enum(0, [0, 'a']).is_compatible(vs.Int().freeze(0)))
|
870
|
+
self.assertTrue(vs.Enum(0, [0, 'a']).is_compatible(vs.Str().freeze('a')))
|
787
871
|
self.assertFalse(vs.Enum(0, [0]).is_compatible(vs.Enum(0, [0, 1])))
|
788
872
|
self.assertFalse(vs.Enum(0, [0]).is_compatible(vs.Int()))
|
789
873
|
|
@@ -814,7 +898,7 @@ class EnumTest(ValueSpecTest):
|
|
814
898
|
self.assertEqual(v.default, 'a')
|
815
899
|
|
816
900
|
with self.assertRaisesRegex(
|
817
|
-
TypeError, '
|
901
|
+
TypeError, '.* cannot extend a frozen value spec'):
|
818
902
|
vs.Enum('c', ['a', 'b', 'c']).extend(v)
|
819
903
|
|
820
904
|
def test_json_conversion(self):
|
@@ -942,15 +1026,21 @@ class ListTest(ValueSpecTest):
|
|
942
1026
|
'Either "size" or "min_size"/"max_size" pair can be specified.'):
|
943
1027
|
vs.List(vs.Int(), size=5, min_size=1)
|
944
1028
|
|
1029
|
+
def test_instantiation(self):
|
1030
|
+
self.assertEqual(vs.List(vs.Int())([1]), [1])
|
1031
|
+
self.assertEqual(vs.List(vs.Int())(), [])
|
1032
|
+
self.assertIsNone(vs.List(vs.Int()).noneable()())
|
1033
|
+
self.assertEqual(vs.List(vs.Int()).freeze([0])([]), [0])
|
1034
|
+
|
945
1035
|
def test_apply(self):
|
946
1036
|
self.assertEqual(vs.List(vs.Int()).apply([]), [])
|
947
1037
|
self.assertEqual(vs.List(vs.Int()).apply([1]), [1])
|
948
1038
|
self.assertEqual(vs.List(vs.Int().noneable()).apply([1, None]), [1, None])
|
949
1039
|
# Automatic conversion: str -> KeyPath is a registered conversion.
|
950
1040
|
# See 'type_conversion.py'.
|
951
|
-
l = vs.List(vs.Object(
|
952
|
-
self.assertIsInstance(l[0],
|
953
|
-
self.assertEqual(l, [
|
1041
|
+
l = vs.List(vs.Object(utils.KeyPath)).apply(['a.b.c'])
|
1042
|
+
self.assertIsInstance(l[0], utils.KeyPath)
|
1043
|
+
self.assertEqual(l, [utils.KeyPath.parse('a.b.c')])
|
954
1044
|
self.assertEqual(
|
955
1045
|
vs.List(vs.Int()).apply(
|
956
1046
|
typed_missing.MISSING_VALUE, allow_partial=True),
|
@@ -1107,7 +1197,7 @@ class ListTest(ValueSpecTest):
|
|
1107
1197
|
self.assertEqual(v.default, [1])
|
1108
1198
|
|
1109
1199
|
with self.assertRaisesRegex(
|
1110
|
-
TypeError, '
|
1200
|
+
TypeError, '.* cannot extend a frozen value spec'):
|
1111
1201
|
vs.List(vs.Int()).extend(v)
|
1112
1202
|
|
1113
1203
|
with self.assertRaisesRegex(
|
@@ -1301,6 +1391,12 @@ class TupleTest(ValueSpecTest):
|
|
1301
1391
|
'<(type|class) \'int\'>.'):
|
1302
1392
|
vs.Tuple([vs.Int()], default=1)
|
1303
1393
|
|
1394
|
+
def test_instantiation(self):
|
1395
|
+
self.assertEqual(vs.Tuple(vs.Int())([1]), (1,))
|
1396
|
+
self.assertEqual(vs.Tuple(vs.Int())(), ())
|
1397
|
+
self.assertIsNone(vs.Tuple(vs.Int()).noneable()())
|
1398
|
+
self.assertEqual(vs.Tuple(vs.Int()).freeze((0,))((1, 2)), (0,))
|
1399
|
+
|
1304
1400
|
def test_apply(self):
|
1305
1401
|
self.assertEqual(vs.Tuple(vs.Int()).apply(tuple()), tuple())
|
1306
1402
|
self.assertEqual(vs.Tuple(vs.Int()).apply((1, 1, 1)), (1, 1, 1))
|
@@ -1533,7 +1629,7 @@ class TupleTest(ValueSpecTest):
|
|
1533
1629
|
self.assertEqual(v.default, (1,))
|
1534
1630
|
|
1535
1631
|
with self.assertRaisesRegex(
|
1536
|
-
TypeError, '
|
1632
|
+
TypeError, '.* cannot extend a frozen value spec'):
|
1537
1633
|
vs.Tuple(vs.Int()).extend(v)
|
1538
1634
|
|
1539
1635
|
with self.assertRaisesRegex(
|
@@ -1756,6 +1852,12 @@ class DictTest(ValueSpecTest):
|
|
1756
1852
|
'should be a dict of objects.'):
|
1757
1853
|
vs.Dict([('key', 1, 'field 1', 123)])
|
1758
1854
|
|
1855
|
+
def test_instantiation(self):
|
1856
|
+
self.assertEqual(vs.Dict()(), {})
|
1857
|
+
self.assertEqual(vs.Dict()({'x': 1, 2: 2}), {'x': 1, 2: 2})
|
1858
|
+
self.assertEqual(vs.Dict()(x=1), dict(x=1))
|
1859
|
+
self.assertEqual(vs.Dict({'a': int, 'b': 1})(a=1), dict(a=1, b=1))
|
1860
|
+
|
1759
1861
|
def test_apply(self):
|
1760
1862
|
self.assertEqual(vs.Dict().apply({'a': 1}), {'a': 1})
|
1761
1863
|
self.assertEqual(
|
@@ -1943,7 +2045,7 @@ class DictTest(ValueSpecTest):
|
|
1943
2045
|
x = vs.Dict([
|
1944
2046
|
('a', int, 'field 1', dict(x=1)),
|
1945
2047
|
]).freeze(dict(a=1))
|
1946
|
-
y =
|
2048
|
+
y = utils.from_json(x.to_json())
|
1947
2049
|
self.assert_json_conversion(
|
1948
2050
|
vs.Dict([
|
1949
2051
|
('a', int, 'field 1', dict(x=1)),
|
@@ -1986,7 +2088,7 @@ class ObjectTest(ValueSpecTest):
|
|
1986
2088
|
class C(A):
|
1987
2089
|
pass
|
1988
2090
|
|
1989
|
-
class D(C,
|
2091
|
+
class D(C, utils.MaybePartial):
|
1990
2092
|
|
1991
2093
|
def missing_values(self):
|
1992
2094
|
return {'SOME_KEY': 'SOME_VALUE'}
|
@@ -2108,6 +2210,10 @@ class ObjectTest(ValueSpecTest):
|
|
2108
2210
|
TypeError, '<(type|class) \'object\'> is too general for Object spec.'):
|
2109
2211
|
vs.Object(object)
|
2110
2212
|
|
2213
|
+
def test_instantiation(self):
|
2214
|
+
self.assertIsInstance(vs.Object(self.A)(), self.A)
|
2215
|
+
self.assertIsInstance(vs.Object(self.B)(1), self.B)
|
2216
|
+
|
2111
2217
|
def test_apply(self):
|
2112
2218
|
a = self.A()
|
2113
2219
|
self.assertEqual(vs.Object(self.A).apply(a), a)
|
@@ -2230,7 +2336,7 @@ class ObjectTest(ValueSpecTest):
|
|
2230
2336
|
self.assertIs(v.default, b)
|
2231
2337
|
|
2232
2338
|
with self.assertRaisesRegex(
|
2233
|
-
TypeError, '
|
2339
|
+
TypeError, '.* cannot extend a frozen value spec'):
|
2234
2340
|
vs.Object(self.A).extend(v)
|
2235
2341
|
|
2236
2342
|
with self.assertRaisesRegex(
|
@@ -2271,7 +2377,7 @@ class CallableTest(ValueSpecTest):
|
|
2271
2377
|
|
2272
2378
|
def test_value_type(self):
|
2273
2379
|
self.assertIsNone(vs.Callable().value_type)
|
2274
|
-
self.assertEqual(vs.Functor().annotation,
|
2380
|
+
self.assertEqual(vs.Functor().annotation, utils.Functor)
|
2275
2381
|
|
2276
2382
|
def test_forward_refs(self):
|
2277
2383
|
self.assertEqual(vs.Callable().forward_refs, set())
|
@@ -2427,6 +2533,10 @@ class CallableTest(ValueSpecTest):
|
|
2427
2533
|
TypeError, '.* only take 0 positional arguments, while 1 is required'):
|
2428
2534
|
vs.Callable([vs.Int()]).apply(f)
|
2429
2535
|
|
2536
|
+
def test_instantiation(self):
|
2537
|
+
with self.assertRaisesRegex(TypeError, '.* cannot be instantiated'):
|
2538
|
+
vs.Callable()()
|
2539
|
+
|
2430
2540
|
def test_apply_on_callable_object(self):
|
2431
2541
|
|
2432
2542
|
class CallableObject:
|
@@ -2473,7 +2583,7 @@ class CallableTest(ValueSpecTest):
|
|
2473
2583
|
|
2474
2584
|
def test_apply_on_functor(self):
|
2475
2585
|
|
2476
|
-
class FunctorWithRegularArgs(
|
2586
|
+
class FunctorWithRegularArgs(utils.Functor):
|
2477
2587
|
|
2478
2588
|
__signature__ = Signature(
|
2479
2589
|
callable_type=callable_signature.CallableType.FUNCTION,
|
@@ -2519,7 +2629,7 @@ class CallableTest(ValueSpecTest):
|
|
2519
2629
|
|
2520
2630
|
def test_apply_on_functor_with_varargs(self):
|
2521
2631
|
|
2522
|
-
class FunctorWithVarArgs(
|
2632
|
+
class FunctorWithVarArgs(utils.Functor):
|
2523
2633
|
|
2524
2634
|
__signature__ = Signature(
|
2525
2635
|
callable_type=callable_signature.CallableType.FUNCTION,
|
@@ -2652,7 +2762,7 @@ class CallableTest(ValueSpecTest):
|
|
2652
2762
|
self.assertIs(v.default, f)
|
2653
2763
|
|
2654
2764
|
with self.assertRaisesRegex(
|
2655
|
-
TypeError, '
|
2765
|
+
TypeError, '.* cannot extend a frozen value spec'):
|
2656
2766
|
vs.Callable().extend(v)
|
2657
2767
|
|
2658
2768
|
with self.assertRaisesRegex(
|
@@ -2670,7 +2780,7 @@ class CallableTest(ValueSpecTest):
|
|
2670
2780
|
)
|
2671
2781
|
)
|
2672
2782
|
x = vs.Callable([vs.Int()], default=lambda x: x + 1).noneable()
|
2673
|
-
y =
|
2783
|
+
y = utils.from_json(x.to_json())
|
2674
2784
|
self.assert_json_conversion(
|
2675
2785
|
vs.Callable([vs.Int()], default=lambda x: x + 1).noneable()
|
2676
2786
|
)
|
@@ -2797,6 +2907,9 @@ class TypeTest(ValueSpecTest):
|
|
2797
2907
|
self.assertNotEqual(
|
2798
2908
|
vs.Type(Exception), vs.Type(Exception, default=ValueError))
|
2799
2909
|
|
2910
|
+
def test_instantiate(self):
|
2911
|
+
self.assertIs(vs.Type[str](), str)
|
2912
|
+
|
2800
2913
|
def test_apply(self):
|
2801
2914
|
self.assertEqual(vs.Type(Exception).apply(Exception), Exception)
|
2802
2915
|
self.assertEqual(vs.Type(Exception).apply(ValueError), ValueError)
|
@@ -2904,7 +3017,7 @@ class TypeTest(ValueSpecTest):
|
|
2904
3017
|
self.assertIs(v.default, e)
|
2905
3018
|
|
2906
3019
|
with self.assertRaisesRegex(
|
2907
|
-
TypeError, '
|
3020
|
+
TypeError, '.* cannot extend a frozen value spec'):
|
2908
3021
|
vs.Type(Exception).extend(v)
|
2909
3022
|
|
2910
3023
|
with self.assertRaisesRegex(
|
@@ -3111,6 +3224,12 @@ class UnionTest(ValueSpecTest):
|
|
3111
3224
|
vs.Union([vs.Callable(), vs.Int()]).get_candidate(vs.Any()),
|
3112
3225
|
vs.Callable())
|
3113
3226
|
|
3227
|
+
def test_instantiate(self):
|
3228
|
+
with self.assertRaisesRegex(
|
3229
|
+
TypeError, '.* cannot be instantiated'
|
3230
|
+
):
|
3231
|
+
vs.Union[int, str]()
|
3232
|
+
|
3114
3233
|
def test_apply(self):
|
3115
3234
|
self.assertEqual(vs.Union([vs.Int(), vs.Str()]).apply(1), 1)
|
3116
3235
|
self.assertEqual(
|
@@ -3261,7 +3380,7 @@ class UnionTest(ValueSpecTest):
|
|
3261
3380
|
self.assertEqual(v.default, 'foo')
|
3262
3381
|
|
3263
3382
|
with self.assertRaisesRegex(
|
3264
|
-
TypeError, '
|
3383
|
+
TypeError, '.* cannot extend a frozen value spec.'):
|
3265
3384
|
vs.Str().extend(v)
|
3266
3385
|
|
3267
3386
|
with self.assertRaisesRegex(
|
@@ -3343,6 +3462,10 @@ class AnyTest(ValueSpecTest):
|
|
3343
3462
|
self.assertNotEqual(vs.Any(), vs.Int())
|
3344
3463
|
self.assertNotEqual(vs.Any(True), vs.Any())
|
3345
3464
|
|
3465
|
+
def test_instantiate(self):
|
3466
|
+
with self.assertRaisesRegex(TypeError, '.* cannot be instantiated'):
|
3467
|
+
vs.Any()()
|
3468
|
+
|
3346
3469
|
def test_apply(self):
|
3347
3470
|
self.assertEqual(vs.Any().apply(True), True)
|
3348
3471
|
self.assertEqual(vs.Any().apply(1), 1)
|
@@ -3392,7 +3515,7 @@ class AnyTest(ValueSpecTest):
|
|
3392
3515
|
self.assertEqual(v.default, 'foo')
|
3393
3516
|
|
3394
3517
|
with self.assertRaisesRegex(
|
3395
|
-
TypeError, '
|
3518
|
+
TypeError, '.* cannot extend a frozen value spec'):
|
3396
3519
|
vs.Any().extend(v)
|
3397
3520
|
|
3398
3521
|
with self.assertRaisesRegex(
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# Copyright 2022 The PyGlove Authors
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
# pylint: disable=line-too-long
|
15
|
+
"""Utility library that provides common traits for objects in Python.
|
16
|
+
|
17
|
+
Overview
|
18
|
+
--------
|
19
|
+
|
20
|
+
``pg.utils`` sits at the bottom of all PyGlove modules and empowers other
|
21
|
+
modules with the following features:
|
22
|
+
|
23
|
+
+---------------------+--------------------------------------------+
|
24
|
+
| Functionality | API |
|
25
|
+
+=====================+============================================+
|
26
|
+
| Formatting | :class:`pg.Formattable`, |
|
27
|
+
| | |
|
28
|
+
| | :func:`pg.format`, |
|
29
|
+
| | |
|
30
|
+
| | :func:`pg.print`, |
|
31
|
+
| | |
|
32
|
+
| | :func:`pg.utils.kvlist_str`, |
|
33
|
+
| | |
|
34
|
+
| | :func:`pg.utils.quote_if_str`, |
|
35
|
+
| | |
|
36
|
+
| | :func:`pg.utils.message_on_path` |
|
37
|
+
+---------------------+--------------------------------------------+
|
38
|
+
| Serialization | :class:`pg.JSONConvertible`, |
|
39
|
+
| | |
|
40
|
+
| | :func:`pg.registered_types`, |
|
41
|
+
| | |
|
42
|
+
| | :func:`pg.utils.to_json`, |
|
43
|
+
| | |
|
44
|
+
| | :func:`pg.utils.from_json`, |
|
45
|
+
+---------------------+--------------------------------------------+
|
46
|
+
| Partial construction| :class:`pg.MaybePartial`, |
|
47
|
+
| | |
|
48
|
+
| | :const:`pg.MISSING_VALUE`. |
|
49
|
+
+---------------------+--------------------------------------------+
|
50
|
+
| Hierarchical key | :class:`pg.KeyPath` |
|
51
|
+
| representation | |
|
52
|
+
+---------------------+--------------------------------------------+
|
53
|
+
| Hierarchical object | :func:`pg.utils.traverse` |
|
54
|
+
| traversal | |
|
55
|
+
+---------------------+--------------------------------------------+
|
56
|
+
| Hierarchical object | :func:`pg.utils.transform`, |
|
57
|
+
| transformation | |
|
58
|
+
| | :func:`pg.utils.merge`, |
|
59
|
+
| | |
|
60
|
+
| | :func:`pg.utils.canonicalize`, |
|
61
|
+
| | |
|
62
|
+
| | :func:`pg.utils.flatten` |
|
63
|
+
+---------------------+--------------------------------------------+
|
64
|
+
| Docstr handling | :class:`pg.docstr`, |
|
65
|
+
+---------------------+--------------------------------------------+
|
66
|
+
| Error handling | :class:`pg.catch_errors`, |
|
67
|
+
+---------------------+--------------------------------------------+
|
68
|
+
"""
|
69
|
+
# pylint: enable=line-too-long
|
70
|
+
# pylint: disable=g-bad-import-order
|
71
|
+
# pylint: disable=g-importing-member
|
72
|
+
|
73
|
+
# Handling JSON conversion.
|
74
|
+
from pyglove.core.utils.json_conversion import Nestable
|
75
|
+
from pyglove.core.utils.json_conversion import JSONValueType
|
76
|
+
|
77
|
+
from pyglove.core.utils.json_conversion import JSONConvertible
|
78
|
+
from pyglove.core.utils.json_conversion import from_json
|
79
|
+
from pyglove.core.utils.json_conversion import to_json
|
80
|
+
from pyglove.core.utils.json_conversion import registered_types
|
81
|
+
|
82
|
+
# Handling formatting.
|
83
|
+
from pyglove.core.utils.formatting import Formattable
|
84
|
+
from pyglove.core.utils.formatting import format # pylint: disable=redefined-builtin
|
85
|
+
from pyglove.core.utils.formatting import printv as print # pylint: disable=redefined-builtin
|
86
|
+
from pyglove.core.utils.formatting import kvlist_str
|
87
|
+
from pyglove.core.utils.formatting import quote_if_str
|
88
|
+
from pyglove.core.utils.formatting import maybe_markdown_quote
|
89
|
+
from pyglove.core.utils.formatting import comma_delimited_str
|
90
|
+
from pyglove.core.utils.formatting import camel_to_snake
|
91
|
+
from pyglove.core.utils.formatting import auto_plural
|
92
|
+
from pyglove.core.utils.formatting import BracketType
|
93
|
+
from pyglove.core.utils.formatting import bracket_chars
|
94
|
+
from pyglove.core.utils.formatting import RawText
|
95
|
+
|
96
|
+
# Context managers for defining the default format for __str__ and __repr__.
|
97
|
+
from pyglove.core.utils.formatting import str_format
|
98
|
+
from pyglove.core.utils.formatting import repr_format
|
99
|
+
|
100
|
+
# Value location.
|
101
|
+
from pyglove.core.utils.value_location import KeyPath
|
102
|
+
from pyglove.core.utils.value_location import KeyPathSet
|
103
|
+
from pyglove.core.utils.value_location import StrKey
|
104
|
+
from pyglove.core.utils.value_location import message_on_path
|
105
|
+
|
106
|
+
# Value markers.
|
107
|
+
from pyglove.core.utils.missing import MissingValue
|
108
|
+
from pyglove.core.utils.missing import MISSING_VALUE
|
109
|
+
|
110
|
+
# Handling hierarchical.
|
111
|
+
from pyglove.core.utils.hierarchical import traverse
|
112
|
+
from pyglove.core.utils.hierarchical import transform
|
113
|
+
from pyglove.core.utils.hierarchical import flatten
|
114
|
+
from pyglove.core.utils.hierarchical import canonicalize
|
115
|
+
from pyglove.core.utils.hierarchical import merge
|
116
|
+
from pyglove.core.utils.hierarchical import merge_tree
|
117
|
+
from pyglove.core.utils.hierarchical import is_partial
|
118
|
+
from pyglove.core.utils.hierarchical import try_listify_dict_with_int_keys
|
119
|
+
|
120
|
+
# Common traits.
|
121
|
+
from pyglove.core.utils.common_traits import MaybePartial
|
122
|
+
from pyglove.core.utils.common_traits import Functor
|
123
|
+
|
124
|
+
from pyglove.core.utils.common_traits import explicit_method_override
|
125
|
+
from pyglove.core.utils.common_traits import ensure_explicit_method_override
|
126
|
+
|
127
|
+
# Handling thread local values.
|
128
|
+
from pyglove.core.utils.thread_local import thread_local_value_scope
|
129
|
+
from pyglove.core.utils.thread_local import thread_local_has
|
130
|
+
from pyglove.core.utils.thread_local import thread_local_set
|
131
|
+
from pyglove.core.utils.thread_local import thread_local_get
|
132
|
+
from pyglove.core.utils.thread_local import thread_local_del
|
133
|
+
from pyglove.core.utils.thread_local import thread_local_increment
|
134
|
+
from pyglove.core.utils.thread_local import thread_local_decrement
|
135
|
+
from pyglove.core.utils.thread_local import thread_local_push
|
136
|
+
from pyglove.core.utils.thread_local import thread_local_pop
|
137
|
+
from pyglove.core.utils.thread_local import thread_local_peek
|
138
|
+
|
139
|
+
# Handling docstrings.
|
140
|
+
from pyglove.core.utils.docstr_utils import DocStr
|
141
|
+
from pyglove.core.utils.docstr_utils import DocStrStyle
|
142
|
+
from pyglove.core.utils.docstr_utils import DocStrEntry
|
143
|
+
from pyglove.core.utils.docstr_utils import DocStrExample
|
144
|
+
from pyglove.core.utils.docstr_utils import DocStrArgument
|
145
|
+
from pyglove.core.utils.docstr_utils import DocStrReturns
|
146
|
+
from pyglove.core.utils.docstr_utils import DocStrRaises
|
147
|
+
from pyglove.core.utils.docstr_utils import docstr
|
148
|
+
|
149
|
+
# Handling exceptions.
|
150
|
+
from pyglove.core.utils.error_utils import catch_errors
|
151
|
+
from pyglove.core.utils.error_utils import CatchErrorsContext
|
152
|
+
from pyglove.core.utils.error_utils import ErrorInfo
|
153
|
+
|
154
|
+
# Timing.
|
155
|
+
from pyglove.core.utils.timing import timeit
|
156
|
+
from pyglove.core.utils.timing import TimeIt
|
157
|
+
|
158
|
+
# Value override from context manager.
|
159
|
+
from pyglove.core.utils.contextual import ContextualOverride
|
160
|
+
from pyglove.core.utils.contextual import contextual_override
|
161
|
+
from pyglove.core.utils.contextual import with_contextual_override
|
162
|
+
from pyglove.core.utils.contextual import get_contextual_override
|
163
|
+
from pyglove.core.utils.contextual import contextual_value
|
164
|
+
from pyglove.core.utils.contextual import all_contextual_values
|
165
|
+
|
166
|
+
# Text color.
|
167
|
+
from pyglove.core.utils.text_color import colored
|
168
|
+
from pyglove.core.utils.text_color import colored_block
|
169
|
+
from pyglove.core.utils.text_color import decolor
|
170
|
+
|
171
|
+
# pylint: enable=g-importing-member
|
172
|
+
# pylint: enable=g-bad-import-order
|
@@ -18,7 +18,7 @@ object, for example, partiality (MaybePartial), functor (Functor).
|
|
18
18
|
"""
|
19
19
|
|
20
20
|
import abc
|
21
|
-
from typing import Any, Dict, Optional
|
21
|
+
from typing import Any, Dict, Optional, Union
|
22
22
|
|
23
23
|
|
24
24
|
class MaybePartial(metaclass=abc.ABCMeta):
|
@@ -47,7 +47,7 @@ class MaybePartial(metaclass=abc.ABCMeta):
|
|
47
47
|
return len(self.missing_values()) > 0 # pylint: disable=g-explicit-length-test
|
48
48
|
|
49
49
|
@abc.abstractmethod
|
50
|
-
def missing_values(self, flatten: bool = True) -> Dict[str, Any]: # pylint: disable=redefined-outer-name
|
50
|
+
def missing_values(self, flatten: bool = True) -> Dict[Union[str, int], Any]: # pylint: disable=redefined-outer-name
|
51
51
|
"""Returns missing values from this object.
|
52
52
|
|
53
53
|
Args:
|
@@ -11,10 +11,8 @@
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
|
-
"""Tests for pyglove.object_utils.common_traits."""
|
15
|
-
|
16
14
|
import unittest
|
17
|
-
from pyglove.core.
|
15
|
+
from pyglove.core.utils import common_traits
|
18
16
|
|
19
17
|
|
20
18
|
class ExplicitlyOverrideTest(unittest.TestCase):
|