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
@@ -11,33 +11,35 @@
|
|
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.core.typing.value_specs."""
|
15
|
-
|
16
14
|
import contextlib
|
15
|
+
import datetime
|
16
|
+
import inspect
|
17
17
|
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
|
25
25
|
from pyglove.core.typing import custom_typing
|
26
|
-
from pyglove.core.typing import inspect as pg_inspect
|
27
|
-
from pyglove.core.typing import typed_missing
|
28
26
|
from pyglove.core.typing import key_specs as ks
|
27
|
+
from pyglove.core.typing import typed_missing
|
29
28
|
from pyglove.core.typing import value_specs as vs
|
30
29
|
|
31
30
|
|
31
|
+
Argument = callable_signature.Argument
|
32
|
+
Signature = callable_signature.Signature
|
33
|
+
|
34
|
+
|
32
35
|
class ValueSpecTest(unittest.TestCase):
|
33
36
|
"""Base class for value spec test."""
|
34
37
|
|
35
38
|
def assert_json_conversion(self, v):
|
36
|
-
self.assertEqual(
|
39
|
+
self.assertEqual(utils.from_json(v.to_json()), v)
|
37
40
|
|
38
41
|
def assert_json_conversion_key(self, v, key):
|
39
|
-
self.assertEqual(
|
40
|
-
v.to_json()[object_utils.JSONConvertible.TYPE_NAME_KEY], key)
|
42
|
+
self.assertEqual(v.to_json()[utils.JSONConvertible.TYPE_NAME_KEY], key)
|
41
43
|
|
42
44
|
|
43
45
|
class BoolTest(ValueSpecTest):
|
@@ -60,15 +62,15 @@ class BoolTest(ValueSpecTest):
|
|
60
62
|
self.assertFalse(vs.Bool().is_noneable)
|
61
63
|
self.assertTrue(vs.Bool().noneable().is_noneable)
|
62
64
|
|
63
|
-
def
|
64
|
-
self.assertEqual(
|
65
|
-
self.assertEqual(
|
66
|
-
self.assertEqual(
|
65
|
+
def test_repr(self):
|
66
|
+
self.assertEqual(repr(vs.Bool()), 'Bool()')
|
67
|
+
self.assertEqual(repr(vs.Bool(True)), 'Bool(default=True)')
|
68
|
+
self.assertEqual(repr(vs.Bool(True).freeze()),
|
67
69
|
'Bool(default=True, frozen=True)')
|
68
70
|
self.assertEqual(
|
69
|
-
|
71
|
+
repr(vs.Bool().noneable()), 'Bool(default=None, noneable=True)')
|
70
72
|
self.assertEqual(
|
71
|
-
|
73
|
+
repr(vs.Bool(True).noneable()), 'Bool(default=True, noneable=True)')
|
72
74
|
|
73
75
|
def test_annotation(self):
|
74
76
|
self.assertEqual(vs.Bool().annotation, bool)
|
@@ -96,6 +98,13 @@ class BoolTest(ValueSpecTest):
|
|
96
98
|
with self.assertRaisesRegex(ValueError, 'Value cannot be None'):
|
97
99
|
vs.Bool().apply(None)
|
98
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
|
+
|
99
108
|
def test_is_compatible(self):
|
100
109
|
v = vs.Bool()
|
101
110
|
self.assertTrue(v.is_compatible(v))
|
@@ -114,6 +123,12 @@ class BoolTest(ValueSpecTest):
|
|
114
123
|
# Child may extend a noneable base into non-noneable.
|
115
124
|
self.assertFalse(vs.Bool().extend(vs.Bool().noneable()).is_noneable)
|
116
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
|
+
|
117
132
|
# Child cannot extend a base with different type.
|
118
133
|
with self.assertRaisesRegex(
|
119
134
|
TypeError, '.* cannot extend .*: incompatible type.'):
|
@@ -124,6 +139,11 @@ class BoolTest(ValueSpecTest):
|
|
124
139
|
TypeError, '.* cannot extend .*: None is not allowed in base spec.'):
|
125
140
|
vs.Bool().noneable().extend(vs.Bool())
|
126
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
|
+
|
127
147
|
def test_freeze(self):
|
128
148
|
self.assertFalse(vs.Bool().frozen)
|
129
149
|
|
@@ -141,7 +161,7 @@ class BoolTest(ValueSpecTest):
|
|
141
161
|
self.assertTrue(v.default)
|
142
162
|
|
143
163
|
with self.assertRaisesRegex(
|
144
|
-
TypeError, '
|
164
|
+
TypeError, '.* cannot extend a frozen value spec'):
|
145
165
|
vs.Bool().extend(v)
|
146
166
|
|
147
167
|
with self.assertRaisesRegex(
|
@@ -193,14 +213,14 @@ class StrTest(ValueSpecTest):
|
|
193
213
|
self.assertFalse(vs.Str().is_noneable)
|
194
214
|
self.assertTrue(vs.Str().noneable().is_noneable)
|
195
215
|
|
196
|
-
def
|
197
|
-
self.assertEqual(
|
216
|
+
def test_repr(self):
|
217
|
+
self.assertEqual(repr(vs.Str()), 'Str()')
|
198
218
|
self.assertEqual(
|
199
|
-
|
200
|
-
self.assertEqual(
|
201
|
-
self.assertEqual(
|
219
|
+
repr(vs.Str().noneable()), 'Str(default=None, noneable=True)')
|
220
|
+
self.assertEqual(repr(vs.Str('a')), 'Str(default=\'a\')')
|
221
|
+
self.assertEqual(repr(vs.Str('a').freeze()),
|
202
222
|
'Str(default=\'a\', frozen=True)')
|
203
|
-
self.assertEqual(
|
223
|
+
self.assertEqual(repr(vs.Str(regex='.*')), 'Str(regex=\'.*\')')
|
204
224
|
|
205
225
|
def test_annotation(self):
|
206
226
|
self.assertEqual(vs.Str().annotation, str)
|
@@ -221,6 +241,12 @@ class StrTest(ValueSpecTest):
|
|
221
241
|
self.assertNotEqual(vs.Str(), vs.Str(regex='.*'))
|
222
242
|
self.assertNotEqual(vs.Str(regex='a'), vs.Str(regex='.*'))
|
223
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
|
+
|
224
250
|
def test_apply(self):
|
225
251
|
self.assertEqual(vs.Str().apply('a'), 'a')
|
226
252
|
self.assertEqual(vs.Str(regex='a.*').apply('a1'), 'a1')
|
@@ -263,6 +289,12 @@ class StrTest(ValueSpecTest):
|
|
263
289
|
# Child may extend a noneable base into non-noneable.
|
264
290
|
self.assertFalse(vs.Str().extend(vs.Str().noneable()).is_noneable)
|
265
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
|
+
|
266
298
|
# Child cannot extend a base of different type.
|
267
299
|
with self.assertRaisesRegex(
|
268
300
|
TypeError, '.* cannot extend .*: incompatible type.'):
|
@@ -273,6 +305,11 @@ class StrTest(ValueSpecTest):
|
|
273
305
|
TypeError, '.* cannot extend .*: None is not allowed in base spec.'):
|
274
306
|
vs.Str().noneable().extend(vs.Str())
|
275
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
|
+
|
276
313
|
def test_freeze(self):
|
277
314
|
self.assertFalse(vs.Str().frozen)
|
278
315
|
|
@@ -290,7 +327,7 @@ class StrTest(ValueSpecTest):
|
|
290
327
|
self.assertEqual(v.default, 'foo')
|
291
328
|
|
292
329
|
with self.assertRaisesRegex(
|
293
|
-
TypeError, '
|
330
|
+
TypeError, '.* cannot extend a frozen value spec'):
|
294
331
|
vs.Str().extend(v)
|
295
332
|
|
296
333
|
with self.assertRaisesRegex(
|
@@ -304,6 +341,7 @@ class StrTest(ValueSpecTest):
|
|
304
341
|
self.assert_json_conversion(vs.Str().noneable().freeze('abc'))
|
305
342
|
self.assert_json_conversion_key(vs.Str(), 'pyglove.typing.Str')
|
306
343
|
|
344
|
+
|
307
345
|
class IntTest(ValueSpecTest):
|
308
346
|
"""Tests for `Int`."""
|
309
347
|
|
@@ -334,17 +372,17 @@ class IntTest(ValueSpecTest):
|
|
334
372
|
self.assertFalse(vs.Int().is_noneable)
|
335
373
|
self.assertTrue(vs.Int().noneable().is_noneable)
|
336
374
|
|
337
|
-
def
|
338
|
-
self.assertEqual(
|
339
|
-
self.assertEqual(
|
340
|
-
self.assertEqual(
|
375
|
+
def test_repr(self):
|
376
|
+
self.assertEqual(repr(vs.Int()), 'Int()')
|
377
|
+
self.assertEqual(repr(vs.Int(1)), 'Int(default=1)')
|
378
|
+
self.assertEqual(repr(vs.Int(1).freeze()),
|
341
379
|
'Int(default=1, frozen=True)')
|
342
380
|
self.assertEqual(
|
343
|
-
|
381
|
+
repr(vs.Int().noneable()), 'Int(default=None, noneable=True)')
|
344
382
|
self.assertEqual(
|
345
|
-
|
383
|
+
repr(vs.Int(1).noneable()), 'Int(default=1, noneable=True)')
|
346
384
|
self.assertEqual(
|
347
|
-
|
385
|
+
repr(vs.Int(min_value=0, max_value=1)), 'Int(min=0, max=1)')
|
348
386
|
|
349
387
|
def test_annotation(self):
|
350
388
|
self.assertEqual(vs.Int().annotation, int)
|
@@ -374,6 +412,16 @@ class IntTest(ValueSpecTest):
|
|
374
412
|
ValueError, '"max_value" must be equal or greater than "min_value".'):
|
375
413
|
vs.Int(min_value=1, max_value=0)
|
376
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
|
+
|
377
425
|
def test_apply(self):
|
378
426
|
self.assertEqual(vs.Int().apply(1), 1)
|
379
427
|
self.assertEqual(vs.Int(min_value=1, max_value=1).apply(1), 1)
|
@@ -431,6 +479,12 @@ class IntTest(ValueSpecTest):
|
|
431
479
|
vs.Int(min_value=1),
|
432
480
|
)
|
433
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
|
+
|
434
488
|
with self.assertRaisesRegex(TypeError,
|
435
489
|
'.* cannot extend .*: incompatible type.'):
|
436
490
|
vs.Int().extend(vs.Bool())
|
@@ -463,6 +517,11 @@ class IntTest(ValueSpecTest):
|
|
463
517
|
TypeError, '.* cannot extend .*: no compatible type found in Union.'):
|
464
518
|
vs.Int().extend(vs.Union([vs.Bool(), vs.Str()]))
|
465
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
|
+
|
466
525
|
def test_freeze(self):
|
467
526
|
self.assertFalse(vs.Int().frozen)
|
468
527
|
|
@@ -480,7 +539,7 @@ class IntTest(ValueSpecTest):
|
|
480
539
|
self.assertEqual(v.default, 1)
|
481
540
|
|
482
541
|
with self.assertRaisesRegex(
|
483
|
-
TypeError, '
|
542
|
+
TypeError, '.* cannot extend a frozen value spec'):
|
484
543
|
vs.Int().extend(v)
|
485
544
|
|
486
545
|
with self.assertRaisesRegex(
|
@@ -526,16 +585,16 @@ class FloatTest(ValueSpecTest):
|
|
526
585
|
self.assertFalse(vs.Float().is_noneable)
|
527
586
|
self.assertTrue(vs.Float().noneable().is_noneable)
|
528
587
|
|
529
|
-
def
|
588
|
+
def test_repr(self):
|
530
589
|
self.assertEqual(str(vs.Float()), 'Float()')
|
531
590
|
self.assertEqual(
|
532
|
-
|
591
|
+
repr(vs.Float().noneable()), 'Float(default=None, noneable=True)')
|
533
592
|
self.assertEqual(
|
534
|
-
|
593
|
+
repr(vs.Float(1.0).freeze()), 'Float(default=1.0, frozen=True)')
|
535
594
|
self.assertEqual(
|
536
|
-
|
595
|
+
repr(vs.Float(1.0).noneable()), 'Float(default=1.0, noneable=True)')
|
537
596
|
self.assertEqual(
|
538
|
-
|
597
|
+
repr(vs.Float(default=1., min_value=0., max_value=1.).noneable()),
|
539
598
|
'Float(default=1.0, min=0.0, max=1.0, noneable=True)')
|
540
599
|
|
541
600
|
def test_annotation(self):
|
@@ -568,6 +627,16 @@ class FloatTest(ValueSpecTest):
|
|
568
627
|
ValueError, '"max_value" must be equal or greater than "min_value".'):
|
569
628
|
vs.Float(min_value=1., max_value=0.)
|
570
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
|
+
|
571
640
|
def test_apply(self):
|
572
641
|
self.assertEqual(vs.Float().apply(1.), 1.)
|
573
642
|
self.assertEqual(vs.Float().apply(1), 1.)
|
@@ -614,6 +683,12 @@ class FloatTest(ValueSpecTest):
|
|
614
683
|
# Child may extend a noneable base into non-noneable.
|
615
684
|
self.assertFalse(vs.Float().extend(vs.Float().noneable()).is_noneable)
|
616
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
|
+
|
617
692
|
with self.assertRaisesRegex(
|
618
693
|
TypeError, '.* cannot extend .*: incompatible type.'):
|
619
694
|
vs.Float().extend(vs.Int())
|
@@ -642,6 +717,11 @@ class FloatTest(ValueSpecTest):
|
|
642
717
|
'min_value .* is greater than max_value .* after extension'):
|
643
718
|
vs.Float(min_value=1.).extend(vs.Float(max_value=0.))
|
644
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
|
+
|
645
725
|
def test_freeze(self):
|
646
726
|
self.assertFalse(vs.Float().frozen)
|
647
727
|
|
@@ -659,7 +739,7 @@ class FloatTest(ValueSpecTest):
|
|
659
739
|
self.assertEqual(v.default, 1.0)
|
660
740
|
|
661
741
|
with self.assertRaisesRegex(
|
662
|
-
TypeError, '
|
742
|
+
TypeError, '.* cannot extend a frozen value spec'):
|
663
743
|
vs.Float().extend(v)
|
664
744
|
|
665
745
|
with self.assertRaisesRegex(
|
@@ -730,13 +810,13 @@ class EnumTest(ValueSpecTest):
|
|
730
810
|
vs.Enum('a', ['a', 'b']).noneable(),
|
731
811
|
vs.Enum('a', ['a', 'b', None]))
|
732
812
|
|
733
|
-
def
|
813
|
+
def test_repr(self):
|
734
814
|
self.assertEqual(
|
735
|
-
|
815
|
+
repr(vs.Enum('a', ['a', 'b', 'c'])),
|
736
816
|
'Enum(default=\'a\', values=[\'a\', \'b\', \'c\'])')
|
737
817
|
|
738
818
|
self.assertEqual(
|
739
|
-
|
819
|
+
repr(vs.Enum('a', ['a', 'b', 'c']).freeze()),
|
740
820
|
'Enum(default=\'a\', values=[\'a\', \'b\', \'c\'], frozen=True)')
|
741
821
|
|
742
822
|
def test_annotation(self):
|
@@ -765,6 +845,11 @@ class EnumTest(ValueSpecTest):
|
|
765
845
|
ValueError, 'Enum default value \'a\' is not in candidate list.'):
|
766
846
|
vs.Enum('a', ['b'])
|
767
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
|
+
|
768
853
|
def test_apply(self):
|
769
854
|
self.assertEqual(vs.Enum('a', ['a']).apply('a'), 'a')
|
770
855
|
self.assertIsNone(vs.Enum('a', ['a', None]).apply(None))
|
@@ -781,6 +866,8 @@ class EnumTest(ValueSpecTest):
|
|
781
866
|
self.assertTrue(
|
782
867
|
vs.Enum(0, [0, 1]).is_compatible(vs.Enum(0, [0, 1])))
|
783
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')))
|
784
871
|
self.assertFalse(vs.Enum(0, [0]).is_compatible(vs.Enum(0, [0, 1])))
|
785
872
|
self.assertFalse(vs.Enum(0, [0]).is_compatible(vs.Int()))
|
786
873
|
|
@@ -791,7 +878,7 @@ class EnumTest(ValueSpecTest):
|
|
791
878
|
|
792
879
|
# Child cannot extend a non-noneable base to noneable.
|
793
880
|
with self.assertRaisesRegex(
|
794
|
-
TypeError, '.* cannot extend .*:
|
881
|
+
TypeError, '.* cannot extend .*: \'b\' is not an acceptable value.'):
|
795
882
|
vs.Enum('a', ['a', 'b']).extend(vs.Enum('a', ['a']))
|
796
883
|
|
797
884
|
def test_freeze(self):
|
@@ -811,7 +898,7 @@ class EnumTest(ValueSpecTest):
|
|
811
898
|
self.assertEqual(v.default, 'a')
|
812
899
|
|
813
900
|
with self.assertRaisesRegex(
|
814
|
-
TypeError, '
|
901
|
+
TypeError, '.* cannot extend a frozen value spec'):
|
815
902
|
vs.Enum('c', ['a', 'b', 'c']).extend(v)
|
816
903
|
|
817
904
|
def test_json_conversion(self):
|
@@ -939,15 +1026,21 @@ class ListTest(ValueSpecTest):
|
|
939
1026
|
'Either "size" or "min_size"/"max_size" pair can be specified.'):
|
940
1027
|
vs.List(vs.Int(), size=5, min_size=1)
|
941
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
|
+
|
942
1035
|
def test_apply(self):
|
943
1036
|
self.assertEqual(vs.List(vs.Int()).apply([]), [])
|
944
1037
|
self.assertEqual(vs.List(vs.Int()).apply([1]), [1])
|
945
1038
|
self.assertEqual(vs.List(vs.Int().noneable()).apply([1, None]), [1, None])
|
946
1039
|
# Automatic conversion: str -> KeyPath is a registered conversion.
|
947
1040
|
# See 'type_conversion.py'.
|
948
|
-
l = vs.List(vs.Object(
|
949
|
-
self.assertIsInstance(l[0],
|
950
|
-
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')])
|
951
1044
|
self.assertEqual(
|
952
1045
|
vs.List(vs.Int()).apply(
|
953
1046
|
typed_missing.MISSING_VALUE, allow_partial=True),
|
@@ -1104,7 +1197,7 @@ class ListTest(ValueSpecTest):
|
|
1104
1197
|
self.assertEqual(v.default, [1])
|
1105
1198
|
|
1106
1199
|
with self.assertRaisesRegex(
|
1107
|
-
TypeError, '
|
1200
|
+
TypeError, '.* cannot extend a frozen value spec'):
|
1108
1201
|
vs.List(vs.Int()).extend(v)
|
1109
1202
|
|
1110
1203
|
with self.assertRaisesRegex(
|
@@ -1298,6 +1391,12 @@ class TupleTest(ValueSpecTest):
|
|
1298
1391
|
'<(type|class) \'int\'>.'):
|
1299
1392
|
vs.Tuple([vs.Int()], default=1)
|
1300
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
|
+
|
1301
1400
|
def test_apply(self):
|
1302
1401
|
self.assertEqual(vs.Tuple(vs.Int()).apply(tuple()), tuple())
|
1303
1402
|
self.assertEqual(vs.Tuple(vs.Int()).apply((1, 1, 1)), (1, 1, 1))
|
@@ -1530,7 +1629,7 @@ class TupleTest(ValueSpecTest):
|
|
1530
1629
|
self.assertEqual(v.default, (1,))
|
1531
1630
|
|
1532
1631
|
with self.assertRaisesRegex(
|
1533
|
-
TypeError, '
|
1632
|
+
TypeError, '.* cannot extend a frozen value spec'):
|
1534
1633
|
vs.Tuple(vs.Int()).extend(v)
|
1535
1634
|
|
1536
1635
|
with self.assertRaisesRegex(
|
@@ -1574,6 +1673,9 @@ class DictTest(ValueSpecTest):
|
|
1574
1673
|
('z', vs.Int(min_value=0, max_value=None), 'field z.', dict(foo=1))
|
1575
1674
|
]))
|
1576
1675
|
|
1676
|
+
self.assertEqual(vs.Dict(vs.Int()), vs.Dict([(ks.StrKey(), vs.Int())]))
|
1677
|
+
self.assertEqual(vs.Dict(int), vs.Dict([(ks.StrKey(), vs.Int())]))
|
1678
|
+
|
1577
1679
|
with self.assertRaisesRegex(
|
1578
1680
|
TypeError,
|
1579
1681
|
'`pg.typing.Dict` accepts 1 dict type argument as the schema'):
|
@@ -1657,7 +1759,12 @@ class DictTest(ValueSpecTest):
|
|
1657
1759
|
('b', 1, 'field 1'),
|
1658
1760
|
('a', vs.Str(), 'field 2'),
|
1659
1761
|
]).noneable()),
|
1660
|
-
|
1762
|
+
(
|
1763
|
+
'Dict(fields=[Field(key=b, value=Int(default=1), '
|
1764
|
+
'description=\'field 1\'), Field(key=a, value=Str(), '
|
1765
|
+
'description=\'field 2\')], noneable=True)'
|
1766
|
+
)
|
1767
|
+
)
|
1661
1768
|
|
1662
1769
|
self.assertEqual(
|
1663
1770
|
repr(
|
@@ -1665,7 +1772,42 @@ class DictTest(ValueSpecTest):
|
|
1665
1772
|
('b', 1, 'field 1'),
|
1666
1773
|
('a', vs.Str('abc'), 'field 2'),
|
1667
1774
|
]).freeze()),
|
1668
|
-
|
1775
|
+
(
|
1776
|
+
'Dict(fields=[Field(key=b, value=Int(default=1), '
|
1777
|
+
'description=\'field 1\'), Field(key=a, '
|
1778
|
+
'value=Str(default=\'abc\'), description=\'field 2\')], '
|
1779
|
+
'frozen=True)'
|
1780
|
+
)
|
1781
|
+
)
|
1782
|
+
|
1783
|
+
def test_str(self):
|
1784
|
+
self.assertEqual(str(vs.Dict()), 'Dict()')
|
1785
|
+
self.assertEqual(
|
1786
|
+
str(
|
1787
|
+
vs.Dict([
|
1788
|
+
('b', 1, 'field 1'),
|
1789
|
+
('a', vs.Str(), 'field 2'),
|
1790
|
+
]).noneable()),
|
1791
|
+
inspect.cleandoc('''
|
1792
|
+
Dict(
|
1793
|
+
fields=[
|
1794
|
+
Field(
|
1795
|
+
key=b,
|
1796
|
+
value=Int(
|
1797
|
+
default=1
|
1798
|
+
),
|
1799
|
+
description='field 1'
|
1800
|
+
),
|
1801
|
+
Field(
|
1802
|
+
key=a,
|
1803
|
+
value=Str(),
|
1804
|
+
description='field 2'
|
1805
|
+
)
|
1806
|
+
],
|
1807
|
+
noneable=True
|
1808
|
+
)
|
1809
|
+
''')
|
1810
|
+
)
|
1669
1811
|
|
1670
1812
|
def test_annotation(self):
|
1671
1813
|
self.assertEqual(vs.Dict().annotation, typing.Dict[str, typing.Any])
|
@@ -1695,11 +1837,6 @@ class DictTest(ValueSpecTest):
|
|
1695
1837
|
vs.Dict(class_schema.create_schema([('a', vs.Int())])),
|
1696
1838
|
vs.Dict([('a', vs.Int())]))
|
1697
1839
|
|
1698
|
-
with self.assertRaisesRegex(
|
1699
|
-
TypeError,
|
1700
|
-
'Schema definition should be a dict .* a list .*'):
|
1701
|
-
vs.Dict(int)
|
1702
|
-
|
1703
1840
|
with self.assertRaisesRegex(
|
1704
1841
|
TypeError, 'The 1st element of field definition should be of '
|
1705
1842
|
'<(type|class) \'str\'>'):
|
@@ -1715,6 +1852,12 @@ class DictTest(ValueSpecTest):
|
|
1715
1852
|
'should be a dict of objects.'):
|
1716
1853
|
vs.Dict([('key', 1, 'field 1', 123)])
|
1717
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
|
+
|
1718
1861
|
def test_apply(self):
|
1719
1862
|
self.assertEqual(vs.Dict().apply({'a': 1}), {'a': 1})
|
1720
1863
|
self.assertEqual(
|
@@ -1902,7 +2045,7 @@ class DictTest(ValueSpecTest):
|
|
1902
2045
|
x = vs.Dict([
|
1903
2046
|
('a', int, 'field 1', dict(x=1)),
|
1904
2047
|
]).freeze(dict(a=1))
|
1905
|
-
y =
|
2048
|
+
y = utils.from_json(x.to_json())
|
1906
2049
|
self.assert_json_conversion(
|
1907
2050
|
vs.Dict([
|
1908
2051
|
('a', int, 'field 1', dict(x=1)),
|
@@ -1945,7 +2088,7 @@ class ObjectTest(ValueSpecTest):
|
|
1945
2088
|
class C(A):
|
1946
2089
|
pass
|
1947
2090
|
|
1948
|
-
class D(C,
|
2091
|
+
class D(C, utils.MaybePartial):
|
1949
2092
|
|
1950
2093
|
def missing_values(self):
|
1951
2094
|
return {'SOME_KEY': 'SOME_VALUE'}
|
@@ -2001,6 +2144,8 @@ class ObjectTest(ValueSpecTest):
|
|
2001
2144
|
def test_forward_refs(self):
|
2002
2145
|
self.assertEqual(vs.Object(self.A).forward_refs, set())
|
2003
2146
|
self.assertEqual(vs.Object('Foo').forward_refs, set([forward_ref('Foo')]))
|
2147
|
+
self.assertEqual(
|
2148
|
+
vs.Object(forward_ref('Foo')).forward_refs, set([forward_ref('Foo')]))
|
2004
2149
|
|
2005
2150
|
def test_default(self):
|
2006
2151
|
self.assertEqual(vs.Object(self.A).default, typed_missing.MISSING_VALUE)
|
@@ -2012,14 +2157,14 @@ class ObjectTest(ValueSpecTest):
|
|
2012
2157
|
self.assertTrue(vs.Object(self.A).noneable().is_noneable)
|
2013
2158
|
self.assertTrue(vs.Object('Foo').noneable().is_noneable)
|
2014
2159
|
|
2015
|
-
def
|
2016
|
-
self.assertEqual(
|
2017
|
-
self.assertEqual(
|
2160
|
+
def test_repr(self):
|
2161
|
+
self.assertEqual(repr(vs.Object(self.A)), 'Object(A)')
|
2162
|
+
self.assertEqual(repr(vs.Object('Foo')), 'Object(Foo)')
|
2018
2163
|
self.assertEqual(
|
2019
|
-
|
2164
|
+
repr(vs.Object(self.A).noneable()),
|
2020
2165
|
'Object(A, default=None, noneable=True)')
|
2021
2166
|
self.assertEqual(
|
2022
|
-
|
2167
|
+
repr(vs.Object(self.A).noneable().freeze()),
|
2023
2168
|
'Object(A, default=None, noneable=True, frozen=True)')
|
2024
2169
|
|
2025
2170
|
def test_annotation(self):
|
@@ -2065,6 +2210,10 @@ class ObjectTest(ValueSpecTest):
|
|
2065
2210
|
TypeError, '<(type|class) \'object\'> is too general for Object spec.'):
|
2066
2211
|
vs.Object(object)
|
2067
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
|
+
|
2068
2217
|
def test_apply(self):
|
2069
2218
|
a = self.A()
|
2070
2219
|
self.assertEqual(vs.Object(self.A).apply(a), a)
|
@@ -2187,7 +2336,7 @@ class ObjectTest(ValueSpecTest):
|
|
2187
2336
|
self.assertIs(v.default, b)
|
2188
2337
|
|
2189
2338
|
with self.assertRaisesRegex(
|
2190
|
-
TypeError, '
|
2339
|
+
TypeError, '.* cannot extend a frozen value spec'):
|
2191
2340
|
vs.Object(self.A).extend(v)
|
2192
2341
|
|
2193
2342
|
with self.assertRaisesRegex(
|
@@ -2228,7 +2377,7 @@ class CallableTest(ValueSpecTest):
|
|
2228
2377
|
|
2229
2378
|
def test_value_type(self):
|
2230
2379
|
self.assertIsNone(vs.Callable().value_type)
|
2231
|
-
self.assertEqual(vs.Functor().annotation,
|
2380
|
+
self.assertEqual(vs.Functor().annotation, utils.Functor)
|
2232
2381
|
|
2233
2382
|
def test_forward_refs(self):
|
2234
2383
|
self.assertEqual(vs.Callable().forward_refs, set())
|
@@ -2268,10 +2417,10 @@ class CallableTest(ValueSpecTest):
|
|
2268
2417
|
self.assertFalse(vs.Callable().is_noneable)
|
2269
2418
|
self.assertTrue(vs.Callable().noneable().is_noneable)
|
2270
2419
|
|
2271
|
-
def
|
2272
|
-
self.assertEqual(
|
2420
|
+
def test_repr(self):
|
2421
|
+
self.assertEqual(repr(vs.Callable()), 'Callable()')
|
2273
2422
|
self.assertEqual(
|
2274
|
-
|
2423
|
+
repr(
|
2275
2424
|
vs.Callable(
|
2276
2425
|
args=[vs.Int(), vs.Int()],
|
2277
2426
|
kw=[('a', vs.Str().noneable())],
|
@@ -2279,7 +2428,7 @@ class CallableTest(ValueSpecTest):
|
|
2279
2428
|
'Callable(args=[Int(), Int()], kw=[(\'a\', '
|
2280
2429
|
'Str(default=None, noneable=True))], returns=Int())')
|
2281
2430
|
self.assertEqual(
|
2282
|
-
|
2431
|
+
repr(
|
2283
2432
|
vs.Callable(
|
2284
2433
|
args=[vs.Int(), vs.Int()],
|
2285
2434
|
kw=[('a', vs.Str().noneable())],
|
@@ -2384,6 +2533,10 @@ class CallableTest(ValueSpecTest):
|
|
2384
2533
|
TypeError, '.* only take 0 positional arguments, while 1 is required'):
|
2385
2534
|
vs.Callable([vs.Int()]).apply(f)
|
2386
2535
|
|
2536
|
+
def test_instantiation(self):
|
2537
|
+
with self.assertRaisesRegex(TypeError, '.* cannot be instantiated'):
|
2538
|
+
vs.Callable()()
|
2539
|
+
|
2387
2540
|
def test_apply_on_callable_object(self):
|
2388
2541
|
|
2389
2542
|
class CallableObject:
|
@@ -2430,15 +2583,15 @@ class CallableTest(ValueSpecTest):
|
|
2430
2583
|
|
2431
2584
|
def test_apply_on_functor(self):
|
2432
2585
|
|
2433
|
-
class FunctorWithRegularArgs(
|
2586
|
+
class FunctorWithRegularArgs(utils.Functor):
|
2434
2587
|
|
2435
|
-
__signature__ =
|
2588
|
+
__signature__ = Signature(
|
2436
2589
|
callable_type=callable_signature.CallableType.FUNCTION,
|
2437
2590
|
name='foo',
|
2438
2591
|
module_name='__main__',
|
2439
2592
|
args=[
|
2440
|
-
|
2441
|
-
|
2593
|
+
Argument('a', Argument.Kind.POSITIONAL_OR_KEYWORD, vs.Int()),
|
2594
|
+
Argument('b', Argument.Kind.POSITIONAL_OR_KEYWORD, vs.Str()),
|
2442
2595
|
])
|
2443
2596
|
|
2444
2597
|
def __init__(self, value):
|
@@ -2476,18 +2629,28 @@ class CallableTest(ValueSpecTest):
|
|
2476
2629
|
|
2477
2630
|
def test_apply_on_functor_with_varargs(self):
|
2478
2631
|
|
2479
|
-
class FunctorWithVarArgs(
|
2632
|
+
class FunctorWithVarArgs(utils.Functor):
|
2480
2633
|
|
2481
|
-
__signature__ =
|
2634
|
+
__signature__ = Signature(
|
2482
2635
|
callable_type=callable_signature.CallableType.FUNCTION,
|
2483
2636
|
name='foo',
|
2484
2637
|
module_name='__main__',
|
2485
2638
|
args=[
|
2486
|
-
|
2487
|
-
|
2639
|
+
Argument(
|
2640
|
+
'a', Argument.Kind.POSITIONAL_OR_KEYWORD, vs.Int()
|
2641
|
+
),
|
2642
|
+
Argument(
|
2643
|
+
'b', Argument.Kind.POSITIONAL_OR_KEYWORD, vs.Str()
|
2644
|
+
)
|
2488
2645
|
],
|
2489
|
-
varargs=
|
2490
|
-
|
2646
|
+
varargs=Argument(
|
2647
|
+
'args', Argument.Kind.VAR_POSITIONAL, vs.List(vs.Int())
|
2648
|
+
),
|
2649
|
+
varkw=Argument(
|
2650
|
+
'kwargs',
|
2651
|
+
Argument.Kind.VAR_KEYWORD,
|
2652
|
+
vs.Dict([(ks.StrKey(), vs.Int())])
|
2653
|
+
),
|
2491
2654
|
return_value=vs.Object(ValueError))
|
2492
2655
|
|
2493
2656
|
def __init__(self, value):
|
@@ -2599,7 +2762,7 @@ class CallableTest(ValueSpecTest):
|
|
2599
2762
|
self.assertIs(v.default, f)
|
2600
2763
|
|
2601
2764
|
with self.assertRaisesRegex(
|
2602
|
-
TypeError, '
|
2765
|
+
TypeError, '.* cannot extend a frozen value spec'):
|
2603
2766
|
vs.Callable().extend(v)
|
2604
2767
|
|
2605
2768
|
with self.assertRaisesRegex(
|
@@ -2617,7 +2780,7 @@ class CallableTest(ValueSpecTest):
|
|
2617
2780
|
)
|
2618
2781
|
)
|
2619
2782
|
x = vs.Callable([vs.Int()], default=lambda x: x + 1).noneable()
|
2620
|
-
y =
|
2783
|
+
y = utils.from_json(x.to_json())
|
2621
2784
|
self.assert_json_conversion(
|
2622
2785
|
vs.Callable([vs.Int()], default=lambda x: x + 1).noneable()
|
2623
2786
|
)
|
@@ -2699,14 +2862,16 @@ class TypeTest(ValueSpecTest):
|
|
2699
2862
|
self.assertFalse(vs.Type(Exception).is_noneable)
|
2700
2863
|
self.assertTrue(vs.Type(Exception).noneable().is_noneable)
|
2701
2864
|
|
2702
|
-
def
|
2703
|
-
self.assertEqual(
|
2865
|
+
def test_repr(self):
|
2866
|
+
self.assertEqual(repr(vs.Type(Exception)), 'Type(<class \'Exception\'>)')
|
2704
2867
|
self.assertEqual(
|
2705
|
-
|
2706
|
-
'Type(<class \'Exception\'>, default=None, noneable=True)'
|
2868
|
+
repr(vs.Type(Exception).noneable()),
|
2869
|
+
'Type(<class \'Exception\'>, default=None, noneable=True)'
|
2870
|
+
)
|
2707
2871
|
self.assertEqual(
|
2708
|
-
|
2709
|
-
'Type(<class \'Exception\'>, default=None, noneable=True, frozen=True)'
|
2872
|
+
repr(vs.Type(Exception).noneable().freeze()),
|
2873
|
+
'Type(<class \'Exception\'>, default=None, noneable=True, frozen=True)'
|
2874
|
+
)
|
2710
2875
|
|
2711
2876
|
def test_annotation(self):
|
2712
2877
|
self.assertEqual(vs.Type(Exception).annotation, typing.Type[Exception])
|
@@ -2742,6 +2907,9 @@ class TypeTest(ValueSpecTest):
|
|
2742
2907
|
self.assertNotEqual(
|
2743
2908
|
vs.Type(Exception), vs.Type(Exception, default=ValueError))
|
2744
2909
|
|
2910
|
+
def test_instantiate(self):
|
2911
|
+
self.assertIs(vs.Type[str](), str)
|
2912
|
+
|
2745
2913
|
def test_apply(self):
|
2746
2914
|
self.assertEqual(vs.Type(Exception).apply(Exception), Exception)
|
2747
2915
|
self.assertEqual(vs.Type(Exception).apply(ValueError), ValueError)
|
@@ -2849,7 +3017,7 @@ class TypeTest(ValueSpecTest):
|
|
2849
3017
|
self.assertIs(v.default, e)
|
2850
3018
|
|
2851
3019
|
with self.assertRaisesRegex(
|
2852
|
-
TypeError, '
|
3020
|
+
TypeError, '.* cannot extend a frozen value spec'):
|
2853
3021
|
vs.Type(Exception).extend(v)
|
2854
3022
|
|
2855
3023
|
with self.assertRaisesRegex(
|
@@ -3056,6 +3224,12 @@ class UnionTest(ValueSpecTest):
|
|
3056
3224
|
vs.Union([vs.Callable(), vs.Int()]).get_candidate(vs.Any()),
|
3057
3225
|
vs.Callable())
|
3058
3226
|
|
3227
|
+
def test_instantiate(self):
|
3228
|
+
with self.assertRaisesRegex(
|
3229
|
+
TypeError, '.* cannot be instantiated'
|
3230
|
+
):
|
3231
|
+
vs.Union[int, str]()
|
3232
|
+
|
3059
3233
|
def test_apply(self):
|
3060
3234
|
self.assertEqual(vs.Union([vs.Int(), vs.Str()]).apply(1), 1)
|
3061
3235
|
self.assertEqual(
|
@@ -3085,6 +3259,15 @@ class UnionTest(ValueSpecTest):
|
|
3085
3259
|
with self.assertRaisesRegex(TypeError, 'Expect .* but encountered .*'):
|
3086
3260
|
_ = v.apply('foo')
|
3087
3261
|
|
3262
|
+
# Union with strong-type and non-strong-type candidates.
|
3263
|
+
self.assertEqual(
|
3264
|
+
vs.Union([typing.Callable[[int], int], str]).apply('foo'), 'foo'
|
3265
|
+
)
|
3266
|
+
# Union with type conversion.
|
3267
|
+
self.assertIsInstance(
|
3268
|
+
vs.Union([int, str]).apply(datetime.datetime.now()), int
|
3269
|
+
)
|
3270
|
+
|
3088
3271
|
# Bad cases.
|
3089
3272
|
with self.assertRaisesRegex(ValueError, 'Value cannot be None'):
|
3090
3273
|
vs.Union([vs.Int(), vs.Str()]).apply(None)
|
@@ -3093,6 +3276,11 @@ class UnionTest(ValueSpecTest):
|
|
3093
3276
|
TypeError, 'Expect \\(.*\\) but encountered <(type|class) \'list\'>.'):
|
3094
3277
|
vs.Union([vs.Int(), vs.Str()]).apply([])
|
3095
3278
|
|
3279
|
+
with self.assertRaisesRegex(
|
3280
|
+
TypeError, '1 does not match any candidate of .*'
|
3281
|
+
):
|
3282
|
+
vs.Union([typing.Callable[[int], int], str]).apply(1)
|
3283
|
+
|
3096
3284
|
def test_is_compatible(self):
|
3097
3285
|
self.assertTrue(
|
3098
3286
|
vs.Union([vs.Int(), vs.Bool()]).is_compatible(
|
@@ -3160,7 +3348,7 @@ class UnionTest(ValueSpecTest):
|
|
3160
3348
|
|
3161
3349
|
# Test enum of different values cannot be extended.
|
3162
3350
|
with self.assertRaisesRegex(
|
3163
|
-
TypeError, '.* cannot extend .*:
|
3351
|
+
TypeError, '.* cannot extend .*: 1 is not an acceptable value'):
|
3164
3352
|
vs.Union([vs.Enum(1, [1, 2]), vs.Int()]).extend(
|
3165
3353
|
vs.Union([vs.Enum('a', ['a', 'b']), vs.Int()]))
|
3166
3354
|
|
@@ -3192,7 +3380,7 @@ class UnionTest(ValueSpecTest):
|
|
3192
3380
|
self.assertEqual(v.default, 'foo')
|
3193
3381
|
|
3194
3382
|
with self.assertRaisesRegex(
|
3195
|
-
TypeError, '
|
3383
|
+
TypeError, '.* cannot extend a frozen value spec.'):
|
3196
3384
|
vs.Str().extend(v)
|
3197
3385
|
|
3198
3386
|
with self.assertRaisesRegex(
|
@@ -3249,10 +3437,17 @@ class AnyTest(ValueSpecTest):
|
|
3249
3437
|
def test_noneable(self):
|
3250
3438
|
self.assertTrue(vs.Any().is_noneable)
|
3251
3439
|
|
3440
|
+
def test_repr(self):
|
3441
|
+
self.assertEqual(repr(vs.Any()), 'Any()')
|
3442
|
+
self.assertEqual(repr(vs.Any(1)), 'Any(default=1)')
|
3443
|
+
self.assertEqual(repr(vs.Any(1).freeze()), 'Any(default=1, frozen=True)')
|
3444
|
+
|
3252
3445
|
def test_str(self):
|
3253
3446
|
self.assertEqual(str(vs.Any()), 'Any()')
|
3254
|
-
self.assertEqual(str(vs.Any(1)), 'Any(default=1)')
|
3255
|
-
self.assertEqual(
|
3447
|
+
self.assertEqual(str(vs.Any(1)), 'Any(\n default=1\n)')
|
3448
|
+
self.assertEqual(
|
3449
|
+
str(vs.Any(1).freeze()), 'Any(\n default=1,\n frozen=True\n)'
|
3450
|
+
)
|
3256
3451
|
|
3257
3452
|
def test_annotation(self):
|
3258
3453
|
self.assertEqual(vs.Any().annotation, typed_missing.MISSING_VALUE)
|
@@ -3267,6 +3462,10 @@ class AnyTest(ValueSpecTest):
|
|
3267
3462
|
self.assertNotEqual(vs.Any(), vs.Int())
|
3268
3463
|
self.assertNotEqual(vs.Any(True), vs.Any())
|
3269
3464
|
|
3465
|
+
def test_instantiate(self):
|
3466
|
+
with self.assertRaisesRegex(TypeError, '.* cannot be instantiated'):
|
3467
|
+
vs.Any()()
|
3468
|
+
|
3270
3469
|
def test_apply(self):
|
3271
3470
|
self.assertEqual(vs.Any().apply(True), True)
|
3272
3471
|
self.assertEqual(vs.Any().apply(1), 1)
|
@@ -3316,7 +3515,7 @@ class AnyTest(ValueSpecTest):
|
|
3316
3515
|
self.assertEqual(v.default, 'foo')
|
3317
3516
|
|
3318
3517
|
with self.assertRaisesRegex(
|
3319
|
-
TypeError, '
|
3518
|
+
TypeError, '.* cannot extend a frozen value spec'):
|
3320
3519
|
vs.Any().extend(v)
|
3321
3520
|
|
3322
3521
|
with self.assertRaisesRegex(
|
@@ -3387,5 +3586,44 @@ def forward_ref(name):
|
|
3387
3586
|
return class_schema.ForwardRef(sys.modules[__name__], name)
|
3388
3587
|
|
3389
3588
|
|
3589
|
+
class EnsureValueSpecTest(unittest.TestCase):
|
3590
|
+
"""Tests for `ensure_value_spec`."""
|
3591
|
+
|
3592
|
+
def test_basics(self):
|
3593
|
+
self.assertEqual(
|
3594
|
+
vs.ensure_value_spec(
|
3595
|
+
vs.Int(min_value=1), vs.Int()),
|
3596
|
+
vs.Int(min_value=1)
|
3597
|
+
)
|
3598
|
+
|
3599
|
+
self.assertEqual(
|
3600
|
+
vs.ensure_value_spec(
|
3601
|
+
vs.Int(min_value=1), vs.Number(int)
|
3602
|
+
),
|
3603
|
+
vs.Int(min_value=1)
|
3604
|
+
)
|
3605
|
+
|
3606
|
+
with self.assertRaisesRegex(
|
3607
|
+
TypeError, 'Source spec .* is not compatible with destination spec'):
|
3608
|
+
vs.ensure_value_spec(vs.Int(min_value=1), vs.Bool())
|
3609
|
+
|
3610
|
+
def test_union(self):
|
3611
|
+
self.assertEqual(
|
3612
|
+
vs.ensure_value_spec(
|
3613
|
+
vs.Union([vs.Int(), vs.Str(regex='a.*')]),
|
3614
|
+
vs.Str()), vs.Str(regex='a.*')
|
3615
|
+
)
|
3616
|
+
|
3617
|
+
with self.assertRaisesRegex(
|
3618
|
+
TypeError, 'Source spec .* is not compatible with destination spec'):
|
3619
|
+
vs.ensure_value_spec(
|
3620
|
+
vs.Union([vs.Int(), vs.Str()]),
|
3621
|
+
vs.Bool()
|
3622
|
+
)
|
3623
|
+
|
3624
|
+
def test_any(self):
|
3625
|
+
self.assertIsNone(vs.ensure_value_spec(vs.Any(), vs.Int()))
|
3626
|
+
|
3627
|
+
|
3390
3628
|
if __name__ == '__main__':
|
3391
3629
|
unittest.main()
|