pyglove 0.5.0.dev202508250811__py3-none-any.whl → 0.5.0.dev202511300809__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 +8 -1
- pyglove/core/geno/base.py +7 -3
- pyglove/core/io/file_system.py +295 -2
- pyglove/core/io/file_system_test.py +291 -0
- pyglove/core/logging.py +45 -1
- pyglove/core/logging_test.py +12 -21
- pyglove/core/monitoring.py +657 -0
- pyglove/core/monitoring_test.py +289 -0
- pyglove/core/symbolic/__init__.py +7 -0
- pyglove/core/symbolic/base.py +89 -35
- pyglove/core/symbolic/base_test.py +3 -3
- pyglove/core/symbolic/dict.py +31 -12
- pyglove/core/symbolic/dict_test.py +49 -0
- pyglove/core/symbolic/list.py +17 -3
- pyglove/core/symbolic/list_test.py +24 -2
- pyglove/core/symbolic/object.py +3 -1
- pyglove/core/symbolic/object_test.py +13 -10
- pyglove/core/symbolic/ref.py +19 -7
- pyglove/core/symbolic/ref_test.py +94 -7
- pyglove/core/symbolic/unknown_symbols.py +147 -0
- pyglove/core/symbolic/unknown_symbols_test.py +100 -0
- pyglove/core/typing/annotation_conversion.py +8 -1
- pyglove/core/typing/annotation_conversion_test.py +14 -19
- pyglove/core/typing/class_schema.py +24 -1
- pyglove/core/typing/json_schema.py +221 -8
- pyglove/core/typing/json_schema_test.py +508 -12
- pyglove/core/typing/type_conversion.py +17 -3
- pyglove/core/typing/type_conversion_test.py +7 -2
- pyglove/core/typing/value_specs.py +5 -1
- pyglove/core/typing/value_specs_test.py +5 -0
- pyglove/core/utils/__init__.py +2 -0
- pyglove/core/utils/contextual.py +9 -4
- pyglove/core/utils/contextual_test.py +10 -0
- pyglove/core/utils/error_utils.py +59 -25
- pyglove/core/utils/json_conversion.py +360 -63
- pyglove/core/utils/json_conversion_test.py +146 -13
- pyglove/core/views/html/controls/tab.py +33 -0
- pyglove/core/views/html/controls/tab_test.py +37 -0
- pyglove/ext/evolution/base_test.py +1 -1
- {pyglove-0.5.0.dev202508250811.dist-info → pyglove-0.5.0.dev202511300809.dist-info}/METADATA +8 -1
- {pyglove-0.5.0.dev202508250811.dist-info → pyglove-0.5.0.dev202511300809.dist-info}/RECORD +44 -40
- {pyglove-0.5.0.dev202508250811.dist-info → pyglove-0.5.0.dev202511300809.dist-info}/WHEEL +0 -0
- {pyglove-0.5.0.dev202508250811.dist-info → pyglove-0.5.0.dev202511300809.dist-info}/licenses/LICENSE +0 -0
- {pyglove-0.5.0.dev202508250811.dist-info → pyglove-0.5.0.dev202511300809.dist-info}/top_level.txt +0 -0
|
@@ -34,6 +34,12 @@ class Foo:
|
|
|
34
34
|
_MODULE = sys.modules[__name__]
|
|
35
35
|
|
|
36
36
|
|
|
37
|
+
def typing_forward_ref(name: str) -> typing.ForwardRef:
|
|
38
|
+
if sys.version_info >= (3, 14):
|
|
39
|
+
return typing.ForwardRef(name)
|
|
40
|
+
return typing.ForwardRef(name, False, _MODULE)
|
|
41
|
+
|
|
42
|
+
|
|
37
43
|
class AnnotationFromStrTest(unittest.TestCase):
|
|
38
44
|
"""Tests for annotation_from_str."""
|
|
39
45
|
|
|
@@ -69,7 +75,7 @@ class AnnotationFromStrTest(unittest.TestCase):
|
|
|
69
75
|
)
|
|
70
76
|
self.assertEqual(
|
|
71
77
|
annotation_conversion.annotation_from_str('list[Foo.Baz]', _MODULE),
|
|
72
|
-
list[
|
|
78
|
+
list[typing_forward_ref('Foo.Baz')]
|
|
73
79
|
)
|
|
74
80
|
|
|
75
81
|
def test_generic_types(self):
|
|
@@ -138,18 +144,12 @@ class AnnotationFromStrTest(unittest.TestCase):
|
|
|
138
144
|
self.assertEqual(
|
|
139
145
|
annotation_conversion.annotation_from_str(
|
|
140
146
|
'AAA', _MODULE),
|
|
141
|
-
|
|
142
|
-
'AAA', False, _MODULE
|
|
143
|
-
)
|
|
147
|
+
typing_forward_ref('AAA')
|
|
144
148
|
)
|
|
145
149
|
self.assertEqual(
|
|
146
150
|
annotation_conversion.annotation_from_str(
|
|
147
151
|
'typing.List[AAA]', _MODULE),
|
|
148
|
-
typing.List[
|
|
149
|
-
typing.ForwardRef(
|
|
150
|
-
'AAA', False, _MODULE
|
|
151
|
-
)
|
|
152
|
-
]
|
|
152
|
+
typing.List[typing_forward_ref('AAA')]
|
|
153
153
|
)
|
|
154
154
|
|
|
155
155
|
def test_reloading(self):
|
|
@@ -157,20 +157,12 @@ class AnnotationFromStrTest(unittest.TestCase):
|
|
|
157
157
|
self.assertEqual(
|
|
158
158
|
annotation_conversion.annotation_from_str(
|
|
159
159
|
'typing.List[Foo]', _MODULE),
|
|
160
|
-
typing.List[
|
|
161
|
-
typing.ForwardRef(
|
|
162
|
-
'Foo', False, _MODULE
|
|
163
|
-
)
|
|
164
|
-
]
|
|
160
|
+
typing.List[typing_forward_ref('Foo')]
|
|
165
161
|
)
|
|
166
162
|
self.assertEqual(
|
|
167
163
|
annotation_conversion.annotation_from_str(
|
|
168
164
|
'typing.List[Foo.Bar]', _MODULE),
|
|
169
|
-
typing.List[
|
|
170
|
-
typing.ForwardRef(
|
|
171
|
-
'Foo.Bar', False, _MODULE
|
|
172
|
-
)
|
|
173
|
-
]
|
|
165
|
+
typing.List[typing_forward_ref('Foo.Bar')]
|
|
174
166
|
)
|
|
175
167
|
delattr(_MODULE, '__reloading__')
|
|
176
168
|
|
|
@@ -396,6 +388,9 @@ class ValueSpecFromAnnotationTest(unittest.TestCase):
|
|
|
396
388
|
self.assertEqual(
|
|
397
389
|
ValueSpec.from_annotation(typing.Dict[str, int], True),
|
|
398
390
|
vs.Dict([(ks.StrKey(), vs.Int())]))
|
|
391
|
+
self.assertEqual(
|
|
392
|
+
ValueSpec.from_annotation(typing.Dict[str, vs.Int()], True),
|
|
393
|
+
vs.Dict([(ks.StrKey(), vs.Int())]))
|
|
399
394
|
self.assertEqual(
|
|
400
395
|
ValueSpec.from_annotation(typing.Mapping[str, int], True),
|
|
401
396
|
vs.Dict([(ks.StrKey(), vs.Int())]))
|
|
@@ -582,6 +582,16 @@ class ValueSpec(utils.Formattable, utils.JSONConvertible):
|
|
|
582
582
|
del include_type_name, include_subclasses, inline_nested_refs, kwargs
|
|
583
583
|
assert False, 'Overridden in `json_schema.py`.'
|
|
584
584
|
|
|
585
|
+
@classmethod
|
|
586
|
+
def from_json_schema(
|
|
587
|
+
cls,
|
|
588
|
+
json_schema: Dict[str, Any],
|
|
589
|
+
class_fn: Optional[Callable[[str, 'Schema'], Type[Any]]] = None
|
|
590
|
+
) -> 'ValueSpec':
|
|
591
|
+
"""Converts a JSON schema to a value spec."""
|
|
592
|
+
del json_schema, class_fn
|
|
593
|
+
assert False, 'Overridden in `json_schema.py`.'
|
|
594
|
+
|
|
585
595
|
|
|
586
596
|
class Field(utils.Formattable, utils.JSONConvertible):
|
|
587
597
|
"""Class that represents the definition of one or a group of attributes.
|
|
@@ -1219,6 +1229,9 @@ class Schema(utils.Formattable, utils.JSONConvertible):
|
|
|
1219
1229
|
f'(parent=\'{root_path}\')'
|
|
1220
1230
|
)
|
|
1221
1231
|
|
|
1232
|
+
# Symbolic.Dict uses `sym_getattr` to support getting symbolic attributes.
|
|
1233
|
+
get_value = getattr(dict_obj, 'sym_getattr', dict_obj.get)
|
|
1234
|
+
|
|
1222
1235
|
for key_spec, keys in matched_keys.items():
|
|
1223
1236
|
field = self._fields[key_spec]
|
|
1224
1237
|
# For missing const keys, we add to keys collection to add missing value.
|
|
@@ -1226,7 +1239,7 @@ class Schema(utils.Formattable, utils.JSONConvertible):
|
|
|
1226
1239
|
keys.append(str(key_spec))
|
|
1227
1240
|
for key in keys:
|
|
1228
1241
|
if dict_obj:
|
|
1229
|
-
value =
|
|
1242
|
+
value = get_value(key, utils.MISSING_VALUE)
|
|
1230
1243
|
else:
|
|
1231
1244
|
value = utils.MISSING_VALUE
|
|
1232
1245
|
# NOTE(daiyip): field.default_value may be MISSING_VALUE too
|
|
@@ -1375,6 +1388,16 @@ class Schema(utils.Formattable, utils.JSONConvertible):
|
|
|
1375
1388
|
def __ne__(self, other: Any) -> bool:
|
|
1376
1389
|
return not self.__eq__(other)
|
|
1377
1390
|
|
|
1391
|
+
@classmethod
|
|
1392
|
+
def from_json_schema(
|
|
1393
|
+
cls,
|
|
1394
|
+
json_schema: Dict[str, Any],
|
|
1395
|
+
class_fn: Optional[Callable[[str, 'Schema'], Type[Any]]] = None
|
|
1396
|
+
) -> 'Schema':
|
|
1397
|
+
"""Converts a JSON schema to a schema."""
|
|
1398
|
+
del json_schema, class_fn
|
|
1399
|
+
assert False, 'Overridden in `json_schema.py`.'
|
|
1400
|
+
|
|
1378
1401
|
|
|
1379
1402
|
FieldDef = Union[
|
|
1380
1403
|
# Key, Value spec/annotation.
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
import dataclasses
|
|
17
17
|
import inspect
|
|
18
|
-
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
18
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple, Type, Union
|
|
19
19
|
|
|
20
20
|
from pyglove.core import utils
|
|
21
21
|
from pyglove.core.typing import callable_signature
|
|
@@ -53,8 +53,8 @@ def _json_schema_from_schema(
|
|
|
53
53
|
required = []
|
|
54
54
|
|
|
55
55
|
if type_name and include_type_name:
|
|
56
|
-
properties[
|
|
57
|
-
required.append(
|
|
56
|
+
properties[utils.JSONConvertible.TYPE_NAME_KEY] = {'const': type_name}
|
|
57
|
+
required.append(utils.JSONConvertible.TYPE_NAME_KEY)
|
|
58
58
|
|
|
59
59
|
for key, field in schema.items():
|
|
60
60
|
if isinstance(key, ks.ConstStrKey):
|
|
@@ -145,7 +145,6 @@ def _json_schema_from_value_spec(
|
|
|
145
145
|
}
|
|
146
146
|
if not isinstance(value_spec.element.value, vs.Any):
|
|
147
147
|
definition['items'] = _child_json_schema(value_spec.element.value)
|
|
148
|
-
return definition
|
|
149
148
|
elif isinstance(value_spec, vs.Dict):
|
|
150
149
|
if value_spec.schema is None:
|
|
151
150
|
definition = {
|
|
@@ -168,9 +167,7 @@ def _json_schema_from_value_spec(
|
|
|
168
167
|
include_type_name=include_type_name,
|
|
169
168
|
include_subclasses=include_subclasses,
|
|
170
169
|
)
|
|
171
|
-
definitions = [
|
|
172
|
-
_json_schema_from_cls(value_spec.cls)
|
|
173
|
-
]
|
|
170
|
+
definitions = [_json_schema_from_cls(value_spec.cls)]
|
|
174
171
|
|
|
175
172
|
if include_subclasses:
|
|
176
173
|
for subclass in value_spec.cls.__subclasses__():
|
|
@@ -206,12 +203,26 @@ def _json_schema_from_value_spec(
|
|
|
206
203
|
f'Value spec {value_spec!r} cannot be converted to JSON schema.'
|
|
207
204
|
)
|
|
208
205
|
|
|
206
|
+
if (value_spec.has_default
|
|
207
|
+
and value_spec.default is not None
|
|
208
|
+
and not isinstance(value_spec, vs.Dict)):
|
|
209
|
+
default = utils.to_json(value_spec.default)
|
|
210
|
+
if not include_type_name:
|
|
211
|
+
def _remove_type_name(_, v: Dict[str, Any]):
|
|
212
|
+
if isinstance(v, dict):
|
|
213
|
+
v.pop(utils.JSONConvertible.TYPE_NAME_KEY, None)
|
|
214
|
+
return v
|
|
215
|
+
default = utils.transform(default, _remove_type_name)
|
|
216
|
+
definition['default'] = default
|
|
217
|
+
|
|
209
218
|
if not ignore_nonable and value_spec.is_noneable:
|
|
210
219
|
nullable = {'type': 'null'}
|
|
211
220
|
if 'anyOf' in definition:
|
|
212
221
|
definition['anyOf'].append(nullable)
|
|
213
222
|
else:
|
|
214
223
|
definition = {'anyOf': [definition, nullable]}
|
|
224
|
+
if value_spec.default is None:
|
|
225
|
+
definition['default'] = None
|
|
215
226
|
return definition
|
|
216
227
|
|
|
217
228
|
|
|
@@ -294,6 +305,8 @@ def _canonicalize_schema(
|
|
|
294
305
|
}
|
|
295
306
|
canonical_form = {'$defs': referenced_defs} if referenced_defs else {}
|
|
296
307
|
canonical_form.update(new_root)
|
|
308
|
+
if 'default' in root:
|
|
309
|
+
canonical_form['default'] = root['default']
|
|
297
310
|
return canonical_form
|
|
298
311
|
|
|
299
312
|
#
|
|
@@ -336,5 +349,205 @@ def _schema_to_json_schema(
|
|
|
336
349
|
**kwargs
|
|
337
350
|
)
|
|
338
351
|
|
|
339
|
-
|
|
352
|
+
class_schema.ValueSpec.to_json_schema = _value_spec_to_json_schema
|
|
340
353
|
class_schema.Schema.to_json_schema = _schema_to_json_schema
|
|
354
|
+
|
|
355
|
+
#
|
|
356
|
+
# from JSON schema to PyGlove value spec.
|
|
357
|
+
#
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def _json_schema_to_value_spec(
|
|
361
|
+
json_schema: Dict[str, Any],
|
|
362
|
+
defs: Dict[str, Type[Any]],
|
|
363
|
+
class_fn: Optional[Callable[[str, class_schema.Schema], Type[Any]]],
|
|
364
|
+
add_json_schema_as_metadata: bool,
|
|
365
|
+
) -> class_schema.ValueSpec:
|
|
366
|
+
"""Converts a JSON schema to a value spec."""
|
|
367
|
+
# Generate code to convert JSON schema to value spec.
|
|
368
|
+
def _value_spec(value_schema: Dict[str, Any]):
|
|
369
|
+
return _json_schema_to_value_spec(
|
|
370
|
+
value_schema, defs, class_fn, add_json_schema_as_metadata
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
if '$ref' in json_schema:
|
|
374
|
+
# TODO(daiyip): Support circular references.
|
|
375
|
+
ref_key = json_schema['$ref'].split('/')[-1]
|
|
376
|
+
type_ref = defs.get(ref_key)
|
|
377
|
+
if type_ref is None:
|
|
378
|
+
raise ValueError(
|
|
379
|
+
f'Reference {ref_key!r} not defined in defs. '
|
|
380
|
+
'Please make sure classes being referenced are defined '
|
|
381
|
+
'before the referencing classes. '
|
|
382
|
+
)
|
|
383
|
+
return type_ref
|
|
384
|
+
type_str = json_schema.get('type')
|
|
385
|
+
default = json_schema.get('default', utils.MISSING_VALUE)
|
|
386
|
+
if type_str is None:
|
|
387
|
+
if 'enum' in json_schema:
|
|
388
|
+
for v in json_schema['enum']:
|
|
389
|
+
if not isinstance(v, (str, int, float, bool)):
|
|
390
|
+
raise ValueError(
|
|
391
|
+
f'Enum candidate {v!r} is not supported for JSON schema '
|
|
392
|
+
'conversion.'
|
|
393
|
+
)
|
|
394
|
+
return vs.Enum(
|
|
395
|
+
default,
|
|
396
|
+
[v for v in json_schema['enum'] if v is not None]
|
|
397
|
+
)
|
|
398
|
+
elif 'anyOf' in json_schema:
|
|
399
|
+
candidates = []
|
|
400
|
+
accepts_none = False
|
|
401
|
+
for v in json_schema['anyOf']:
|
|
402
|
+
candidate = _value_spec(v)
|
|
403
|
+
if candidate.frozen and candidate.default is None:
|
|
404
|
+
accepts_none = True
|
|
405
|
+
continue
|
|
406
|
+
candidates.append(candidate)
|
|
407
|
+
|
|
408
|
+
if len(candidates) == 1:
|
|
409
|
+
spec = candidates[0]
|
|
410
|
+
else:
|
|
411
|
+
spec = vs.Union(candidates)
|
|
412
|
+
if accepts_none:
|
|
413
|
+
spec = spec.noneable()
|
|
414
|
+
return spec
|
|
415
|
+
elif type_str == 'null':
|
|
416
|
+
return vs.Any().freeze(None)
|
|
417
|
+
elif type_str == 'boolean':
|
|
418
|
+
return vs.Bool(default=default)
|
|
419
|
+
elif type_str == 'integer':
|
|
420
|
+
minimum = json_schema.get('minimum')
|
|
421
|
+
maximum = json_schema.get('maximum')
|
|
422
|
+
return vs.Int(min_value=minimum, max_value=maximum, default=default)
|
|
423
|
+
elif type_str == 'number':
|
|
424
|
+
minimum = json_schema.get('minimum')
|
|
425
|
+
maximum = json_schema.get('maximum')
|
|
426
|
+
return vs.Float(min_value=minimum, max_value=maximum, default=default)
|
|
427
|
+
elif type_str == 'string':
|
|
428
|
+
pattern = json_schema.get('pattern')
|
|
429
|
+
return vs.Str(regex=pattern, default=default)
|
|
430
|
+
elif type_str == 'array':
|
|
431
|
+
items = json_schema.get('items')
|
|
432
|
+
return vs.List(_value_spec(items) if items else vs.Any(), default=default)
|
|
433
|
+
elif type_str == 'object':
|
|
434
|
+
schema = _json_schema_to_schema(
|
|
435
|
+
json_schema, defs, class_fn, add_json_schema_as_metadata
|
|
436
|
+
)
|
|
437
|
+
if class_fn is not None and 'title' in json_schema:
|
|
438
|
+
return vs.Object(class_fn(json_schema['title'], schema))
|
|
439
|
+
return vs.Dict(schema=schema if schema.fields else None)
|
|
440
|
+
raise ValueError(f'Unsupported type {type_str!r} in JSON schema.')
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def _json_schema_to_schema(
|
|
444
|
+
json_schema: Dict[str, Any],
|
|
445
|
+
defs: Dict[str, Type[Any]],
|
|
446
|
+
class_fn: Optional[Callable[[str, class_schema.Schema], Type[Any]]],
|
|
447
|
+
add_json_schema_as_metadata: bool,
|
|
448
|
+
) -> class_schema.Schema:
|
|
449
|
+
"""Converts a JSON schema to a schema."""
|
|
450
|
+
title = json_schema.get('title')
|
|
451
|
+
properties = json_schema.get('properties', {})
|
|
452
|
+
fields = []
|
|
453
|
+
required = set(json_schema.get('required', []))
|
|
454
|
+
for name, property_schema in properties.items():
|
|
455
|
+
value_spec = _json_schema_to_value_spec(
|
|
456
|
+
property_schema, defs, class_fn, add_json_schema_as_metadata
|
|
457
|
+
)
|
|
458
|
+
if name not in required and not value_spec.has_default:
|
|
459
|
+
value_spec = value_spec.noneable()
|
|
460
|
+
fields.append(
|
|
461
|
+
class_schema.Field(
|
|
462
|
+
name,
|
|
463
|
+
value_spec,
|
|
464
|
+
description=property_schema.get('description'),
|
|
465
|
+
metadata=(
|
|
466
|
+
dict(json_schema=property_schema)
|
|
467
|
+
if add_json_schema_as_metadata else None
|
|
468
|
+
)
|
|
469
|
+
)
|
|
470
|
+
)
|
|
471
|
+
additional_properties = json_schema.get('additionalProperties')
|
|
472
|
+
if additional_properties:
|
|
473
|
+
if isinstance(additional_properties, dict):
|
|
474
|
+
value_spec = _json_schema_to_value_spec(
|
|
475
|
+
additional_properties, defs, class_fn, add_json_schema_as_metadata
|
|
476
|
+
)
|
|
477
|
+
else:
|
|
478
|
+
value_spec = vs.Any()
|
|
479
|
+
fields.append(class_schema.Field(ks.StrKey(), value_spec))
|
|
480
|
+
return class_schema.Schema(
|
|
481
|
+
name=title,
|
|
482
|
+
description=json_schema.get('description'),
|
|
483
|
+
fields=fields,
|
|
484
|
+
allow_nonconst_keys=True,
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
@classmethod
|
|
489
|
+
def _value_spec_from_json_schema(
|
|
490
|
+
cls,
|
|
491
|
+
json_schema: Dict[str, Any],
|
|
492
|
+
class_fn: Optional[Callable[[str, class_schema.Schema], Type[Any]]] = None,
|
|
493
|
+
add_json_schema_as_metadata: bool = False,
|
|
494
|
+
) -> class_schema.ValueSpec:
|
|
495
|
+
"""Creates a PyGlove value spec from a JSON schema.
|
|
496
|
+
|
|
497
|
+
Args:
|
|
498
|
+
json_schema: The JSON schema for a value spec.
|
|
499
|
+
class_fn: A function that creates a PyGlove class from a class name and a
|
|
500
|
+
schema. If None, all "object" type properties will be converted to
|
|
501
|
+
`pg.typing.Dict`. Otherwise, "object" type properties will be converted to
|
|
502
|
+
a class.
|
|
503
|
+
add_json_schema_as_metadata: Whether to add the JSON schema as field
|
|
504
|
+
metadata.
|
|
505
|
+
|
|
506
|
+
Returns:
|
|
507
|
+
A PyGlove value spec.
|
|
508
|
+
"""
|
|
509
|
+
del cls
|
|
510
|
+
defs = {}
|
|
511
|
+
if '$defs' in json_schema:
|
|
512
|
+
for key, def_entry in json_schema['$defs'].items():
|
|
513
|
+
defs[key] = _json_schema_to_value_spec(
|
|
514
|
+
def_entry, defs, class_fn, add_json_schema_as_metadata
|
|
515
|
+
)
|
|
516
|
+
return _json_schema_to_value_spec(
|
|
517
|
+
json_schema, defs, class_fn, add_json_schema_as_metadata
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
@classmethod
|
|
522
|
+
def _schema_from_json_schema(
|
|
523
|
+
cls,
|
|
524
|
+
json_schema: Dict[str, Any],
|
|
525
|
+
class_fn: Optional[Callable[[str, class_schema.Schema], Type[Any]]] = None,
|
|
526
|
+
add_json_schema_as_metadata: bool = False,
|
|
527
|
+
) -> class_schema.Schema:
|
|
528
|
+
"""Creates a PyGlove schema from a JSON schema.
|
|
529
|
+
|
|
530
|
+
Args:
|
|
531
|
+
json_schema: The JSON schema to convert.
|
|
532
|
+
class_fn: A function that creates a PyGlove class from a class name and a
|
|
533
|
+
schema. If None, all "object" type properties will be converted to
|
|
534
|
+
`pg.typing.Dict`. Otherwise, "object" type properties will be converted to
|
|
535
|
+
a class.
|
|
536
|
+
add_json_schema_as_metadata: Whether to add the JSON schema as field
|
|
537
|
+
metadata.
|
|
538
|
+
|
|
539
|
+
Returns:
|
|
540
|
+
A PyGlove schema.
|
|
541
|
+
"""
|
|
542
|
+
del cls
|
|
543
|
+
if json_schema.get('type') != 'object':
|
|
544
|
+
raise ValueError(
|
|
545
|
+
f'JSON schema is not an object type: {json_schema!r}'
|
|
546
|
+
)
|
|
547
|
+
return _json_schema_to_schema(
|
|
548
|
+
json_schema, {}, class_fn, add_json_schema_as_metadata
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
class_schema.ValueSpec.from_json_schema = _value_spec_from_json_schema
|
|
553
|
+
class_schema.Schema.from_json_schema = _schema_from_json_schema
|