pyglove 0.4.5.dev20240318__py3-none-any.whl → 0.4.5.dev202501132210__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.dev20240318.dist-info → pyglove-0.4.5.dev202501132210.dist-info}/METADATA +18 -4
  131. pyglove-0.4.5.dev202501132210.dist-info/RECORD +214 -0
  132. {pyglove-0.4.5.dev20240318.dist-info → pyglove-0.4.5.dev202501132210.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.dev20240318.dist-info/RECORD +0 -185
  143. /pyglove/core/{object_utils → utils}/thread_local.py +0 -0
  144. {pyglove-0.4.5.dev20240318.dist-info → pyglove-0.4.5.dev202501132210.dist-info}/LICENSE +0 -0
  145. {pyglove-0.4.5.dev20240318.dist-info → pyglove-0.4.5.dev202501132210.dist-info}/top_level.txt +0 -0
@@ -20,9 +20,9 @@ import types
20
20
  from typing import Any, Callable, List, Optional, Tuple, Union
21
21
 
22
22
  from pyglove.core import geno
23
- from pyglove.core import object_utils
24
23
  from pyglove.core import symbolic
25
24
  from pyglove.core import typing as pg_typing
25
+ from pyglove.core import utils
26
26
  from pyglove.core.hyper import custom
27
27
 
28
28
 
@@ -44,7 +44,7 @@ class MutationPoint:
44
44
  parent: The parent node of the mutation point.
45
45
  """
46
46
  mutation_type: 'MutationType'
47
- location: object_utils.KeyPath
47
+ location: utils.KeyPath
48
48
  old_value: Any
49
49
  parent: Optional[symbolic.Symbolic]
50
50
 
@@ -71,9 +71,9 @@ class Evolvable(custom.CustomHyper):
71
71
  mutation_points: List[MutationPoint] = []
72
72
  mutation_weights: List[float] = []
73
73
 
74
- def _choose_mutation_point(k: object_utils.KeyPath,
75
- v: Any,
76
- p: Optional[symbolic.Symbolic]):
74
+ def _choose_mutation_point(
75
+ k: utils.KeyPath, v: Any, p: Optional[symbolic.Symbolic]
76
+ ):
77
77
  """Visiting function for a symbolic node."""
78
78
  def _add_point(mt: MutationType, k=k, v=v, p=p):
79
79
  mutation_points.append(MutationPoint(mt, k, v, p))
@@ -98,10 +98,9 @@ class Evolvable(custom.CustomHyper):
98
98
  reached_min_size = False
99
99
 
100
100
  for i, cv in enumerate(v):
101
- ck = object_utils.KeyPath(i, parent=k)
101
+ ck = utils.KeyPath(i, parent=k)
102
102
  if not reached_max_size:
103
- _add_point(MutationType.INSERT,
104
- k=ck, v=object_utils.MISSING_VALUE, p=v)
103
+ _add_point(MutationType.INSERT, k=ck, v=utils.MISSING_VALUE, p=v)
105
104
 
106
105
  if not reached_min_size:
107
106
  _add_point(MutationType.DELETE, k=ck, v=cv, p=v)
@@ -109,10 +108,12 @@ class Evolvable(custom.CustomHyper):
109
108
  # Replace type and value will be added in traverse.
110
109
  symbolic.traverse(cv, _choose_mutation_point, root_path=ck, parent=v)
111
110
  if not reached_max_size and i == len(v) - 1:
112
- _add_point(MutationType.INSERT,
113
- k=object_utils.KeyPath(i + 1, parent=k),
114
- v=object_utils.MISSING_VALUE,
115
- p=v)
111
+ _add_point(
112
+ MutationType.INSERT,
113
+ k=utils.KeyPath(i + 1, parent=k),
114
+ v=utils.MISSING_VALUE,
115
+ p=v,
116
+ )
116
117
  return symbolic.TraverseAction.CONTINUE
117
118
  return symbolic.TraverseAction.ENTER
118
119
 
@@ -157,7 +158,7 @@ class Evolvable(custom.CustomHyper):
157
158
  point.location, point.old_value, point.parent)
158
159
  elif point.mutation_type == MutationType.INSERT:
159
160
  assert isinstance(point.parent, symbolic.List), point
160
- assert point.old_value == object_utils.MISSING_VALUE, point
161
+ assert point.old_value == utils.MISSING_VALUE, point
161
162
  assert isinstance(point.location.key, int), point
162
163
  with symbolic.allow_writable_accessors():
163
164
  point.parent.insert(
@@ -175,24 +176,31 @@ class Evolvable(custom.CustomHyper):
175
176
  # We defer members declaration for Evolvable since the weights will reference
176
177
  # the definition of MutationType.
177
178
  symbolic.members([
178
- ('initial_value', pg_typing.Object(symbolic.Symbolic),
179
- 'Symbolic value to involve.'),
180
- ('node_transform', pg_typing.Callable(
181
- [],
182
- returns=pg_typing.Any()),
183
- ''),
184
- ('weights', pg_typing.Callable(
185
- [
186
- pg_typing.Object(MutationType),
187
- pg_typing.Object(object_utils.KeyPath),
188
- pg_typing.Any().noneable(),
189
- pg_typing.Object(symbolic.Symbolic)
190
- ], returns=pg_typing.Float(min_value=0.0)).noneable(),
191
- ('An optional callable object that returns the unnormalized (e.g. '
192
- 'the sum of all probabilities do not have to sum to 1.0) mutation '
193
- 'probabilities for all the nodes in the symbolic tree, based on '
194
- '(mutation type, location, old value, parent node). If None, all the '
195
- 'locations and mutation types will be sampled uniformly.')),
179
+ (
180
+ 'initial_value',
181
+ pg_typing.Object(symbolic.Symbolic),
182
+ 'Symbolic value to involve.',
183
+ ),
184
+ ('node_transform', pg_typing.Callable([], returns=pg_typing.Any()), ''),
185
+ (
186
+ 'weights',
187
+ pg_typing.Callable(
188
+ [
189
+ pg_typing.Object(MutationType),
190
+ pg_typing.Object(utils.KeyPath),
191
+ pg_typing.Any().noneable(),
192
+ pg_typing.Object(symbolic.Symbolic),
193
+ ],
194
+ returns=pg_typing.Float(min_value=0.0),
195
+ ).noneable(),
196
+ (
197
+ 'An optional callable object that returns the unnormalized (e.g.'
198
+ ' the sum of all probabilities do not have to sum to 1.0) mutation'
199
+ ' probabilities for all the nodes in the symbolic tree, based on'
200
+ ' (mutation type, location, old value, parent node). If None, all'
201
+ ' the locations and mutation types will be sampled uniformly.'
202
+ ),
203
+ ),
196
204
  ])(Evolvable)
197
205
 
198
206
 
@@ -200,25 +208,28 @@ def evolve(
200
208
  initial_value: symbolic.Symbolic,
201
209
  node_transform: Callable[
202
210
  [
203
- object_utils.KeyPath, # Location.
204
- Any, # Old value.
205
- # pg.MISSING_VALUE for insertion.
206
- symbolic.Symbolic, # Parent node.
211
+ utils.KeyPath, # Location.
212
+ Any, # Old value.
213
+ # pg.MISSING_VALUE for insertion.
214
+ symbolic.Symbolic, # Parent node.
207
215
  ],
208
- Any # Replacement.
216
+ Any, # Replacement.
209
217
  ],
210
218
  *,
211
- weights: Optional[Callable[
212
- [
213
- MutationType, # Mutation type.
214
- object_utils.KeyPath, # Location.
215
- Any, # Value.
216
- symbolic.Symbolic, # Parent.
217
- ],
218
- float # Mutation weight.
219
- ]] = None, # pylint: disable=bad-whitespace
219
+ weights: Optional[
220
+ Callable[
221
+ [
222
+ MutationType, # Mutation type.
223
+ utils.KeyPath, # Location.
224
+ Any, # Value.
225
+ symbolic.Symbolic, # Parent.
226
+ ],
227
+ float, # Mutation weight.
228
+ ]
229
+ ] = None, # pylint: disable=bad-whitespace
220
230
  name: Optional[str] = None,
221
- hints: Optional[Any] = None) -> Evolvable:
231
+ hints: Optional[Any] = None
232
+ ) -> Evolvable:
222
233
  """An evolvable symbolic value.
223
234
 
224
235
  Example::
@@ -17,9 +17,9 @@ import typing
17
17
  from typing import Any, Callable, Optional, Tuple
18
18
 
19
19
  from pyglove.core import geno
20
- from pyglove.core import object_utils
21
20
  from pyglove.core import symbolic
22
21
  from pyglove.core import typing as pg_typing
22
+ from pyglove.core import utils
23
23
  from pyglove.core.hyper import base
24
24
 
25
25
 
@@ -62,8 +62,7 @@ class Float(base.HyperPrimitive):
62
62
  f'\'min_value\' must be positive when `scale` is {self.scale!r}. '
63
63
  f'encountered: {self.min_value}.')
64
64
 
65
- def dna_spec(self,
66
- location: Optional[object_utils.KeyPath] = None) -> geno.Float:
65
+ def dna_spec(self, location: Optional[utils.KeyPath] = None) -> geno.Float:
67
66
  """Returns corresponding DNASpec."""
68
67
  return geno.Float(
69
68
  min_value=self.min_value,
@@ -71,55 +70,74 @@ class Float(base.HyperPrimitive):
71
70
  scale=self.scale,
72
71
  hints=self.hints,
73
72
  name=self.name,
74
- location=location or object_utils.KeyPath())
73
+ location=location or utils.KeyPath(),
74
+ )
75
75
 
76
76
  def _decode(self) -> float:
77
77
  """Decode a DNA into a float value."""
78
78
  dna = self._dna
79
79
  if not isinstance(dna.value, float):
80
80
  raise ValueError(
81
- object_utils.message_on_path(
82
- f'Expect float value. Encountered: {dna.value}.', self.sym_path))
81
+ utils.message_on_path(
82
+ f'Expect float value. Encountered: {dna.value}.', self.sym_path
83
+ )
84
+ )
83
85
  if dna.value < self.min_value:
84
86
  raise ValueError(
85
- object_utils.message_on_path(
87
+ utils.message_on_path(
86
88
  f'DNA value should be no less than {self.min_value}. '
87
- f'Encountered {dna.value}.', self.sym_path))
89
+ f'Encountered {dna.value}.',
90
+ self.sym_path,
91
+ )
92
+ )
88
93
 
89
94
  if dna.value > self.max_value:
90
95
  raise ValueError(
91
- object_utils.message_on_path(
96
+ utils.message_on_path(
92
97
  f'DNA value should be no greater than {self.max_value}. '
93
- f'Encountered {dna.value}.', self.sym_path))
98
+ f'Encountered {dna.value}.',
99
+ self.sym_path,
100
+ )
101
+ )
94
102
  return dna.value
95
103
 
96
104
  def encode(self, value: float) -> geno.DNA:
97
105
  """Encode a float value into a DNA."""
98
106
  if not isinstance(value, float):
99
107
  raise ValueError(
100
- object_utils.message_on_path(
108
+ utils.message_on_path(
101
109
  f'Value should be float to be encoded for {self!r}. '
102
- f'Encountered {value}.', self.sym_path))
110
+ f'Encountered {value}.',
111
+ self.sym_path,
112
+ )
113
+ )
103
114
  if value < self.min_value:
104
115
  raise ValueError(
105
- object_utils.message_on_path(
116
+ utils.message_on_path(
106
117
  f'Value should be no less than {self.min_value}. '
107
- f'Encountered {value}.', self.sym_path))
118
+ f'Encountered {value}.',
119
+ self.sym_path,
120
+ )
121
+ )
108
122
  if value > self.max_value:
109
123
  raise ValueError(
110
- object_utils.message_on_path(
124
+ utils.message_on_path(
111
125
  f'Value should be no greater than {self.max_value}. '
112
- f'Encountered {value}.', self.sym_path))
126
+ f'Encountered {value}.',
127
+ self.sym_path,
128
+ )
129
+ )
113
130
  return geno.DNA(value)
114
131
 
115
132
  def custom_apply(
116
133
  self,
117
- path: object_utils.KeyPath,
134
+ path: utils.KeyPath,
118
135
  value_spec: pg_typing.ValueSpec,
119
136
  allow_partial: bool = False,
120
- child_transform: Optional[Callable[
121
- [object_utils.KeyPath, pg_typing.Field, Any], Any]] = None
122
- ) -> Tuple[bool, 'Float']:
137
+ child_transform: Optional[
138
+ Callable[[utils.KeyPath, pg_typing.Field, Any], Any]
139
+ ] = None,
140
+ ) -> Tuple[bool, 'Float']:
123
141
  """Validate candidates during value_spec binding time."""
124
142
  del allow_partial
125
143
  del child_transform
@@ -134,17 +152,23 @@ class Float(base.HyperPrimitive):
134
152
  if (float_spec.min_value is not None
135
153
  and self.min_value < float_spec.min_value):
136
154
  raise ValueError(
137
- object_utils.message_on_path(
155
+ utils.message_on_path(
138
156
  f'Float.min_value ({self.min_value}) should be no less than '
139
157
  f'the min value ({float_spec.min_value}) of value spec: '
140
- f'{float_spec}.', path))
158
+ f'{float_spec}.',
159
+ path,
160
+ )
161
+ )
141
162
  if (float_spec.max_value is not None
142
163
  and self.max_value > float_spec.max_value):
143
164
  raise ValueError(
144
- object_utils.message_on_path(
165
+ utils.message_on_path(
145
166
  f'Float.max_value ({self.max_value}) should be no greater than '
146
167
  f'the max value ({float_spec.max_value}) of value spec: '
147
- f'{float_spec}.', path))
168
+ f'{float_spec}.',
169
+ path,
170
+ )
171
+ )
148
172
  return (False, self)
149
173
 
150
174
  def is_leaf(self) -> bool:
@@ -11,14 +11,12 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
- """Tests for pyglove.hyper.Float."""
15
-
16
14
  import unittest
17
15
 
18
16
  from pyglove.core import geno
19
- from pyglove.core import object_utils
20
17
  from pyglove.core import symbolic
21
18
  from pyglove.core import typing as pg_typing
19
+ from pyglove.core import utils
22
20
  from pyglove.core.hyper.numerical import Float
23
21
  from pyglove.core.hyper.numerical import floatv
24
22
 
@@ -44,12 +42,14 @@ class FloatTest(unittest.TestCase):
44
42
  floatv(-1.0, 1.0, 'log')
45
43
 
46
44
  def test_dna_spec(self):
47
- self.assertTrue(symbolic.eq(
48
- floatv(0.0, 1.0).dna_spec('a'),
49
- geno.Float(
50
- location=object_utils.KeyPath('a'),
51
- min_value=0.0,
52
- max_value=1.0)))
45
+ self.assertTrue(
46
+ symbolic.eq(
47
+ floatv(0.0, 1.0).dna_spec('a'),
48
+ geno.Float(
49
+ location=utils.KeyPath('a'), min_value=0.0, max_value=1.0
50
+ ),
51
+ )
52
+ )
53
53
 
54
54
  def test_decode(self):
55
55
  v = floatv(0.0, 1.0)
@@ -16,14 +16,14 @@
16
16
  from typing import Any, Callable, Dict, List, Optional, Tuple, Union
17
17
 
18
18
  from pyglove.core import geno
19
- from pyglove.core import object_utils
20
19
  from pyglove.core import symbolic
21
20
  from pyglove.core import typing as pg_typing
21
+ from pyglove.core import utils
22
22
  from pyglove.core.hyper import base
23
23
  from pyglove.core.hyper import derived
24
24
 
25
25
 
26
- class ObjectTemplate(base.HyperValue, object_utils.Formattable):
26
+ class ObjectTemplate(base.HyperValue, utils.Formattable):
27
27
  """Object template that encodes and decodes symbolic values.
28
28
 
29
29
  An object template can be created from a hyper value, which is a symbolic
@@ -131,18 +131,18 @@ class ObjectTemplate(base.HyperValue, object_utils.Formattable):
131
131
  """
132
132
  super().__init__()
133
133
  self._value = value
134
- self._root_path = object_utils.KeyPath()
134
+ self._root_path = utils.KeyPath()
135
135
  self._compute_derived = compute_derived
136
136
  self._where = where
137
137
  self._parse_generators()
138
138
 
139
139
  @property
140
- def root_path(self) -> object_utils.KeyPath:
140
+ def root_path(self) -> utils.KeyPath:
141
141
  """Returns root path."""
142
142
  return self._root_path
143
143
 
144
144
  @root_path.setter
145
- def root_path(self, path: object_utils.KeyPath):
145
+ def root_path(self, path: utils.KeyPath):
146
146
  """Set root path."""
147
147
  self._root_path = path
148
148
 
@@ -150,7 +150,8 @@ class ObjectTemplate(base.HyperValue, object_utils.Formattable):
150
150
  """Parse generators from its templated value."""
151
151
  hyper_primitives = []
152
152
  def _extract_immediate_child_hyper_primitives(
153
- path: object_utils.KeyPath, value: Any) -> bool:
153
+ path: utils.KeyPath, value: Any
154
+ ) -> bool:
154
155
  """Extract top-level hyper primitives."""
155
156
  if (isinstance(value, base.HyperValue)
156
157
  and (not self._where or self._where(value))):
@@ -162,13 +163,14 @@ class ObjectTemplate(base.HyperValue, object_utils.Formattable):
162
163
  hyper_primitives.append((path, value))
163
164
  elif isinstance(value, symbolic.Object):
164
165
  for k, v in value.sym_items():
165
- object_utils.traverse(
166
- v, _extract_immediate_child_hyper_primitives,
167
- root_path=object_utils.KeyPath(k, path))
166
+ utils.traverse(
167
+ v,
168
+ _extract_immediate_child_hyper_primitives,
169
+ root_path=utils.KeyPath(k, path),
170
+ )
168
171
  return True
169
172
 
170
- object_utils.traverse(
171
- self._value, _extract_immediate_child_hyper_primitives)
173
+ utils.traverse(self._value, _extract_immediate_child_hyper_primitives)
172
174
  self._hyper_primitives = hyper_primitives
173
175
 
174
176
  @property
@@ -186,15 +188,15 @@ class ObjectTemplate(base.HyperValue, object_utils.Formattable):
186
188
  """Returns whether current template is constant value."""
187
189
  return not self._hyper_primitives
188
190
 
189
- def dna_spec(
190
- self, location: Optional[object_utils.KeyPath] = None) -> geno.Space:
191
+ def dna_spec(self, location: Optional[utils.KeyPath] = None) -> geno.Space:
191
192
  """Return DNA spec (geno.Space) from this template."""
192
193
  return geno.Space(
193
194
  elements=[
194
195
  primitive.dna_spec(primitive_location)
195
196
  for primitive_location, primitive in self._hyper_primitives
196
197
  ],
197
- location=location or object_utils.KeyPath())
198
+ location=location or utils.KeyPath(),
199
+ )
198
200
 
199
201
  def _decode(self) -> Any:
200
202
  """Decode DNA into a value."""
@@ -202,9 +204,10 @@ class ObjectTemplate(base.HyperValue, object_utils.Formattable):
202
204
  assert dna is not None
203
205
  if not self._hyper_primitives and (dna.value is not None or dna.children):
204
206
  raise ValueError(
205
- object_utils.message_on_path(
206
- f'Encountered extra DNA value to decode: {dna!r}',
207
- self._root_path))
207
+ utils.message_on_path(
208
+ f'Encountered extra DNA value to decode: {dna!r}', self._root_path
209
+ )
210
+ )
208
211
 
209
212
  # Compute hyper primitive values first.
210
213
  rebind_dict = {}
@@ -214,11 +217,14 @@ class ObjectTemplate(base.HyperValue, object_utils.Formattable):
214
217
  else:
215
218
  if len(dna.children) != len(self._hyper_primitives):
216
219
  raise ValueError(
217
- object_utils.message_on_path(
220
+ utils.message_on_path(
218
221
  f'The length of child values ({len(dna.children)}) is '
219
- f'different from the number of hyper primitives '
222
+ 'different from the number of hyper primitives '
220
223
  f'({len(self._hyper_primitives)}) in ObjectTemplate. '
221
- f'DNA={dna!r}, ObjectTemplate={self!r}.', self._root_path))
224
+ f'DNA={dna!r}, ObjectTemplate={self!r}.',
225
+ self._root_path,
226
+ )
227
+ )
222
228
  for i, (primitive_location, primitive) in enumerate(
223
229
  self._hyper_primitives):
224
230
  rebind_dict[primitive_location.path] = (
@@ -247,18 +253,18 @@ class ObjectTemplate(base.HyperValue, object_utils.Formattable):
247
253
  # TODO(daiyip): Currently derived value parsing is done at decode time,
248
254
  # which can be optimized by moving to template creation time.
249
255
  derived_values = []
250
- def _extract_derived_values(
251
- path: object_utils.KeyPath, value: Any) -> bool:
256
+ def _extract_derived_values(path: utils.KeyPath, value: Any) -> bool:
252
257
  """Extract top-level primitives."""
253
258
  if isinstance(value, derived.DerivedValue):
254
259
  derived_values.append((path, value))
255
260
  elif isinstance(value, symbolic.Object):
256
261
  for k, v in value.sym_items():
257
- object_utils.traverse(
258
- v, _extract_derived_values,
259
- root_path=object_utils.KeyPath(k, path))
262
+ utils.traverse(
263
+ v, _extract_derived_values, root_path=utils.KeyPath(k, path)
264
+ )
260
265
  return True
261
- object_utils.traverse(value, _extract_derived_values)
266
+
267
+ utils.traverse(value, _extract_derived_values)
262
268
 
263
269
  if derived_values:
264
270
  if not copied:
@@ -299,9 +305,9 @@ class ObjectTemplate(base.HyperValue, object_utils.Formattable):
299
305
  ValueError if value cannot be encoded by this template.
300
306
  """
301
307
  children = []
302
- def _encode(path: object_utils.KeyPath,
303
- template_value: Any,
304
- input_value: Any) -> Any:
308
+ def _encode(
309
+ path: utils.KeyPath, template_value: Any, input_value: Any
310
+ ) -> Any:
305
311
  """Encode input value according to template value."""
306
312
  if (pg_typing.MISSING_VALUE == input_value
307
313
  and pg_typing.MISSING_VALUE != template_value):
@@ -339,10 +345,12 @@ class ObjectTemplate(base.HyperValue, object_utils.Formattable):
339
345
  f'TemplateOnlyKeys={template_keys - value_keys}, '
340
346
  f'InputOnlyKeys={value_keys - template_keys})')
341
347
  for key in template_value.sym_keys():
342
- object_utils.merge_tree(
348
+ utils.merge_tree(
343
349
  template_value.sym_getattr(key),
344
350
  input_value.sym_getattr(key),
345
- _encode, root_path=object_utils.KeyPath(key, path))
351
+ _encode,
352
+ root_path=utils.KeyPath(key, path),
353
+ )
346
354
  elif isinstance(template_value, symbolic.Dict):
347
355
  # Do nothing since merge will iterate all elements in dict and list.
348
356
  if not isinstance(input_value, dict):
@@ -358,19 +366,23 @@ class ObjectTemplate(base.HyperValue, object_utils.Formattable):
358
366
  f'value. (Path=\'{path}\', Template={template_value!r}, '
359
367
  f'Input={input_value!r})')
360
368
  for i, template_item in enumerate(template_value):
361
- object_utils.merge_tree(
362
- template_item, input_value[i], _encode,
363
- root_path=object_utils.KeyPath(i, path))
369
+ utils.merge_tree(
370
+ template_item,
371
+ input_value[i],
372
+ _encode,
373
+ root_path=utils.KeyPath(i, path),
374
+ )
364
375
  else:
365
376
  if template_value != input_value:
366
377
  raise ValueError(
367
- f'Unmatched value between template and input. '
368
- f'(Path=\'{path}\', '
369
- f'Template={object_utils.quote_if_str(template_value)}, '
370
- f'Input={object_utils.quote_if_str(input_value)})')
378
+ 'Unmatched value between template and input. '
379
+ f"(Path='{path}', "
380
+ f'Template={utils.quote_if_str(template_value)}, '
381
+ f'Input={utils.quote_if_str(input_value)})'
382
+ )
371
383
  return template_value
372
- object_utils.merge_tree(
373
- self._value, value, _encode, root_path=self._root_path)
384
+
385
+ utils.merge_tree(self._value, value, _encode, root_path=self._root_path)
374
386
  return geno.DNA(None, children)
375
387
 
376
388
  def try_encode(self, value: Any) -> Tuple[bool, geno.DNA]:
@@ -399,18 +411,18 @@ class ObjectTemplate(base.HyperValue, object_utils.Formattable):
399
411
  root_indent: int = 0,
400
412
  **kwargs) -> str:
401
413
  """Format this object."""
402
- details = object_utils.format(
403
- self._value, compact, verbose, root_indent, **kwargs)
414
+ details = utils.format(self._value, compact, verbose, root_indent, **kwargs)
404
415
  return f'{self.__class__.__name__}(value={details})'
405
416
 
406
417
  def custom_apply(
407
418
  self,
408
- path: object_utils.KeyPath,
419
+ path: utils.KeyPath,
409
420
  value_spec: pg_typing.ValueSpec,
410
421
  allow_partial: bool,
411
- child_transform: Optional[Callable[
412
- [object_utils.KeyPath, pg_typing.Field, Any], Any]] = None
413
- ) -> Tuple[bool, 'ObjectTemplate']:
422
+ child_transform: Optional[
423
+ Callable[[utils.KeyPath, pg_typing.Field, Any], Any]
424
+ ] = None,
425
+ ) -> Tuple[bool, 'ObjectTemplate']:
414
426
  """Validate candidates during value_spec binding time."""
415
427
  # Check if value_spec directly accepts `self`.
416
428
  if not value_spec.value_type or not isinstance(self, value_spec.value_type):
@@ -16,5 +16,6 @@
16
16
  # pylint: disable=g-bad-import-order
17
17
 
18
18
  from pyglove.core.io.file_system import *
19
+ from pyglove.core.io.sequence import *
19
20
 
20
21
  # pylint: enable=g-bad-import-order
@@ -51,6 +51,10 @@ class File(metaclass=abc.ABCMeta):
51
51
  def tell(self) -> int:
52
52
  """Returns the current position of the file."""
53
53
 
54
+ @abc.abstractmethod
55
+ def flush(self) -> None:
56
+ """Flushes the written content to the storage."""
57
+
54
58
  @abc.abstractmethod
55
59
  def close(self) -> None:
56
60
  """Closes the file."""
@@ -112,7 +116,7 @@ class FileSystem(metaclass=abc.ABCMeta):
112
116
  """Removes a directory chain based on a path."""
113
117
 
114
118
 
115
- def _resolve_path(path: Union[str, os.PathLike[str]]) -> str:
119
+ def resolve_path(path: Union[str, os.PathLike[str]]) -> str:
116
120
  if isinstance(path, str):
117
121
  return path
118
122
  elif hasattr(path, '__fspath__'):
@@ -148,6 +152,9 @@ class StdFile(File):
148
152
  def tell(self) -> int:
149
153
  return self._file_object.tell()
150
154
 
155
+ def flush(self) -> None:
156
+ self._file_object.flush()
157
+
151
158
  def close(self) -> None:
152
159
  self._file_object.close()
153
160
 
@@ -185,7 +192,7 @@ class StdFileSystem(FileSystem):
185
192
  def rm(self, path: Union[str, os.PathLike[str]]) -> None:
186
193
  os.remove(path)
187
194
 
188
- def rmdir(self, path: Union[str, os.PathLike[str]]) -> None:
195
+ def rmdir(self, path: Union[str, os.PathLike[str]]) -> None: # pytype: disable=signature-mismatch
189
196
  os.rmdir(path)
190
197
 
191
198
  def rmdirs(self, path: Union[str, os.PathLike[str]]) -> None:
@@ -220,6 +227,9 @@ class MemoryFile(File):
220
227
  def tell(self) -> int:
221
228
  return self._buffer.tell()
222
229
 
230
+ def flush(self) -> None:
231
+ pass
232
+
223
233
  def close(self) -> None:
224
234
  self.seek(0)
225
235
 
@@ -233,7 +243,7 @@ class MemoryFileSystem(FileSystem):
233
243
  self._prefix = prefix
234
244
 
235
245
  def _internal_path(self, path: Union[str, os.PathLike[str]]) -> str:
236
- return '/' + _resolve_path(path).lstrip(self._prefix)
246
+ return '/' + resolve_path(path).lstrip(self._prefix)
237
247
 
238
248
  def _locate(self, path: Union[str, os.PathLike[str]]) -> Any:
239
249
  current = self._root
@@ -277,7 +287,7 @@ class MemoryFileSystem(FileSystem):
277
287
  def _parent_and_name(
278
288
  self, path: Union[str, os.PathLike[str]]
279
289
  ) -> tuple[dict[str, Any], str]:
280
- path = _resolve_path(path)
290
+ path = resolve_path(path)
281
291
  rpos = path.rfind('/')
282
292
  assert rpos >= 0, path
283
293
  name = path[rpos + 1:]
@@ -326,7 +336,7 @@ class MemoryFileSystem(FileSystem):
326
336
  raise IsADirectoryError(path)
327
337
  del parent_dir[name]
328
338
 
329
- def rmdir(self, path: Union[str, os.PathLike[str]]) -> None:
339
+ def rmdir(self, path: Union[str, os.PathLike[str]]) -> None: # pytype: disable=signature-mismatch
330
340
  parent_dir, name = self._parent_and_name(path)
331
341
  entry = parent_dir.get(name)
332
342
  if entry is None:
@@ -372,7 +382,7 @@ class _FileSystemRegistry:
372
382
 
373
383
  def get(self, path: Union[str, os.PathLike[str]]) -> FileSystem:
374
384
  """Gets the file system for a path."""
375
- path = _resolve_path(path)
385
+ path = resolve_path(path)
376
386
  for prefix, fs in self._filesystems:
377
387
  if path.startswith(prefix):
378
388
  return fs
@@ -475,4 +485,4 @@ def rmdir(path: Union[str, os.PathLike[str]]) -> bool:
475
485
 
476
486
  def rmdirs(path: Union[str, os.PathLike[str]]) -> bool:
477
487
  """Removes a directory chain until a parent directory is not empty."""
478
- return _fs.get(path).rmdirs(path)
488
+ return _fs.get(path).rmdirs(path) # pytype: disable=bad-return-type