pyglove 0.4.5.dev20240319__py3-none-any.whl → 0.4.5.dev202501140808__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.dev202501140808.dist-info}/METADATA +18 -4
- pyglove-0.4.5.dev202501140808.dist-info/RECORD +214 -0
- {pyglove-0.4.5.dev20240319.dist-info → pyglove-0.4.5.dev202501140808.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.dev202501140808.dist-info}/LICENSE +0 -0
- {pyglove-0.4.5.dev20240319.dist-info → pyglove-0.4.5.dev202501140808.dist-info}/top_level.txt +0 -0
pyglove/core/symbolic/origin.py
CHANGED
@@ -16,11 +16,11 @@
|
|
16
16
|
import traceback
|
17
17
|
from typing import Any, Callable, List, Optional
|
18
18
|
|
19
|
-
from pyglove.core import
|
19
|
+
from pyglove.core import utils
|
20
20
|
from pyglove.core.symbolic import flags
|
21
21
|
|
22
22
|
|
23
|
-
class Origin(
|
23
|
+
class Origin(utils.Formattable):
|
24
24
|
"""Class that represents the origin of a symbolic value.
|
25
25
|
|
26
26
|
Origin is used for debugging the creation chain of a symbolic value, as
|
@@ -152,23 +152,26 @@ class Origin(object_utils.Formattable):
|
|
152
152
|
compact: bool = False,
|
153
153
|
verbose: bool = True,
|
154
154
|
root_indent: int = 0,
|
155
|
-
*,
|
156
|
-
markdown: bool = False,
|
157
155
|
**kwargs,
|
158
156
|
) -> str:
|
159
157
|
"""Formats this object."""
|
160
158
|
if isinstance(self._source, (str, type(None))):
|
161
|
-
source_str =
|
159
|
+
source_str = self._source
|
162
160
|
else:
|
163
|
-
source_info =
|
164
|
-
self._source, compact, verbose, root_indent + 1, **kwargs
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
161
|
+
source_info = utils.format(
|
162
|
+
self._source, compact, verbose, root_indent + 1, **kwargs
|
163
|
+
)
|
164
|
+
source_str = utils.RawText(f'{source_info} at 0x{id(self._source):8x}')
|
165
|
+
|
166
|
+
return utils.kvlist_str(
|
167
|
+
[
|
168
|
+
('tag', self._tag, None),
|
169
|
+
('source', source_str, None),
|
170
|
+
],
|
171
|
+
label=self.__class__.__name__,
|
172
|
+
compact=compact,
|
173
|
+
verbose=verbose,
|
174
|
+
root_indent=root_indent,
|
172
175
|
)
|
173
176
|
|
174
177
|
def __eq__(self, other: Any) -> bool:
|
@@ -92,11 +92,13 @@ class OriginTest(unittest.TestCase):
|
|
92
92
|
def test_format(self):
|
93
93
|
a = Dict(a=1)
|
94
94
|
o = Origin(None, '__init__')
|
95
|
-
self.assertEqual(o.format(), 'Origin(tag=\'__init__\')')
|
95
|
+
self.assertEqual(o.format(compact=True), 'Origin(tag=\'__init__\')')
|
96
96
|
|
97
97
|
o = Origin('/path/to/file', 'load')
|
98
98
|
self.assertEqual(
|
99
|
-
o.format(),
|
99
|
+
o.format(compact=False),
|
100
|
+
"Origin(\n tag='load',\n source='/path/to/file'\n)"
|
101
|
+
)
|
100
102
|
|
101
103
|
o = Origin(a, 'builder')
|
102
104
|
self.assertEqual(
|
@@ -14,8 +14,8 @@
|
|
14
14
|
"""Interfaces for pure symbolic objects."""
|
15
15
|
|
16
16
|
from typing import Any, Callable, Optional, Tuple
|
17
|
-
from pyglove.core import object_utils
|
18
17
|
from pyglove.core import typing as pg_typing
|
18
|
+
from pyglove.core import utils
|
19
19
|
|
20
20
|
|
21
21
|
class PureSymbolic(pg_typing.CustomTyping):
|
@@ -37,11 +37,12 @@ class PureSymbolic(pg_typing.CustomTyping):
|
|
37
37
|
|
38
38
|
def custom_apply(
|
39
39
|
self,
|
40
|
-
path:
|
40
|
+
path: utils.KeyPath,
|
41
41
|
value_spec: pg_typing.ValueSpec,
|
42
42
|
allow_partial: bool,
|
43
43
|
child_transform: Optional[
|
44
|
-
Callable[[
|
44
|
+
Callable[[utils.KeyPath, pg_typing.Field, Any], Any]
|
45
|
+
] = None,
|
45
46
|
) -> Tuple[bool, Any]:
|
46
47
|
"""Custom apply on a value based on its original value spec.
|
47
48
|
|
pyglove/core/symbolic/ref.py
CHANGED
@@ -13,15 +13,31 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
"""Symbolic reference."""
|
15
15
|
|
16
|
-
import
|
17
|
-
|
18
|
-
from
|
16
|
+
import functools
|
17
|
+
import typing
|
18
|
+
from typing import Any, Callable, List, Optional, Tuple, Type
|
19
19
|
from pyglove.core import typing as pg_typing
|
20
|
+
from pyglove.core import utils
|
20
21
|
from pyglove.core.symbolic import base
|
21
|
-
from pyglove.core.symbolic
|
22
|
+
from pyglove.core.symbolic import object as pg_object
|
23
|
+
from pyglove.core.views.html import tree_view
|
22
24
|
|
23
25
|
|
24
|
-
class
|
26
|
+
class RefMeta(pg_object.ObjectMeta):
|
27
|
+
"""Metaclass for Ref."""
|
28
|
+
|
29
|
+
def __getitem__(cls, type_arg: Type[Any]) -> Any: # pylint: disable=no-self-argument
|
30
|
+
if typing.TYPE_CHECKING:
|
31
|
+
return type_arg
|
32
|
+
return pg_typing.Object(type_arg, transform=Ref)
|
33
|
+
|
34
|
+
|
35
|
+
class Ref(
|
36
|
+
pg_object.Object,
|
37
|
+
base.Inferential,
|
38
|
+
tree_view.HtmlTreeView.Extension,
|
39
|
+
metaclass=RefMeta
|
40
|
+
):
|
25
41
|
"""Symbolic reference.
|
26
42
|
|
27
43
|
When adding a symbolic node to a symbolic tree, it undergoes a copy operation
|
@@ -80,11 +96,11 @@ class Ref(Object, base.Inferential):
|
|
80
96
|
|
81
97
|
def __new__(cls, value: Any, **kwargs):
|
82
98
|
del kwargs
|
83
|
-
if isinstance(value, (
|
84
|
-
return
|
85
|
-
return
|
99
|
+
if isinstance(value, (base.Symbolic, list, dict)):
|
100
|
+
return object.__new__(cls)
|
101
|
+
return value
|
86
102
|
|
87
|
-
@
|
103
|
+
@utils.explicit_method_override
|
88
104
|
def __init__(self, value: Any, **kwargs) -> None:
|
89
105
|
super().__init__(**kwargs)
|
90
106
|
if isinstance(value, Ref):
|
@@ -111,12 +127,13 @@ class Ref(Object, base.Inferential):
|
|
111
127
|
|
112
128
|
def custom_apply(
|
113
129
|
self,
|
114
|
-
path:
|
130
|
+
path: utils.KeyPath,
|
115
131
|
value_spec: pg_typing.ValueSpec,
|
116
132
|
allow_partial: bool = False,
|
117
|
-
child_transform: Optional[
|
118
|
-
[
|
119
|
-
|
133
|
+
child_transform: Optional[
|
134
|
+
Callable[[utils.KeyPath, pg_typing.Field, Any], Any]
|
135
|
+
] = None,
|
136
|
+
) -> Tuple[bool, Any]:
|
120
137
|
"""Validate candidates during value_spec binding time."""
|
121
138
|
del child_transform
|
122
139
|
# Check if the field being assigned could accept the referenced value.
|
@@ -135,7 +152,9 @@ class Ref(Object, base.Inferential):
|
|
135
152
|
def sym_eq(self, other: Any) -> bool:
|
136
153
|
return isinstance(other, Ref) and self.value is other.value
|
137
154
|
|
138
|
-
def sym_jsonify(self, **kwargs: Any) -> Any:
|
155
|
+
def sym_jsonify(self, *, save_ref_value: bool = False, **kwargs: Any) -> Any:
|
156
|
+
if save_ref_value:
|
157
|
+
return base.to_json(self._value, save_ref_value=save_ref_value, **kwargs)
|
139
158
|
raise TypeError(f'{self!r} cannot be serialized at the moment.')
|
140
159
|
|
141
160
|
def __getstate__(self):
|
@@ -146,24 +165,68 @@ class Ref(Object, base.Inferential):
|
|
146
165
|
compact: bool = False,
|
147
166
|
verbose: bool = False,
|
148
167
|
root_indent: int = 0,
|
149
|
-
*,
|
150
|
-
markdown: bool = False,
|
151
168
|
**kwargs: Any,
|
152
169
|
) -> str:
|
153
|
-
value_str =
|
170
|
+
value_str = utils.format(
|
154
171
|
self._value,
|
155
|
-
compact=compact,
|
172
|
+
compact=compact,
|
173
|
+
verbose=verbose,
|
174
|
+
root_indent=root_indent + 1,
|
175
|
+
)
|
156
176
|
if compact:
|
157
|
-
|
177
|
+
return f'{self.__class__.__name__}({value_str})'
|
158
178
|
else:
|
159
|
-
|
179
|
+
return (
|
160
180
|
f'{self.__class__.__name__}(\n'
|
161
181
|
+ ' ' * (root_indent + 1)
|
162
182
|
+ f'value = {value_str}\n'
|
163
183
|
+ ' ' * root_indent
|
164
184
|
+ ')'
|
165
185
|
)
|
166
|
-
|
186
|
+
|
187
|
+
def _html_tree_view_content(
|
188
|
+
self,
|
189
|
+
*,
|
190
|
+
view: tree_view.HtmlTreeView,
|
191
|
+
**kwargs: Any) -> tree_view.Html:
|
192
|
+
"""Overrides `_html_content` to render the referenced value."""
|
193
|
+
return view.content(self._value, **kwargs)
|
194
|
+
|
195
|
+
def _html_tree_view_summary(
|
196
|
+
self,
|
197
|
+
*,
|
198
|
+
view: tree_view.HtmlTreeView,
|
199
|
+
title: Optional[str] = None,
|
200
|
+
**kwargs: Any) -> Optional[tree_view.Html]:
|
201
|
+
"""Overrides `_html_content` to render the referenced value."""
|
202
|
+
return view.summary(
|
203
|
+
self,
|
204
|
+
title=title or f'{type(self._value).__name__}(...)',
|
205
|
+
**kwargs
|
206
|
+
)
|
207
|
+
|
208
|
+
@classmethod
|
209
|
+
@functools.cache
|
210
|
+
def _html_tree_view_config(cls) -> dict[str, Any]:
|
211
|
+
return tree_view.HtmlTreeView.get_kwargs(
|
212
|
+
super()._html_tree_view_config(),
|
213
|
+
dict(
|
214
|
+
css_classes=['ref'],
|
215
|
+
)
|
216
|
+
)
|
217
|
+
|
218
|
+
@classmethod
|
219
|
+
@functools.cache
|
220
|
+
def _html_tree_view_css_styles(cls) -> List[str]:
|
221
|
+
return super()._html_tree_view_css_styles() + [
|
222
|
+
"""
|
223
|
+
/* Ref styles. */
|
224
|
+
.ref.summary-title::before {
|
225
|
+
content: 'ref: ';
|
226
|
+
color: #aaa;
|
227
|
+
}
|
228
|
+
"""
|
229
|
+
]
|
167
230
|
|
168
231
|
|
169
232
|
def maybe_ref(value: Any) -> Optional[Ref]:
|
@@ -172,3 +235,27 @@ def maybe_ref(value: Any) -> Optional[Ref]:
|
|
172
235
|
if value.sym_parent is None:
|
173
236
|
return value
|
174
237
|
return Ref(value)
|
238
|
+
|
239
|
+
|
240
|
+
def deref(value: base.Symbolic, recursive: bool = False) -> Any:
|
241
|
+
"""Dereferences a symbolic value that may contain pg.Ref.
|
242
|
+
|
243
|
+
Args:
|
244
|
+
value: The input symbolic value.
|
245
|
+
recursive: If True, dereference `pg.Ref` in the entire tree. Otherwise
|
246
|
+
Only dereference the root node.
|
247
|
+
|
248
|
+
Returns:
|
249
|
+
The dereferenced root, or dereferenced tree if recursive is True.
|
250
|
+
"""
|
251
|
+
if isinstance(value, Ref):
|
252
|
+
value = value.value
|
253
|
+
|
254
|
+
if recursive:
|
255
|
+
def _deref(k, v, p):
|
256
|
+
del k, p
|
257
|
+
if isinstance(v, Ref):
|
258
|
+
return deref(v.value, recursive=True)
|
259
|
+
return v
|
260
|
+
return value.rebind(_deref, raise_on_no_change=False)
|
261
|
+
return value
|
@@ -16,11 +16,13 @@
|
|
16
16
|
import copy
|
17
17
|
import inspect
|
18
18
|
import pickle
|
19
|
+
import typing
|
19
20
|
from typing import Any
|
20
21
|
import unittest
|
21
22
|
|
22
23
|
from pyglove.core import typing as pg_typing
|
23
24
|
from pyglove.core.symbolic import ref
|
25
|
+
from pyglove.core.symbolic.base import contains
|
24
26
|
from pyglove.core.symbolic.dict import Dict
|
25
27
|
from pyglove.core.symbolic.object import Object
|
26
28
|
|
@@ -111,6 +113,11 @@ class RefTest(unittest.TestCase):
|
|
111
113
|
TypeError, '.* cannot be serialized at the moment'):
|
112
114
|
ref.Ref(A(1)).to_json()
|
113
115
|
|
116
|
+
self.assertEqual(
|
117
|
+
ref.Ref(A(1)).to_json(save_ref_value=True),
|
118
|
+
A(1).to_json()
|
119
|
+
)
|
120
|
+
|
114
121
|
def test_pickle(self):
|
115
122
|
with self.assertRaisesRegex(
|
116
123
|
TypeError, '.* cannot be pickled at the moment'):
|
@@ -126,6 +133,92 @@ class RefTest(unittest.TestCase):
|
|
126
133
|
self.assertIsInstance(x, ref.Ref)
|
127
134
|
self.assertIs(x.value, a)
|
128
135
|
|
136
|
+
def test_deref(self):
|
137
|
+
class Foo(Object):
|
138
|
+
x: int
|
139
|
+
y: Any
|
140
|
+
|
141
|
+
# Deref on non-ref value.
|
142
|
+
a = Foo(1, 2)
|
143
|
+
self.assertIs(ref.deref(a), a)
|
144
|
+
self.assertEqual(ref.deref(a), Foo(1, 2))
|
145
|
+
|
146
|
+
# Deref top-level Ref.
|
147
|
+
a = ref.Ref(Foo(1, 2))
|
148
|
+
self.assertIs(ref.deref(a), a.value)
|
149
|
+
|
150
|
+
a = ref.Ref(Foo(1, ref.Ref(Foo(2, ref.Ref(Foo(3, 4))))))
|
151
|
+
self.assertTrue(contains(a, type=ref.Ref))
|
152
|
+
a_prime = ref.deref(a)
|
153
|
+
self.assertIs(a_prime, a.value)
|
154
|
+
self.assertIsInstance(a_prime.sym_getattr('y'), ref.Ref)
|
155
|
+
self.assertIsInstance(a_prime.y.sym_getattr('y'), ref.Ref)
|
156
|
+
|
157
|
+
# Deref entire tree.
|
158
|
+
a = ref.Ref(Foo(1, ref.Ref(Foo(2, ref.Ref(Foo(3, 4))))))
|
159
|
+
self.assertTrue(contains(a, type=ref.Ref))
|
160
|
+
a_prime = ref.deref(a, recursive=True)
|
161
|
+
self.assertIs(a_prime, a.value)
|
162
|
+
self.assertFalse(contains(a_prime, type=ref.Ref))
|
163
|
+
|
164
|
+
def test_to_html(self):
|
165
|
+
|
166
|
+
def assert_style(html, expected):
|
167
|
+
expected = inspect.cleandoc(expected).strip()
|
168
|
+
actual = html.style_section.strip()
|
169
|
+
if expected not in actual:
|
170
|
+
print(actual)
|
171
|
+
self.assertIn(expected, actual)
|
172
|
+
|
173
|
+
def assert_content(html, expected):
|
174
|
+
expected = inspect.cleandoc(expected).strip()
|
175
|
+
actual = html.content.strip()
|
176
|
+
if actual != expected:
|
177
|
+
print(actual)
|
178
|
+
self.assertEqual(actual.strip(), expected)
|
179
|
+
|
180
|
+
class Foo(Object):
|
181
|
+
x: Any
|
182
|
+
|
183
|
+
assert_style(
|
184
|
+
Foo(ref.Ref(Foo(1))).to_html(),
|
185
|
+
"""
|
186
|
+
/* Ref styles. */
|
187
|
+
.ref.summary-title::before {
|
188
|
+
content: 'ref: ';
|
189
|
+
color: #aaa;
|
190
|
+
}
|
191
|
+
"""
|
192
|
+
)
|
193
|
+
assert_content(
|
194
|
+
Foo(ref.Ref(Foo(1))).to_html(
|
195
|
+
extra_flags=dict(
|
196
|
+
use_inferred=False,
|
197
|
+
),
|
198
|
+
enable_summary_tooltip=False,
|
199
|
+
enable_key_tooltip=False,
|
200
|
+
),
|
201
|
+
"""
|
202
|
+
<details open class="pyglove foo"><summary><div class="summary-title">Foo(...)</div></summary><div class="complex-value foo"><details class="pyglove ref"><summary><div class="summary-name ref">x</div><div class="summary-title ref">Foo(...)</div></summary><div class="complex-value foo"><details open class="pyglove int"><summary><div class="summary-name">x</div><div class="summary-title">int</div></summary><span class="simple-value int">1</span></details></div></details></div></details>
|
203
|
+
"""
|
204
|
+
)
|
205
|
+
|
206
|
+
def test_annotation(self):
|
207
|
+
typing.TYPE_CHECKING = True
|
208
|
+
assert ref.Ref[ValueError] is ValueError
|
209
|
+
typing.TYPE_CHECKING = False
|
210
|
+
|
211
|
+
class Bar(Object):
|
212
|
+
x: int
|
213
|
+
|
214
|
+
class Foo(Object):
|
215
|
+
y: ref.Ref[Bar]
|
216
|
+
|
217
|
+
f = Foo(Bar(1))
|
218
|
+
self.assertIsInstance(f.sym_getattr('y'), ref.Ref)
|
219
|
+
self.assertEqual(f.y.sym_path, '')
|
220
|
+
self.assertIsInstance(ref.Ref[Bar](1), ref.Ref)
|
221
|
+
|
129
222
|
|
130
223
|
if __name__ == '__main__':
|
131
224
|
unittest.main()
|
@@ -73,8 +73,16 @@ class SymbolizeFunctionsTest(unittest.TestCase):
|
|
73
73
|
self.assertEqual(
|
74
74
|
f.__signature__.args,
|
75
75
|
[
|
76
|
-
|
77
|
-
|
76
|
+
pg_typing.Argument(
|
77
|
+
'x',
|
78
|
+
pg_typing.Argument.Kind.POSITIONAL_OR_KEYWORD,
|
79
|
+
pg_typing.Int()
|
80
|
+
),
|
81
|
+
pg_typing.Argument(
|
82
|
+
'y',
|
83
|
+
pg_typing.Argument.Kind.POSITIONAL_OR_KEYWORD,
|
84
|
+
pg_typing.Str()
|
85
|
+
),
|
78
86
|
],
|
79
87
|
)
|
80
88
|
self.assertEqual(f.__signature__.return_value, pg_typing.Int())
|
@@ -21,8 +21,8 @@ from typing import Any, Callable, Dict, List, Optional, Sequence
|
|
21
21
|
|
22
22
|
from pyglove.core import geno
|
23
23
|
from pyglove.core import logging
|
24
|
-
from pyglove.core import object_utils
|
25
24
|
from pyglove.core import symbolic
|
25
|
+
from pyglove.core import utils
|
26
26
|
from pyglove.core.tuning import backend
|
27
27
|
from pyglove.core.tuning.early_stopping import EarlyStoppingPolicy
|
28
28
|
from pyglove.core.tuning.protocols import Feedback
|
@@ -278,7 +278,7 @@ class _InMemoryResult(Result):
|
|
278
278
|
('step', self._best_trial.final_measurement.step),
|
279
279
|
('dna', self._best_trial.dna.format(compact=True))
|
280
280
|
])
|
281
|
-
return
|
281
|
+
return utils.format(json_repr, compact, False, root_indent, **kwargs)
|
282
282
|
|
283
283
|
|
284
284
|
@backend.add_backend('in-memory')
|
pyglove/core/tuning/protocols.py
CHANGED
@@ -22,9 +22,9 @@ from typing import Any, Dict, List, Optional, Sequence, Tuple, Type, Union
|
|
22
22
|
|
23
23
|
from pyglove.core import geno
|
24
24
|
from pyglove.core import logging
|
25
|
-
from pyglove.core import object_utils
|
26
25
|
from pyglove.core import symbolic
|
27
26
|
from pyglove.core import typing as pg_typing
|
27
|
+
from pyglove.core import utils
|
28
28
|
|
29
29
|
|
30
30
|
class _DataEntity(symbolic.Object):
|
@@ -116,7 +116,7 @@ class Trial(_DataEntity):
|
|
116
116
|
return tuple(metric_values) if len(metric_values) > 1 else metric_values[0]
|
117
117
|
|
118
118
|
|
119
|
-
class Result(
|
119
|
+
class Result(utils.Formattable):
|
120
120
|
"""Interface for tuning result."""
|
121
121
|
|
122
122
|
@property
|
@@ -416,7 +416,7 @@ class Feedback(metaclass=abc.ABCMeta):
|
|
416
416
|
error_stack = traceback.format_exc()
|
417
417
|
logging.warning('Skipping trial on unhandled exception: %s', error_stack)
|
418
418
|
self.skip(error_stack)
|
419
|
-
return
|
419
|
+
return utils.catch_errors(exceptions, skip_on_exception)
|
420
420
|
|
421
421
|
@contextlib.contextmanager
|
422
422
|
def ignore_race_condition(self):
|
@@ -173,15 +173,15 @@ class SamplingTest(unittest.TestCase):
|
|
173
173
|
_, f = next(sample)
|
174
174
|
with self.assertRaisesRegex(
|
175
175
|
ValueError, 'bcd'):
|
176
|
-
with f.skip_on_exceptions(((Exception, '
|
176
|
+
with f.skip_on_exceptions(((Exception, 'abc'),)):
|
177
177
|
# should skip.
|
178
178
|
raise ValueError('bcd')
|
179
179
|
self.assertEqual(algo.num_proposals, 7)
|
180
180
|
self.assertEqual(algo.num_feedbacks, 1)
|
181
181
|
|
182
182
|
_, f = next(sample)
|
183
|
-
with f.skip_on_exceptions(((ValueError, '
|
184
|
-
(ValueError, '
|
183
|
+
with f.skip_on_exceptions(((ValueError, 'abc'),
|
184
|
+
(ValueError, 'cd'),
|
185
185
|
KeyError)):
|
186
186
|
# should skip.
|
187
187
|
raise ValueError('bcd')
|
pyglove/core/typing/__init__.py
CHANGED
@@ -303,15 +303,14 @@ from pyglove.core.typing.typed_missing import MissingValue # Typed.
|
|
303
303
|
from pyglove.core.typing.class_schema import KeySpec
|
304
304
|
from pyglove.core.typing.class_schema import ValueSpec
|
305
305
|
from pyglove.core.typing.class_schema import Field
|
306
|
+
from pyglove.core.typing.class_schema import FieldKeyDef
|
307
|
+
from pyglove.core.typing.class_schema import FieldValueDef
|
308
|
+
from pyglove.core.typing.class_schema import FieldDef
|
306
309
|
from pyglove.core.typing.class_schema import Schema
|
307
310
|
from pyglove.core.typing.class_schema import create_field
|
308
311
|
from pyglove.core.typing.class_schema import create_schema
|
309
312
|
from pyglove.core.typing.class_schema import ForwardRef
|
310
313
|
|
311
|
-
# Class schema helpers.
|
312
|
-
from pyglove.core.typing.class_schema_utils import get_arg_fields
|
313
|
-
from pyglove.core.typing.class_schema_utils import ensure_value_spec
|
314
|
-
|
315
314
|
# Concrete key specifications.
|
316
315
|
from pyglove.core.typing.key_specs import ConstStrKey
|
317
316
|
from pyglove.core.typing.key_specs import NonConstKey
|
@@ -337,6 +336,8 @@ from pyglove.core.typing.value_specs import Type
|
|
337
336
|
from pyglove.core.typing.value_specs import Union
|
338
337
|
from pyglove.core.typing.value_specs import Any
|
339
338
|
|
339
|
+
from pyglove.core.typing.value_specs import ensure_value_spec
|
340
|
+
|
340
341
|
# Generic type aliases.
|
341
342
|
from pyglove.core.typing.value_specs import GenericMeta
|
342
343
|
from pyglove.core.typing.value_specs import Generic
|
@@ -361,6 +362,7 @@ from pyglove.core.typing.type_conversion import get_json_value_converter
|
|
361
362
|
# Inspect helpers.
|
362
363
|
from pyglove.core.typing.inspect import is_subclass
|
363
364
|
from pyglove.core.typing.inspect import is_instance
|
365
|
+
from pyglove.core.typing.inspect import get_outer_class
|
364
366
|
from pyglove.core.typing.inspect import get_type
|
365
367
|
from pyglove.core.typing.inspect import get_type_args
|
366
368
|
from pyglove.core.typing.inspect import is_generic
|
@@ -377,9 +379,16 @@ from pyglove.core.typing.custom_typing import CustomTyping
|
|
377
379
|
from pyglove.core.typing.callable_signature import Argument
|
378
380
|
from pyglove.core.typing.callable_signature import CallableType
|
379
381
|
from pyglove.core.typing.callable_signature import Signature
|
380
|
-
from pyglove.core.typing.callable_signature import
|
382
|
+
from pyglove.core.typing.callable_signature import signature
|
383
|
+
from pyglove.core.typing.callable_signature import schema
|
384
|
+
|
385
|
+
# For backward compatibility.
|
386
|
+
get_signature = signature
|
381
387
|
|
382
388
|
# Callable extensions.
|
389
|
+
from pyglove.core.typing.callable_ext import PresetArgValue
|
390
|
+
from pyglove.core.typing.callable_ext import enable_preset_args
|
391
|
+
from pyglove.core.typing.callable_ext import preset_args
|
383
392
|
from pyglove.core.typing.callable_ext import CallableWithOptionalKeywordArgs
|
384
393
|
|
385
394
|
# PyType support.
|