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.
Files changed (145) hide show
  1. pyglove/core/__init__.py +54 -20
  2. pyglove/core/coding/__init__.py +42 -0
  3. pyglove/core/coding/errors.py +111 -0
  4. pyglove/core/coding/errors_test.py +98 -0
  5. pyglove/core/coding/execution.py +309 -0
  6. pyglove/core/coding/execution_test.py +333 -0
  7. pyglove/core/{object_utils/codegen.py → coding/function_generation.py} +10 -4
  8. pyglove/core/{object_utils/codegen_test.py → coding/function_generation_test.py} +5 -7
  9. pyglove/core/coding/parsing.py +153 -0
  10. pyglove/core/coding/parsing_test.py +150 -0
  11. pyglove/core/coding/permissions.py +100 -0
  12. pyglove/core/coding/permissions_test.py +93 -0
  13. pyglove/core/geno/base.py +54 -41
  14. pyglove/core/geno/base_test.py +2 -4
  15. pyglove/core/geno/categorical.py +37 -28
  16. pyglove/core/geno/custom.py +19 -16
  17. pyglove/core/geno/numerical.py +20 -17
  18. pyglove/core/geno/space.py +4 -5
  19. pyglove/core/hyper/base.py +6 -6
  20. pyglove/core/hyper/categorical.py +94 -55
  21. pyglove/core/hyper/custom.py +7 -7
  22. pyglove/core/hyper/custom_test.py +9 -10
  23. pyglove/core/hyper/derived.py +30 -22
  24. pyglove/core/hyper/derived_test.py +2 -4
  25. pyglove/core/hyper/dynamic_evaluation.py +5 -6
  26. pyglove/core/hyper/evolvable.py +57 -46
  27. pyglove/core/hyper/numerical.py +48 -24
  28. pyglove/core/hyper/numerical_test.py +9 -9
  29. pyglove/core/hyper/object_template.py +58 -46
  30. pyglove/core/io/__init__.py +1 -0
  31. pyglove/core/io/file_system.py +17 -7
  32. pyglove/core/io/file_system_test.py +2 -0
  33. pyglove/core/io/sequence.py +299 -0
  34. pyglove/core/io/sequence_test.py +124 -0
  35. pyglove/core/logging_test.py +0 -2
  36. pyglove/core/patching/object_factory.py +4 -4
  37. pyglove/core/patching/pattern_based.py +4 -4
  38. pyglove/core/patching/rule_based.py +17 -5
  39. pyglove/core/patching/rule_based_test.py +27 -4
  40. pyglove/core/symbolic/__init__.py +2 -7
  41. pyglove/core/symbolic/base.py +320 -183
  42. pyglove/core/symbolic/base_test.py +123 -19
  43. pyglove/core/symbolic/boilerplate.py +7 -13
  44. pyglove/core/symbolic/boilerplate_test.py +25 -23
  45. pyglove/core/symbolic/class_wrapper.py +48 -45
  46. pyglove/core/symbolic/class_wrapper_test.py +2 -2
  47. pyglove/core/symbolic/compounding.py +9 -15
  48. pyglove/core/symbolic/compounding_test.py +2 -4
  49. pyglove/core/symbolic/dict.py +154 -110
  50. pyglove/core/symbolic/dict_test.py +238 -130
  51. pyglove/core/symbolic/diff.py +199 -10
  52. pyglove/core/symbolic/diff_test.py +226 -0
  53. pyglove/core/symbolic/flags.py +1 -1
  54. pyglove/core/symbolic/functor.py +29 -26
  55. pyglove/core/symbolic/functor_test.py +102 -50
  56. pyglove/core/symbolic/inferred.py +2 -2
  57. pyglove/core/symbolic/list.py +81 -50
  58. pyglove/core/symbolic/list_test.py +119 -97
  59. pyglove/core/symbolic/object.py +225 -113
  60. pyglove/core/symbolic/object_test.py +320 -108
  61. pyglove/core/symbolic/origin.py +17 -14
  62. pyglove/core/symbolic/origin_test.py +4 -2
  63. pyglove/core/symbolic/pure_symbolic.py +4 -3
  64. pyglove/core/symbolic/ref.py +108 -21
  65. pyglove/core/symbolic/ref_test.py +93 -0
  66. pyglove/core/symbolic/symbolize_test.py +10 -2
  67. pyglove/core/tuning/local_backend.py +2 -2
  68. pyglove/core/tuning/protocols.py +3 -3
  69. pyglove/core/tuning/sample_test.py +3 -3
  70. pyglove/core/typing/__init__.py +14 -5
  71. pyglove/core/typing/annotation_conversion.py +43 -27
  72. pyglove/core/typing/annotation_conversion_test.py +23 -0
  73. pyglove/core/typing/callable_ext.py +241 -3
  74. pyglove/core/typing/callable_ext_test.py +255 -0
  75. pyglove/core/typing/callable_signature.py +510 -66
  76. pyglove/core/typing/callable_signature_test.py +619 -99
  77. pyglove/core/typing/class_schema.py +229 -154
  78. pyglove/core/typing/class_schema_test.py +149 -95
  79. pyglove/core/typing/custom_typing.py +5 -4
  80. pyglove/core/typing/inspect.py +63 -0
  81. pyglove/core/typing/inspect_test.py +39 -0
  82. pyglove/core/typing/key_specs.py +10 -11
  83. pyglove/core/typing/key_specs_test.py +7 -4
  84. pyglove/core/typing/type_conversion.py +4 -5
  85. pyglove/core/typing/type_conversion_test.py +12 -12
  86. pyglove/core/typing/typed_missing.py +6 -7
  87. pyglove/core/typing/typed_missing_test.py +7 -8
  88. pyglove/core/typing/value_specs.py +604 -362
  89. pyglove/core/typing/value_specs_test.py +328 -90
  90. pyglove/core/utils/__init__.py +164 -0
  91. pyglove/core/{object_utils → utils}/common_traits.py +3 -67
  92. pyglove/core/utils/common_traits_test.py +36 -0
  93. pyglove/core/{object_utils → utils}/docstr_utils.py +23 -0
  94. pyglove/core/{object_utils → utils}/docstr_utils_test.py +36 -4
  95. pyglove/core/{object_utils → utils}/error_utils.py +78 -9
  96. pyglove/core/{object_utils → utils}/error_utils_test.py +61 -5
  97. pyglove/core/utils/formatting.py +464 -0
  98. pyglove/core/utils/formatting_test.py +453 -0
  99. pyglove/core/{object_utils → utils}/hierarchical.py +23 -25
  100. pyglove/core/{object_utils → utils}/hierarchical_test.py +3 -5
  101. pyglove/core/{object_utils → utils}/json_conversion.py +177 -52
  102. pyglove/core/{object_utils → utils}/json_conversion_test.py +97 -16
  103. pyglove/core/{object_utils → utils}/missing.py +3 -3
  104. pyglove/core/{object_utils → utils}/missing_test.py +2 -4
  105. pyglove/core/utils/text_color.py +128 -0
  106. pyglove/core/utils/text_color_test.py +94 -0
  107. pyglove/core/{object_utils → utils}/thread_local_test.py +1 -3
  108. pyglove/core/utils/timing.py +236 -0
  109. pyglove/core/utils/timing_test.py +154 -0
  110. pyglove/core/{object_utils → utils}/value_location.py +275 -6
  111. pyglove/core/utils/value_location_test.py +707 -0
  112. pyglove/core/views/__init__.py +32 -0
  113. pyglove/core/views/base.py +804 -0
  114. pyglove/core/views/base_test.py +580 -0
  115. pyglove/core/views/html/__init__.py +27 -0
  116. pyglove/core/views/html/base.py +547 -0
  117. pyglove/core/views/html/base_test.py +830 -0
  118. pyglove/core/views/html/controls/__init__.py +35 -0
  119. pyglove/core/views/html/controls/base.py +275 -0
  120. pyglove/core/views/html/controls/label.py +207 -0
  121. pyglove/core/views/html/controls/label_test.py +157 -0
  122. pyglove/core/views/html/controls/progress_bar.py +183 -0
  123. pyglove/core/views/html/controls/progress_bar_test.py +97 -0
  124. pyglove/core/views/html/controls/tab.py +320 -0
  125. pyglove/core/views/html/controls/tab_test.py +87 -0
  126. pyglove/core/views/html/controls/tooltip.py +99 -0
  127. pyglove/core/views/html/controls/tooltip_test.py +99 -0
  128. pyglove/core/views/html/tree_view.py +1517 -0
  129. pyglove/core/views/html/tree_view_test.py +1461 -0
  130. {pyglove-0.4.5.dev20240319.dist-info → pyglove-0.4.5.dev202501140808.dist-info}/METADATA +18 -4
  131. pyglove-0.4.5.dev202501140808.dist-info/RECORD +214 -0
  132. {pyglove-0.4.5.dev20240319.dist-info → pyglove-0.4.5.dev202501140808.dist-info}/WHEEL +1 -1
  133. pyglove/core/object_utils/__init__.py +0 -154
  134. pyglove/core/object_utils/common_traits_test.py +0 -82
  135. pyglove/core/object_utils/formatting.py +0 -234
  136. pyglove/core/object_utils/formatting_test.py +0 -223
  137. pyglove/core/object_utils/value_location_test.py +0 -385
  138. pyglove/core/symbolic/schema_utils.py +0 -327
  139. pyglove/core/symbolic/schema_utils_test.py +0 -57
  140. pyglove/core/typing/class_schema_utils.py +0 -202
  141. pyglove/core/typing/class_schema_utils_test.py +0 -194
  142. pyglove-0.4.5.dev20240319.dist-info/RECORD +0 -185
  143. /pyglove/core/{object_utils → utils}/thread_local.py +0 -0
  144. {pyglove-0.4.5.dev20240319.dist-info → pyglove-0.4.5.dev202501140808.dist-info}/LICENSE +0 -0
  145. {pyglove-0.4.5.dev20240319.dist-info → pyglove-0.4.5.dev202501140808.dist-info}/top_level.txt +0 -0
@@ -16,11 +16,11 @@
16
16
  import traceback
17
17
  from typing import Any, Callable, List, Optional
18
18
 
19
- from pyglove.core import object_utils
19
+ from pyglove.core import utils
20
20
  from pyglove.core.symbolic import flags
21
21
 
22
22
 
23
- class Origin(object_utils.Formattable):
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 = object_utils.quote_if_str(self._source)
159
+ source_str = self._source
162
160
  else:
163
- source_info = object_utils.format(
164
- self._source, compact, verbose, root_indent + 1, **kwargs)
165
- source_str = f'{source_info} at 0x{id(self._source):8x}'
166
- details = object_utils.kvlist_str([
167
- ('tag', object_utils.quote_if_str(self._tag), None),
168
- ('source', source_str, None),
169
- ])
170
- return object_utils.maybe_markdown_quote(
171
- f'{self.__class__.__name__}({details})', markdown
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(), 'Origin(tag=\'load\', source=\'/path/to/file\')')
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: object_utils.KeyPath,
40
+ path: utils.KeyPath,
41
41
  value_spec: pg_typing.ValueSpec,
42
42
  allow_partial: bool,
43
43
  child_transform: Optional[
44
- Callable[[object_utils.KeyPath, pg_typing.Field, Any], Any]] = None
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
 
@@ -13,15 +13,31 @@
13
13
  # limitations under the License.
14
14
  """Symbolic reference."""
15
15
 
16
- import numbers
17
- from typing import Any, Callable, Optional, Tuple
18
- from pyglove.core import object_utils
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.object import Object
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 Ref(Object, base.Inferential):
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, (bool, numbers.Number, str)):
84
- return value
85
- return object.__new__(cls)
99
+ if isinstance(value, (base.Symbolic, list, dict)):
100
+ return object.__new__(cls)
101
+ return value
86
102
 
87
- @object_utils.explicit_method_override
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: object_utils.KeyPath,
130
+ path: utils.KeyPath,
115
131
  value_spec: pg_typing.ValueSpec,
116
132
  allow_partial: bool = False,
117
- child_transform: Optional[Callable[
118
- [object_utils.KeyPath, pg_typing.Field, Any], Any]] = None
119
- ) -> Tuple[bool, Any]:
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 = object_utils.format(
170
+ value_str = utils.format(
154
171
  self._value,
155
- compact=compact, verbose=verbose, root_indent=root_indent + 1)
172
+ compact=compact,
173
+ verbose=verbose,
174
+ root_indent=root_indent + 1,
175
+ )
156
176
  if compact:
157
- s = f'{self.__class__.__name__}({value_str})'
177
+ return f'{self.__class__.__name__}({value_str})'
158
178
  else:
159
- s = (
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
- return object_utils.maybe_markdown_quote(s, markdown)
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
- (pg_typing.Argument('x', pg_typing.Int())),
77
- (pg_typing.Argument('y', pg_typing.Str())),
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 object_utils.format(json_repr, compact, False, root_indent, **kwargs)
281
+ return utils.format(json_repr, compact, False, root_indent, **kwargs)
282
282
 
283
283
 
284
284
  @backend.add_backend('in-memory')
@@ -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(object_utils.Formattable):
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 object_utils.catch_errors(exceptions, skip_on_exception)
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, '.*a'),)):
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, '.*a'),
184
- (ValueError, '.*b'),
183
+ with f.skip_on_exceptions(((ValueError, 'abc'),
184
+ (ValueError, 'cd'),
185
185
  KeyError)):
186
186
  # should skip.
187
187
  raise ValueError('bcd')
@@ -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 get_signature
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.