pyglove 0.4.5.dev202410301815__py3-none-any.whl → 0.4.5.dev202411020808__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.
Potentially problematic release.
This version of pyglove might be problematic. Click here for more details.
- pyglove/core/io/file_system.py +4 -4
- pyglove/core/io/sequence.py +8 -10
- pyglove/core/io/sequence_test.py +2 -15
- pyglove/core/object_utils/error_utils.py +6 -6
- pyglove/core/object_utils/error_utils_test.py +4 -4
- pyglove/core/object_utils/json_conversion.py +1 -2
- pyglove/core/object_utils/timing.py +23 -12
- pyglove/core/object_utils/timing_test.py +8 -4
- pyglove/core/symbolic/base.py +39 -24
- pyglove/core/symbolic/dict.py +13 -4
- pyglove/core/symbolic/list.py +13 -4
- pyglove/core/symbolic/object.py +5 -2
- {pyglove-0.4.5.dev202410301815.dist-info → pyglove-0.4.5.dev202411020808.dist-info}/METADATA +1 -1
- {pyglove-0.4.5.dev202410301815.dist-info → pyglove-0.4.5.dev202411020808.dist-info}/RECORD +17 -17
- {pyglove-0.4.5.dev202410301815.dist-info → pyglove-0.4.5.dev202411020808.dist-info}/LICENSE +0 -0
- {pyglove-0.4.5.dev202410301815.dist-info → pyglove-0.4.5.dev202411020808.dist-info}/WHEEL +0 -0
- {pyglove-0.4.5.dev202410301815.dist-info → pyglove-0.4.5.dev202411020808.dist-info}/top_level.txt +0 -0
pyglove/core/io/file_system.py
CHANGED
@@ -116,7 +116,7 @@ class FileSystem(metaclass=abc.ABCMeta):
|
|
116
116
|
"""Removes a directory chain based on a path."""
|
117
117
|
|
118
118
|
|
119
|
-
def
|
119
|
+
def resolve_path(path: Union[str, os.PathLike[str]]) -> str:
|
120
120
|
if isinstance(path, str):
|
121
121
|
return path
|
122
122
|
elif hasattr(path, '__fspath__'):
|
@@ -243,7 +243,7 @@ class MemoryFileSystem(FileSystem):
|
|
243
243
|
self._prefix = prefix
|
244
244
|
|
245
245
|
def _internal_path(self, path: Union[str, os.PathLike[str]]) -> str:
|
246
|
-
return '/' +
|
246
|
+
return '/' + resolve_path(path).lstrip(self._prefix)
|
247
247
|
|
248
248
|
def _locate(self, path: Union[str, os.PathLike[str]]) -> Any:
|
249
249
|
current = self._root
|
@@ -287,7 +287,7 @@ class MemoryFileSystem(FileSystem):
|
|
287
287
|
def _parent_and_name(
|
288
288
|
self, path: Union[str, os.PathLike[str]]
|
289
289
|
) -> tuple[dict[str, Any], str]:
|
290
|
-
path =
|
290
|
+
path = resolve_path(path)
|
291
291
|
rpos = path.rfind('/')
|
292
292
|
assert rpos >= 0, path
|
293
293
|
name = path[rpos + 1:]
|
@@ -382,7 +382,7 @@ class _FileSystemRegistry:
|
|
382
382
|
|
383
383
|
def get(self, path: Union[str, os.PathLike[str]]) -> FileSystem:
|
384
384
|
"""Gets the file system for a path."""
|
385
|
-
path =
|
385
|
+
path = resolve_path(path)
|
386
386
|
for prefix, fs in self._filesystems:
|
387
387
|
if path.startswith(prefix):
|
388
388
|
return fs
|
pyglove/core/io/sequence.py
CHANGED
@@ -107,7 +107,7 @@ class _SequenceIORegistry(object):
|
|
107
107
|
|
108
108
|
def get(self, path: Union[str, os.PathLike[str]]) -> SequenceIO:
|
109
109
|
"""Gets the record IO system for a path."""
|
110
|
-
path =
|
110
|
+
path = file_system.resolve_path(path)
|
111
111
|
parts = path.split('.')
|
112
112
|
if parts:
|
113
113
|
extension = parts[-1].lower()
|
@@ -126,15 +126,6 @@ def add_sequence_io(extension: str, sequence_io: SequenceIO) -> None:
|
|
126
126
|
_registry.add(extension, sequence_io)
|
127
127
|
|
128
128
|
|
129
|
-
def _resolve_path(path: Union[str, os.PathLike[str]]) -> str:
|
130
|
-
if isinstance(path, str):
|
131
|
-
return path
|
132
|
-
elif hasattr(path, '__fspath__'):
|
133
|
-
return path.__fspath__()
|
134
|
-
else:
|
135
|
-
raise ValueError(f'Unsupported path: {path!r}.')
|
136
|
-
|
137
|
-
|
138
129
|
def open_sequence(
|
139
130
|
path: Union[str, os.PathLike[str]],
|
140
131
|
mode: str = 'r',
|
@@ -145,6 +136,7 @@ def open_sequence(
|
|
145
136
|
deserializer: Optional[
|
146
137
|
Callable[[Union[bytes, str]], Any]
|
147
138
|
] = None,
|
139
|
+
make_dirs_if_not_exist: bool = True,
|
148
140
|
) -> Sequence:
|
149
141
|
"""Open sequence for reading or writing.
|
150
142
|
|
@@ -155,10 +147,16 @@ def open_sequence(
|
|
155
147
|
object to a string or bytes.
|
156
148
|
deserializer: (Optional) A deserializer function for converting a string or
|
157
149
|
bytes to a structured object.
|
150
|
+
make_dirs_if_not_exist: (Optional) Whether to create the directories
|
151
|
+
if they do not exist. Applicable when opening in write or append mode.
|
158
152
|
|
159
153
|
Returns:
|
160
154
|
A sequence for reading or writing.
|
161
155
|
"""
|
156
|
+
if 'w' in mode or 'a' in mode:
|
157
|
+
parent_dir = os.path.dirname(path)
|
158
|
+
if make_dirs_if_not_exist:
|
159
|
+
file_system.mkdirs(parent_dir, exist_ok=True)
|
162
160
|
return _registry.get(path).open(
|
163
161
|
path, mode, serializer=serializer, deserializer=deserializer
|
164
162
|
)
|
pyglove/core/io/sequence_test.py
CHANGED
@@ -13,7 +13,6 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
15
|
import os
|
16
|
-
import pathlib
|
17
16
|
import tempfile
|
18
17
|
import unittest
|
19
18
|
from pyglove.core.io import sequence as sequence_io
|
@@ -25,7 +24,7 @@ class LineSequenceIOTest(unittest.TestCase):
|
|
25
24
|
|
26
25
|
def test_read_write(self):
|
27
26
|
tmp_dir = tempfile.gettempdir()
|
28
|
-
file1 = os.path.join(tmp_dir, 'file1')
|
27
|
+
file1 = os.path.join(tmp_dir, 'abc', 'file1')
|
29
28
|
with pg_symbolic.open_jsonl(file1, 'w') as f:
|
30
29
|
self.assertIsInstance(f, sequence_io.LineSequence)
|
31
30
|
f.add(1)
|
@@ -73,19 +72,7 @@ class LineSequenceIOTest(unittest.TestCase):
|
|
73
72
|
self.assertEqual(list(iter(f)), ['foo', 'bar'])
|
74
73
|
|
75
74
|
|
76
|
-
class
|
77
|
-
|
78
|
-
def test_resolve_path(self):
|
79
|
-
self.assertEqual(
|
80
|
-
sequence_io._resolve_path('/file1.mem@123'),
|
81
|
-
'/file1.mem@123'
|
82
|
-
)
|
83
|
-
self.assertEqual(
|
84
|
-
sequence_io._resolve_path(pathlib.Path('/file1.mem@123.txt')),
|
85
|
-
'/file1.mem@123.txt'
|
86
|
-
)
|
87
|
-
with self.assertRaisesRegex(ValueError, 'Unsupported path'):
|
88
|
-
sequence_io._resolve_path(1)
|
75
|
+
class MemorySequenceIOTest(unittest.TestCase):
|
89
76
|
|
90
77
|
def test_read_write(self):
|
91
78
|
with sequence_io.open_sequence('/file1.mem@123', 'w') as f:
|
@@ -30,19 +30,19 @@ class ErrorInfo(json_conversion.JSONConvertible, formatting.Formattable):
|
|
30
30
|
"""Serializable error information.
|
31
31
|
|
32
32
|
Attributes:
|
33
|
-
|
33
|
+
tag: A path of the error types in the exception chain. For example,
|
34
34
|
`ValueError.ZeroDivisionError` means the error is a `ZeroDivisionError`
|
35
35
|
raised at the first place and then reraised as a `ValueError`.
|
36
36
|
description: The description of the error.
|
37
37
|
stacktrace: The stacktrace of the error.
|
38
38
|
"""
|
39
39
|
|
40
|
-
|
40
|
+
tag: str
|
41
41
|
description: str
|
42
42
|
stacktrace: str
|
43
43
|
|
44
44
|
@classmethod
|
45
|
-
def
|
45
|
+
def _compute_tag(cls, error: BaseException):
|
46
46
|
error_types = []
|
47
47
|
while error is not None:
|
48
48
|
error_types.append(error.__class__.__name__)
|
@@ -53,7 +53,7 @@ class ErrorInfo(json_conversion.JSONConvertible, formatting.Formattable):
|
|
53
53
|
def from_exception(cls, error: BaseException) -> 'ErrorInfo':
|
54
54
|
"""Creates an error info from an exception."""
|
55
55
|
return cls(
|
56
|
-
|
56
|
+
tag=cls._compute_tag(error),
|
57
57
|
description=str(error),
|
58
58
|
stacktrace=''.join(
|
59
59
|
traceback.format_exception(*sys.exc_info())
|
@@ -63,7 +63,7 @@ class ErrorInfo(json_conversion.JSONConvertible, formatting.Formattable):
|
|
63
63
|
def to_json(self, **kwargs) -> Dict[str, Any]:
|
64
64
|
return self.to_json_dict(
|
65
65
|
fields=dict(
|
66
|
-
|
66
|
+
tag=(self.tag, None),
|
67
67
|
description=(self.description, None),
|
68
68
|
stacktrace=(self.stacktrace, None),
|
69
69
|
),
|
@@ -74,7 +74,7 @@ class ErrorInfo(json_conversion.JSONConvertible, formatting.Formattable):
|
|
74
74
|
def format(self, *args, **kwargs) -> str:
|
75
75
|
return formatting.kvlist_str(
|
76
76
|
[
|
77
|
-
('
|
77
|
+
('tag', self.tag, None),
|
78
78
|
('description', self.description, None),
|
79
79
|
('stacktrace', self.stacktrace, None),
|
80
80
|
],
|
@@ -36,13 +36,13 @@ class ErrorInfoTest(unittest.TestCase):
|
|
36
36
|
except ValueError as e:
|
37
37
|
error_info = error_utils.ErrorInfo.from_exception(e)
|
38
38
|
self.assertIsNotNone(error_info)
|
39
|
-
self.assertEqual(error_info.
|
39
|
+
self.assertEqual(error_info.tag, 'ValueError.ZeroDivisionError')
|
40
40
|
self.assertEqual(error_info.description, 'Bad call to `foo`')
|
41
41
|
self.assertIn('Traceback (most recent call last)', error_info.stacktrace)
|
42
42
|
|
43
43
|
def test_to_json(self):
|
44
44
|
error_info = error_utils.ErrorInfo(
|
45
|
-
|
45
|
+
tag='ValueError.ZeroDivisionError',
|
46
46
|
description='Bad call to `foo`',
|
47
47
|
stacktrace='Traceback (most recent call last)',
|
48
48
|
)
|
@@ -53,7 +53,7 @@ class ErrorInfoTest(unittest.TestCase):
|
|
53
53
|
|
54
54
|
def test_format(self):
|
55
55
|
error_info = error_utils.ErrorInfo(
|
56
|
-
|
56
|
+
tag='ValueError.ZeroDivisionError',
|
57
57
|
description='Bad call to `foo`',
|
58
58
|
stacktrace='Traceback (most recent call last)',
|
59
59
|
)
|
@@ -62,7 +62,7 @@ class ErrorInfoTest(unittest.TestCase):
|
|
62
62
|
inspect.cleandoc(
|
63
63
|
"""
|
64
64
|
ErrorInfo(
|
65
|
-
|
65
|
+
tag='ValueError.ZeroDivisionError',
|
66
66
|
description='Bad call to `foo`',
|
67
67
|
stacktrace='Traceback (most recent call last)'
|
68
68
|
)
|
@@ -202,9 +202,8 @@ class JSONConvertible(metaclass=abc.ABCMeta):
|
|
202
202
|
Returns:
|
203
203
|
An instance of cls.
|
204
204
|
"""
|
205
|
-
del kwargs
|
206
205
|
assert isinstance(json_value, dict)
|
207
|
-
init_args = {k: from_json(v) for k, v in json_value.items()
|
206
|
+
init_args = {k: from_json(v, **kwargs) for k, v in json_value.items()
|
208
207
|
if k != JSONConvertible.TYPE_NAME_KEY}
|
209
208
|
return cls(**init_args)
|
210
209
|
|
@@ -13,10 +13,12 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
"""Utilities for timing."""
|
15
15
|
|
16
|
+
import collections
|
16
17
|
import dataclasses
|
17
18
|
import time
|
18
19
|
from typing import Any, Dict, List, Optional
|
19
20
|
|
21
|
+
from pyglove.core.object_utils import error_utils
|
20
22
|
from pyglove.core.object_utils import json_conversion
|
21
23
|
from pyglove.core.object_utils import thread_local
|
22
24
|
|
@@ -30,7 +32,7 @@ class TimeIt:
|
|
30
32
|
name: str
|
31
33
|
elapse: float = 0.0
|
32
34
|
has_ended: bool = True
|
33
|
-
error: Optional[
|
35
|
+
error: Optional[error_utils.ErrorInfo] = None
|
34
36
|
|
35
37
|
@property
|
36
38
|
def has_started(self) -> bool:
|
@@ -66,6 +68,9 @@ class TimeIt:
|
|
66
68
|
num_ended: int = 0
|
67
69
|
num_failed: int = 0
|
68
70
|
avg_duration: float = 0.0
|
71
|
+
error_tags: Dict[str, int] = dataclasses.field(
|
72
|
+
default_factory=lambda: collections.defaultdict(int)
|
73
|
+
)
|
69
74
|
|
70
75
|
def update(self, status: 'TimeIt.Status'):
|
71
76
|
self.avg_duration = (
|
@@ -77,6 +82,8 @@ class TimeIt:
|
|
77
82
|
self.num_ended += 1
|
78
83
|
if status.has_error:
|
79
84
|
self.num_failed += 1
|
85
|
+
assert status.error is not None
|
86
|
+
self.error_tags[status.error.tag] += 1
|
80
87
|
|
81
88
|
def to_json(self, **kwargs) -> Dict[str, Any]:
|
82
89
|
return self.to_json_dict(
|
@@ -85,6 +92,7 @@ class TimeIt:
|
|
85
92
|
num_ended=(self.num_ended, 0),
|
86
93
|
num_failed=(self.num_failed, 0),
|
87
94
|
avg_duration=(self.avg_duration, 0.0),
|
95
|
+
error_tags=(self.error_tags, {}),
|
88
96
|
),
|
89
97
|
exclude_default=True,
|
90
98
|
**kwargs,
|
@@ -113,13 +121,13 @@ class TimeIt:
|
|
113
121
|
**kwargs,
|
114
122
|
)
|
115
123
|
|
116
|
-
def __init__(self, name: str):
|
117
|
-
self._name = name
|
118
|
-
self._start_time = None
|
119
|
-
self._end_time = None
|
120
|
-
self._child_contexts = {}
|
121
|
-
self._error = None
|
122
|
-
self._parent = None
|
124
|
+
def __init__(self, name: str = ''):
|
125
|
+
self._name: str = name
|
126
|
+
self._start_time: Optional[float] = None
|
127
|
+
self._end_time: Optional[float] = None
|
128
|
+
self._child_contexts: Dict[str, TimeIt] = {}
|
129
|
+
self._error: Optional[error_utils.ErrorInfo] = None
|
130
|
+
self._parent: Optional[TimeIt] = None
|
123
131
|
|
124
132
|
@property
|
125
133
|
def name(self) -> str:
|
@@ -145,7 +153,9 @@ class TimeIt:
|
|
145
153
|
"""Ends timing."""
|
146
154
|
if not self.has_ended:
|
147
155
|
self._end_time = time.time()
|
148
|
-
self._error =
|
156
|
+
self._error = (
|
157
|
+
None if error is None else error_utils.ErrorInfo.from_exception(error)
|
158
|
+
)
|
149
159
|
return True
|
150
160
|
return False
|
151
161
|
|
@@ -170,7 +180,7 @@ class TimeIt:
|
|
170
180
|
return self._end_time
|
171
181
|
|
172
182
|
@property
|
173
|
-
def error(self) -> Optional[
|
183
|
+
def error(self) -> Optional[error_utils.ErrorInfo]:
|
174
184
|
"""Returns error."""
|
175
185
|
return self._error
|
176
186
|
|
@@ -199,7 +209,8 @@ class TimeIt:
|
|
199
209
|
for child in self._child_contexts.values():
|
200
210
|
child_result = child.status()
|
201
211
|
for k, v in child_result.items():
|
202
|
-
|
212
|
+
key = f'{self.name}.{k}' if self.name else k
|
213
|
+
result[key] = v
|
203
214
|
return result
|
204
215
|
|
205
216
|
def __enter__(self):
|
@@ -220,6 +231,6 @@ class TimeIt:
|
|
220
231
|
thread_local.thread_local_set('__timing_context__', self._parent)
|
221
232
|
|
222
233
|
|
223
|
-
def timeit(name: str) -> TimeIt:
|
234
|
+
def timeit(name: str = '') -> TimeIt:
|
224
235
|
"""Context manager to time a block of code."""
|
225
236
|
return TimeIt(name)
|
@@ -100,8 +100,8 @@ class TimeItTest(unittest.TestCase):
|
|
100
100
|
r = t.status()
|
101
101
|
self.assertTrue(r['node'].has_error)
|
102
102
|
self.assertTrue(t.has_error)
|
103
|
-
self.
|
104
|
-
self.
|
103
|
+
self.assertTrue(t.error.tag.startswith('ValueError'))
|
104
|
+
self.assertTrue(r['node'].error.tag.startswith('ValueError'))
|
105
105
|
self.assertTrue(r['node.child'].has_error)
|
106
106
|
self.assertTrue(t1.has_error)
|
107
107
|
self.assertTrue(r['node.child.grandchild'].has_error)
|
@@ -111,7 +111,7 @@ class TimeItTest(unittest.TestCase):
|
|
111
111
|
summary = timing.TimeIt.StatusSummary()
|
112
112
|
self.assertFalse(summary)
|
113
113
|
for i in range(10):
|
114
|
-
with timing.timeit(
|
114
|
+
with timing.timeit() as t:
|
115
115
|
time.sleep(0.1)
|
116
116
|
with timing.timeit('child'):
|
117
117
|
time.sleep(0.1)
|
@@ -126,7 +126,7 @@ class TimeItTest(unittest.TestCase):
|
|
126
126
|
self.assertTrue(summary)
|
127
127
|
self.assertEqual(
|
128
128
|
list(summary.breakdown.keys()),
|
129
|
-
['
|
129
|
+
['', 'child', 'child.grandchild']
|
130
130
|
)
|
131
131
|
self.assertEqual(
|
132
132
|
[x.num_started for x in summary.breakdown.values()],
|
@@ -140,6 +140,10 @@ class TimeItTest(unittest.TestCase):
|
|
140
140
|
[x.num_failed for x in summary.breakdown.values()],
|
141
141
|
[0, 0, 2]
|
142
142
|
)
|
143
|
+
self.assertEqual(
|
144
|
+
summary.breakdown['child.grandchild'].error_tags,
|
145
|
+
{'ValueError': 2},
|
146
|
+
)
|
143
147
|
# Test serialization.
|
144
148
|
json_dict = summary.to_json()
|
145
149
|
summary2 = timing.TimeIt.StatusSummary.from_json(json_dict)
|
pyglove/core/symbolic/base.py
CHANGED
@@ -2041,13 +2041,16 @@ def contains(
|
|
2041
2041
|
return not traverse(x, _contains)
|
2042
2042
|
|
2043
2043
|
|
2044
|
-
def from_json(
|
2045
|
-
|
2046
|
-
|
2047
|
-
|
2048
|
-
|
2049
|
-
|
2050
|
-
|
2044
|
+
def from_json(
|
2045
|
+
json_value: Any,
|
2046
|
+
*,
|
2047
|
+
allow_partial: bool = False,
|
2048
|
+
root_path: Optional[object_utils.KeyPath] = None,
|
2049
|
+
auto_import: bool = True,
|
2050
|
+
auto_dict: bool = False,
|
2051
|
+
value_spec: Optional[pg_typing.ValueSpec] = None,
|
2052
|
+
**kwargs
|
2053
|
+
) -> Any:
|
2051
2054
|
"""Deserializes a (maybe) symbolic value from JSON value.
|
2052
2055
|
|
2053
2056
|
Example::
|
@@ -2073,6 +2076,7 @@ def from_json(json_value: Any,
|
|
2073
2076
|
find the class 'A' within the imported module.
|
2074
2077
|
auto_dict: If True, dict with '_type' that cannot be loaded will remain
|
2075
2078
|
as dict, with '_type' renamed to 'type_name'.
|
2079
|
+
value_spec: The value spec for the symbolic list or dict.
|
2076
2080
|
**kwargs: Allow passing through keyword arguments to from_json of specific
|
2077
2081
|
types.
|
2078
2082
|
|
@@ -2093,10 +2097,15 @@ def from_json(json_value: Any,
|
|
2093
2097
|
json_value, auto_import=auto_import, auto_dict=auto_dict
|
2094
2098
|
)
|
2095
2099
|
|
2096
|
-
|
2097
|
-
|
2098
|
-
|
2099
|
-
|
2100
|
+
def _load_child(k, v):
|
2101
|
+
return from_json(
|
2102
|
+
v,
|
2103
|
+
root_path=object_utils.KeyPath(k, root_path),
|
2104
|
+
_typename_resolved=True,
|
2105
|
+
allow_partial=allow_partial,
|
2106
|
+
**kwargs
|
2107
|
+
)
|
2108
|
+
|
2100
2109
|
if isinstance(json_value, list):
|
2101
2110
|
if (json_value
|
2102
2111
|
and json_value[0] == object_utils.JSONConvertible.TUPLE_MARKER):
|
@@ -2106,21 +2115,27 @@ def from_json(json_value: Any,
|
|
2106
2115
|
f'Tuple should have at least one element '
|
2107
2116
|
f'besides \'{object_utils.JSONConvertible.TUPLE_MARKER}\'. '
|
2108
2117
|
f'Encountered: {json_value}', root_path))
|
2109
|
-
|
2110
|
-
|
2111
|
-
|
2112
|
-
|
2113
|
-
|
2114
|
-
|
2115
|
-
|
2116
|
-
|
2117
|
-
for i, v in enumerate(json_value[1:])
|
2118
|
-
])
|
2119
|
-
return Symbolic.ListType(json_value, **kwargs) # pytype: disable=not-callable # pylint: disable=not-callable
|
2118
|
+
return tuple(_load_child(i, v) for i, v in enumerate(json_value[1:]))
|
2119
|
+
return Symbolic.ListType.from_json( # pytype: disable=attribute-error
|
2120
|
+
json_value,
|
2121
|
+
value_spec=value_spec,
|
2122
|
+
root_path=root_path,
|
2123
|
+
allow_partial=allow_partial,
|
2124
|
+
**kwargs,
|
2125
|
+
)
|
2120
2126
|
elif isinstance(json_value, dict):
|
2121
2127
|
if object_utils.JSONConvertible.TYPE_NAME_KEY not in json_value:
|
2122
|
-
return Symbolic.DictType.from_json(
|
2123
|
-
|
2128
|
+
return Symbolic.DictType.from_json( # pytype: disable=attribute-error
|
2129
|
+
json_value,
|
2130
|
+
value_spec=value_spec,
|
2131
|
+
root_path=root_path,
|
2132
|
+
allow_partial=allow_partial,
|
2133
|
+
**kwargs,
|
2134
|
+
)
|
2135
|
+
return object_utils.from_json(
|
2136
|
+
json_value, _typename_resolved=True,
|
2137
|
+
root_path=root_path, allow_partial=allow_partial, **kwargs
|
2138
|
+
)
|
2124
2139
|
return json_value
|
2125
2140
|
|
2126
2141
|
|
pyglove/core/symbolic/dict.py
CHANGED
@@ -152,10 +152,19 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
|
|
152
152
|
# Not okay:
|
153
153
|
d.a.f2.abc = 1
|
154
154
|
"""
|
155
|
-
return cls(
|
156
|
-
|
157
|
-
|
158
|
-
|
155
|
+
return cls(
|
156
|
+
{
|
157
|
+
k: base.from_json(
|
158
|
+
v,
|
159
|
+
root_path=object_utils.KeyPath(k, root_path),
|
160
|
+
allow_partial=allow_partial,
|
161
|
+
**kwargs
|
162
|
+
) for k, v in json_value.items()
|
163
|
+
},
|
164
|
+
value_spec=value_spec,
|
165
|
+
root_path=root_path,
|
166
|
+
allow_partial=allow_partial,
|
167
|
+
)
|
159
168
|
|
160
169
|
def __init__(self,
|
161
170
|
dict_obj: Union[
|
pyglove/core/symbolic/list.py
CHANGED
@@ -132,10 +132,19 @@ class List(list, base.Symbolic, pg_typing.CustomTyping):
|
|
132
132
|
Returns:
|
133
133
|
A schema-less symbolic list, but its items maybe symbolic.
|
134
134
|
"""
|
135
|
-
return cls(
|
136
|
-
|
137
|
-
|
138
|
-
|
135
|
+
return cls(
|
136
|
+
[
|
137
|
+
base.from_json(
|
138
|
+
v,
|
139
|
+
root_path=object_utils.KeyPath(i, root_path),
|
140
|
+
allow_partial=allow_partial,
|
141
|
+
**kwargs
|
142
|
+
) for i, v in enumerate(json_value)
|
143
|
+
],
|
144
|
+
value_spec=value_spec,
|
145
|
+
root_path=root_path,
|
146
|
+
allow_partial=allow_partial,
|
147
|
+
)
|
139
148
|
|
140
149
|
def __init__(
|
141
150
|
self,
|
pyglove/core/symbolic/object.py
CHANGED
@@ -543,7 +543,9 @@ class Object(base.Symbolic, metaclass=ObjectMeta):
|
|
543
543
|
json_value: Any,
|
544
544
|
*,
|
545
545
|
allow_partial: bool = False,
|
546
|
-
root_path: Optional[object_utils.KeyPath] = None
|
546
|
+
root_path: Optional[object_utils.KeyPath] = None,
|
547
|
+
**kwargs
|
548
|
+
) -> 'Object':
|
547
549
|
"""Class method that load an symbolic Object from a JSON value.
|
548
550
|
|
549
551
|
Example::
|
@@ -580,12 +582,13 @@ class Object(base.Symbolic, metaclass=ObjectMeta):
|
|
580
582
|
json_value: Input JSON value, only JSON dict is acceptable.
|
581
583
|
allow_partial: Whether to allow elements of the list to be partial.
|
582
584
|
root_path: KeyPath of loaded object in its object tree.
|
585
|
+
**kwargs: Additional keyword arguments to pass through.
|
583
586
|
|
584
587
|
Returns:
|
585
588
|
A symbolic Object instance.
|
586
589
|
"""
|
587
590
|
return cls(allow_partial=allow_partial, root_path=root_path, **{
|
588
|
-
k: base.from_json(v, allow_partial=allow_partial)
|
591
|
+
k: base.from_json(v, allow_partial=allow_partial, **kwargs)
|
589
592
|
for k, v in json_value.items()
|
590
593
|
})
|
591
594
|
|
@@ -43,10 +43,10 @@ pyglove/core/hyper/numerical_test.py,sha256=_xZe0qe3wJdrQL4F8pgt0zf04zpbc_9c_5va
|
|
43
43
|
pyglove/core/hyper/object_template.py,sha256=x6aBe-6ueo3CsFJ2yhbXbcdJKfId6wm_KsgkHHbpSoY,22325
|
44
44
|
pyglove/core/hyper/object_template_test.py,sha256=TEFX7LIqUvdCdJILnK_gP5xIgNJKzRnioUF0CGVBzcY,9105
|
45
45
|
pyglove/core/io/__init__.py,sha256=4ZT1a595DqQuLTNYc2JP_eCp_KesXvHmKRkr777bzpg,785
|
46
|
-
pyglove/core/io/file_system.py,sha256=
|
46
|
+
pyglove/core/io/file_system.py,sha256=ZfeQeZ1HF1WjV1REC9wCaut5Wkdu_FjxPzulUju7Z_E,13279
|
47
47
|
pyglove/core/io/file_system_test.py,sha256=FX0ySuh_Xcg1RO68do7ikD4pvslKUzfSpwZ6P4wIP7c,8691
|
48
|
-
pyglove/core/io/sequence.py,sha256=
|
49
|
-
pyglove/core/io/sequence_test.py,sha256=
|
48
|
+
pyglove/core/io/sequence.py,sha256=7QWMGXPtJzHyGPgqkT3yJ01FxKJ4mP4lF5HRDiIHNbQ,8165
|
49
|
+
pyglove/core/io/sequence_test.py,sha256=6tmnS7frBuDR8ussT5jugeh23TDsPDrDnbfxHh81Gy4,3891
|
50
50
|
pyglove/core/object_utils/__init__.py,sha256=bFyt-_uNzpOjJGDfYFTRP1kV6rL4du9xGx_OaFMcIxI,8758
|
51
51
|
pyglove/core/object_utils/codegen.py,sha256=HQlpjz42oh_2lrGSflwuAcEneE9Bv8pgPahQzvJm1KI,1568
|
52
52
|
pyglove/core/object_utils/codegen_test.py,sha256=ThPVyE805plNvM8vwlDxMrcjV-GGdDgbBLSqSOpCvJ0,2166
|
@@ -54,20 +54,20 @@ pyglove/core/object_utils/common_traits.py,sha256=9wfWvcq3uuVgL8mQexgJmWdF2aaQ-w
|
|
54
54
|
pyglove/core/object_utils/common_traits_test.py,sha256=wIeVsF3kon9K0Kbblcaib9hBJeZ76LyGZDD-5A1BD9w,1182
|
55
55
|
pyglove/core/object_utils/docstr_utils.py,sha256=5BY40kXozPKVGOB0eN8jy1P5_GHIzqFJ9FXAu_kzxaw,5119
|
56
56
|
pyglove/core/object_utils/docstr_utils_test.py,sha256=1NIsXXpp87HGC8LB7vx1KEMVVrmeGi733Xpuf6yTUq0,4287
|
57
|
-
pyglove/core/object_utils/error_utils.py,sha256=
|
58
|
-
pyglove/core/object_utils/error_utils_test.py,sha256=
|
57
|
+
pyglove/core/object_utils/error_utils.py,sha256=5mcD61La1tTYmmKodsWfnt8R-yV3yfBfbW4o5rZzCTg,5801
|
58
|
+
pyglove/core/object_utils/error_utils_test.py,sha256=rnX4HYsZaMphWPm4ppgzCQ49LAXfJJ9KDTiDz4f9Ahc,4171
|
59
59
|
pyglove/core/object_utils/formatting.py,sha256=cXGWJd6zleS9QiEfY3w4fqWfFYgi_QF8S2IapTOzAj8,14994
|
60
60
|
pyglove/core/object_utils/formatting_test.py,sha256=QGbpUS8u70NoF8-3v5IYmPWEsML3ZbIwkQpUMwQdTkQ,13589
|
61
61
|
pyglove/core/object_utils/hierarchical.py,sha256=za9sA6XnlUOKUHMKNwQPNSOK9E3wDqXFQB0AND2Yghw,19754
|
62
62
|
pyglove/core/object_utils/hierarchical_test.py,sha256=H7AMpUmL8g9AeKsb4T8JrP1eXOleyLSsrw6TGUvEDB4,21150
|
63
|
-
pyglove/core/object_utils/json_conversion.py,sha256=
|
63
|
+
pyglove/core/object_utils/json_conversion.py,sha256=bR46Z3IfSRYFV-QUsxZgetA09PyfDZ6gkAj1-q-WxvQ,26578
|
64
64
|
pyglove/core/object_utils/json_conversion_test.py,sha256=KGt0r628KHi4fTfeCgrhirfINpYXIR45l5ER4z09jjI,11907
|
65
65
|
pyglove/core/object_utils/missing.py,sha256=0liMs9iEyQxxu6UohdJ5hEM246e9Nu05qp0hqriHsl0,1459
|
66
66
|
pyglove/core/object_utils/missing_test.py,sha256=B36p-vqUvAnXWMszAj9GOPBN0_8cq7vVF61AkcsZ9qU,1396
|
67
67
|
pyglove/core/object_utils/thread_local.py,sha256=i-CnyY3VREtLfAj4_JndBnsKuQLIgwG29ma8dAyRxbI,4839
|
68
68
|
pyglove/core/object_utils/thread_local_test.py,sha256=EvU1-TF7KqpLQxxBvHd7dxtuY22YUQSIwQ0UcR-NORA,6816
|
69
|
-
pyglove/core/object_utils/timing.py,sha256=
|
70
|
-
pyglove/core/object_utils/timing_test.py,sha256=
|
69
|
+
pyglove/core/object_utils/timing.py,sha256=ihOdcZaJo96wpe4U-bXEu-H1PZMWeWOaU-nSW9DFStY,7262
|
70
|
+
pyglove/core/object_utils/timing_test.py,sha256=jym-jV55kRujhgoxmDUUIXuqRLDnBGYiNKN3lUMbnfg,4943
|
71
71
|
pyglove/core/object_utils/value_location.py,sha256=lSFQNTazY2M6_nRLmMbouqZAcZSiOLZQnmQPMD2FDMs,26770
|
72
72
|
pyglove/core/object_utils/value_location_test.py,sha256=wCZYqWf_tq3l4ZM6dnkAs1obmq8LfYawkns2NKos9kk,21406
|
73
73
|
pyglove/core/patching/__init__.py,sha256=C1Q1cWPV74YL3eXbzGvc-8aPw1DR8EK6lRhQYDCwHek,2059
|
@@ -78,7 +78,7 @@ pyglove/core/patching/pattern_based_test.py,sha256=PW1EcVfsFPB6wtgwg3s4dzvigWn3b
|
|
78
78
|
pyglove/core/patching/rule_based.py,sha256=QJsDsYVpyfUoaTZeXZtAdFuxpvB1eniY0qYdZvVqzk0,17056
|
79
79
|
pyglove/core/patching/rule_based_test.py,sha256=qfy0ILmczV_LMHWEnwo2y079OrJsGYO0nKxSZdmIUcI,18782
|
80
80
|
pyglove/core/symbolic/__init__.py,sha256=jYq-LwR1Ql3iMChjz9lN-0heDKiocmxR0ZJFEJ8RHHQ,5787
|
81
|
-
pyglove/core/symbolic/base.py,sha256=
|
81
|
+
pyglove/core/symbolic/base.py,sha256=y84C1c5x1va_HOFaPg4Htpud78B_UqoDKJ4HxiWSOJQ,78607
|
82
82
|
pyglove/core/symbolic/base_test.py,sha256=YLMlQGF4TbxAB2PcN1-ckI70gRIVDxabv-goh43CV7A,7333
|
83
83
|
pyglove/core/symbolic/boilerplate.py,sha256=YO8ZTZJ3VfAqeHqJpIY_ORVDxc1XIzxdplNlpexWEyc,6000
|
84
84
|
pyglove/core/symbolic/boilerplate_test.py,sha256=1CZ1W6kq3l-3tpaknhGFa04V18bO7vPzis5qzWnxHEs,5252
|
@@ -86,7 +86,7 @@ pyglove/core/symbolic/class_wrapper.py,sha256=s8TL7LLim4xRcC5fxlYMuS1zdaEDIZomSF
|
|
86
86
|
pyglove/core/symbolic/class_wrapper_test.py,sha256=bPK-qZ7tVKanzXb1mZ4StkLB5kHtecBjKBesg2NQfL0,21636
|
87
87
|
pyglove/core/symbolic/compounding.py,sha256=zg6x09rtaSEb5v5pYJWWWpI5Cezy2Oy1iuWw4LUj2HY,11717
|
88
88
|
pyglove/core/symbolic/compounding_test.py,sha256=PSnk_ds0q6pRJrpN9teMwZtMrbUp5_P-D7ea7wRTK6k,8372
|
89
|
-
pyglove/core/symbolic/dict.py,sha256=
|
89
|
+
pyglove/core/symbolic/dict.py,sha256=gp2sn9nf7movorKWDQsbCQmj0PBdqFul0FYJLm5_kac,36839
|
90
90
|
pyglove/core/symbolic/dict_test.py,sha256=09kAEy_Kiwr8O59CIKK9a9KG-xxJJEIKbfEcZhs5KfU,70809
|
91
91
|
pyglove/core/symbolic/diff.py,sha256=g_KGL50R--LaMcqMQfC2jq7EuRxd8gaYY9X_KYGsdSk,16186
|
92
92
|
pyglove/core/symbolic/diff_test.py,sha256=EDiGHqqKhi-NeMxr-bgjBEqlquee_4l_0IM6hgAb9Mg,29400
|
@@ -96,9 +96,9 @@ pyglove/core/symbolic/functor.py,sha256=YGhSgsll2dKIc8JeVMVRzzhIliEO025XaGPzHzv0
|
|
96
96
|
pyglove/core/symbolic/functor_test.py,sha256=7ZKPmJkDj-zvlDJyp1EKLJ8HDmvlhsiVFky0sbeCqpA,31846
|
97
97
|
pyglove/core/symbolic/inferred.py,sha256=jGCKXLkYGDs-iUflR57UWrCrOQIpkpv5kHVyj-Jbhy4,3192
|
98
98
|
pyglove/core/symbolic/inferred_test.py,sha256=G6uPykONcChvs6vZujXHSWaYfjewLTVBscMqzzKNty0,1270
|
99
|
-
pyglove/core/symbolic/list.py,sha256=
|
99
|
+
pyglove/core/symbolic/list.py,sha256=63v4Ph0FdkoCDj1FjwcmjUHGZSJLBLxaTKcGg7PdghE,30345
|
100
100
|
pyglove/core/symbolic/list_test.py,sha256=yHYAJhe_EYwtU9p8eDztSXNBjnAGKe0UDN5U6S-xDr8,60627
|
101
|
-
pyglove/core/symbolic/object.py,sha256=
|
101
|
+
pyglove/core/symbolic/object.py,sha256=1NdVMGoZiyuuxXIbQiW-n4NhUdHSuiedB-chx1n0BrQ,42349
|
102
102
|
pyglove/core/symbolic/object_test.py,sha256=9-iwW3WVXfQnN4Jj2nlWFXAMJz3PEAYVlINgb7m9_LA,93013
|
103
103
|
pyglove/core/symbolic/origin.py,sha256=5bH1jZvFHY5jwku32vDm8Bj2i-buv-YNuzOOsA5GlSA,6177
|
104
104
|
pyglove/core/symbolic/origin_test.py,sha256=dU_ZGrGDetM_lYVMn3wQO0d367_t_t8eESe3NrKPBNE,3159
|
@@ -186,8 +186,8 @@ pyglove/ext/scalars/randoms.py,sha256=LkMIIx7lOq_lvJvVS3BrgWGuWl7Pi91-lA-O8x_gZs
|
|
186
186
|
pyglove/ext/scalars/randoms_test.py,sha256=nEhiqarg8l_5EOucp59CYrpO2uKxS1pe0hmBdZUzRNM,2000
|
187
187
|
pyglove/ext/scalars/step_wise.py,sha256=IDw3tuTpv0KVh7AN44W43zqm1-E0HWPUlytWOQC9w3Y,3789
|
188
188
|
pyglove/ext/scalars/step_wise_test.py,sha256=TL1vJ19xVx2t5HKuyIzGoogF7N3Rm8YhLE6JF7i0iy8,2540
|
189
|
-
pyglove-0.4.5.
|
190
|
-
pyglove-0.4.5.
|
191
|
-
pyglove-0.4.5.
|
192
|
-
pyglove-0.4.5.
|
193
|
-
pyglove-0.4.5.
|
189
|
+
pyglove-0.4.5.dev202411020808.dist-info/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
190
|
+
pyglove-0.4.5.dev202411020808.dist-info/METADATA,sha256=OFXogc-n-WTnIvBpoY6reTyUuXwvVN0ZyWGPWxkRBSY,6666
|
191
|
+
pyglove-0.4.5.dev202411020808.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
192
|
+
pyglove-0.4.5.dev202411020808.dist-info/top_level.txt,sha256=wITzJSKcj8GZUkbq-MvUQnFadkiuAv_qv5qQMw0fIow,8
|
193
|
+
pyglove-0.4.5.dev202411020808.dist-info/RECORD,,
|
File without changes
|
File without changes
|
{pyglove-0.4.5.dev202410301815.dist-info → pyglove-0.4.5.dev202411020808.dist-info}/top_level.txt
RENAMED
File without changes
|