pyglove 0.4.5.dev20240319__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.dev20240319.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.dev20240319.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.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.dev202501132210.dist-info}/LICENSE +0 -0
  145. {pyglove-0.4.5.dev20240319.dist-info → pyglove-0.4.5.dev202501132210.dist-info}/top_level.txt +0 -0
@@ -16,8 +16,8 @@
16
16
  import typing
17
17
  from typing import Any, Callable, Iterable, Iterator, List, Optional, Sequence, Set, Tuple, Union
18
18
 
19
- from pyglove.core import object_utils
20
19
  from pyglove.core import typing as pg_typing
20
+ from pyglove.core import utils
21
21
  from pyglove.core.symbolic import base
22
22
  from pyglove.core.symbolic import flags
23
23
 
@@ -96,14 +96,16 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
96
96
  """
97
97
 
98
98
  @classmethod
99
- def partial(cls,
100
- dict_obj: Optional[typing.Dict[str, Any]] = None,
101
- value_spec: Optional[pg_typing.Dict] = None,
102
- *,
103
- onchange_callback: Optional[Callable[
104
- [typing.Dict[object_utils.KeyPath, base.FieldUpdate]], None]
105
- ] = None, # pylint: disable=bad-continuation
106
- **kwargs) -> 'Dict':
99
+ def partial(
100
+ cls,
101
+ dict_obj: Optional[typing.Dict[Union[str, int], Any]] = None,
102
+ value_spec: Optional[pg_typing.Dict] = None,
103
+ *,
104
+ onchange_callback: Optional[
105
+ Callable[[typing.Dict[utils.KeyPath, base.FieldUpdate]], None]
106
+ ] = None, # pylint: disable=bad-continuation
107
+ **kwargs,
108
+ ) -> 'Dict':
107
109
  """Class method that creates a partial Dict object."""
108
110
  return cls(dict_obj,
109
111
  value_spec=value_spec,
@@ -112,13 +114,15 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
112
114
  **kwargs)
113
115
 
114
116
  @classmethod
115
- def from_json(cls,
116
- json_value: Any,
117
- *,
118
- value_spec: Optional[pg_typing.Dict] = None,
119
- allow_partial: bool = False,
120
- root_path: Optional[object_utils.KeyPath] = None,
121
- **kwargs) -> 'Dict':
117
+ def from_json(
118
+ cls,
119
+ json_value: Any,
120
+ *,
121
+ value_spec: Optional[pg_typing.Dict] = None,
122
+ allow_partial: bool = False,
123
+ root_path: Optional[utils.KeyPath] = None,
124
+ **kwargs,
125
+ ) -> 'Dict':
122
126
  """Class method that load an symbolic Dict from a JSON value.
123
127
 
124
128
  Args:
@@ -152,22 +156,35 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
152
156
  # Not okay:
153
157
  d.a.f2.abc = 1
154
158
  """
155
- return cls(json_value,
156
- value_spec=value_spec,
157
- allow_partial=allow_partial,
158
- root_path=root_path)
159
-
160
- def __init__(self,
161
- dict_obj: Union[
162
- None,
163
- Iterable[Tuple[str, Any]],
164
- typing.Dict[str, Any]] = None,
165
- *,
166
- value_spec: Optional[pg_typing.Dict] = None,
167
- onchange_callback: Optional[Callable[
168
- [typing.Dict[object_utils.KeyPath, base.FieldUpdate]], None]
169
- ] = None, # pylint: disable=bad-continuation
170
- **kwargs):
159
+ return cls(
160
+ {
161
+ k: base.from_json(
162
+ v,
163
+ root_path=utils.KeyPath(k, root_path),
164
+ allow_partial=allow_partial,
165
+ **kwargs,
166
+ )
167
+ for k, v in json_value.items()
168
+ },
169
+ value_spec=value_spec,
170
+ root_path=root_path,
171
+ allow_partial=allow_partial,
172
+ )
173
+
174
+ def __init__(
175
+ self,
176
+ dict_obj: Union[
177
+ None,
178
+ Iterable[Tuple[Union[str, int], Any]],
179
+ typing.Dict[Union[str, int], Any],
180
+ ] = None,
181
+ *,
182
+ value_spec: Optional[pg_typing.Dict] = None,
183
+ onchange_callback: Optional[
184
+ Callable[[typing.Dict[utils.KeyPath, base.FieldUpdate]], None]
185
+ ] = None, # pylint: disable=bad-continuation
186
+ **kwargs,
187
+ ):
171
188
  """Constructor.
172
189
 
173
190
  Args:
@@ -326,8 +343,8 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
326
343
  return self
327
344
 
328
345
  def _sym_rebind(
329
- self, path_value_pairs: typing.Dict[object_utils.KeyPath, Any]
330
- ) -> List[base.FieldUpdate]:
346
+ self, path_value_pairs: typing.Dict[utils.KeyPath, Any]
347
+ ) -> List[base.FieldUpdate]:
331
348
  """Subclass specific rebind implementation."""
332
349
  updates = []
333
350
  for k, v in path_value_pairs.items():
@@ -336,7 +353,7 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
336
353
  updates.append(update)
337
354
  return updates
338
355
 
339
- def _sym_missing(self) -> typing.Dict[str, Any]:
356
+ def _sym_missing(self) -> typing.Dict[Union[str, int], Any]:
340
357
  """Returns missing values.
341
358
 
342
359
  Returns:
@@ -351,7 +368,7 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
351
368
  if keys:
352
369
  for key in keys:
353
370
  v = self.sym_getattr(key)
354
- if object_utils.MISSING_VALUE == v:
371
+ if utils.MISSING_VALUE == v:
355
372
  missing[key] = field.value.default
356
373
  else:
357
374
  if isinstance(v, base.Symbolic):
@@ -366,7 +383,7 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
366
383
  missing[k] = missing_child
367
384
  return missing
368
385
 
369
- def _sym_nondefault(self) -> typing.Dict[str, Any]:
386
+ def _sym_nondefault(self) -> typing.Dict[Union[str, int], Any]:
370
387
  """Returns non-default values as key/value pairs in a dict."""
371
388
  non_defaults = dict()
372
389
  if self._value_spec is not None and self._value_spec.schema:
@@ -399,9 +416,9 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
399
416
  return value
400
417
 
401
418
  if value.__class__ is base_value.__class__:
402
- getter = lambda x, k: x.sym_getattr(k)
419
+ getter = lambda x, k: x.sym_getattr(k, pg_typing.MISSING_VALUE)
403
420
  elif isinstance(value, dict) and isinstance(base_value, dict):
404
- getter = lambda x, k: x[k]
421
+ getter = lambda x, k: x.get(k, pg_typing.MISSING_VALUE)
405
422
  else:
406
423
  return value
407
424
 
@@ -435,7 +452,7 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
435
452
  """Tests if a symbolic attribute exists."""
436
453
  return key in self
437
454
 
438
- def sym_keys(self) -> Iterator[str]:
455
+ def sym_keys(self) -> Iterator[Union[str, int]]:
439
456
  """Iterates the keys of symbolic attributes."""
440
457
  if self._value_spec is None or self._value_spec.schema is None:
441
458
  for key in super().__iter__():
@@ -458,7 +475,7 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
458
475
  yield self._sym_getattr(k)
459
476
 
460
477
  def sym_items(self) -> Iterator[
461
- Tuple[str, Any]]:
478
+ Tuple[Union[str, int], Any]]:
462
479
  """Iterates the (key, value) pairs of symbolic attributes."""
463
480
  for k in self.sym_keys():
464
481
  yield k, self._sym_getattr(k)
@@ -481,7 +498,7 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
481
498
  if v != pg_typing.MISSING_VALUE])))
482
499
 
483
500
  def _sym_getattr( # pytype: disable=signature-mismatch # overriding-parameter-type-checks
484
- self, key: str) -> Any:
501
+ self, key: Union[str, int]) -> Any:
485
502
  """Gets symbolic attribute by key."""
486
503
  return super().__getitem__(key)
487
504
 
@@ -505,21 +522,20 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
505
522
  pass_through=True)
506
523
 
507
524
  def _update_children_paths(
508
- self,
509
- old_path: object_utils.KeyPath,
510
- new_path: object_utils.KeyPath) -> None:
525
+ self, old_path: utils.KeyPath, new_path: utils.KeyPath
526
+ ) -> None:
511
527
  """Update children paths according to root_path of current node."""
512
528
  del old_path
513
529
  for k, v in self.sym_items():
514
530
  if isinstance(v, base.TopologyAware):
515
- v.sym_setpath(object_utils.KeyPath(k, new_path))
531
+ v.sym_setpath(utils.KeyPath(k, new_path))
516
532
 
517
533
  def _set_item_without_permission_check( # pytype: disable=signature-mismatch # overriding-parameter-type-checks
518
- self, key: str, value: Any) -> Optional[base.FieldUpdate]:
534
+ self, key: Union[str, int], value: Any) -> Optional[base.FieldUpdate]:
519
535
  """Set item without permission check."""
520
- if not isinstance(key, str):
536
+ if not isinstance(key, (str, int)):
521
537
  raise KeyError(self._error_message(
522
- f'Key must be string type. Encountered {key!r}.'))
538
+ f'Key must be string or int type. Encountered {key!r}.'))
523
539
 
524
540
  old_value = self.get(key, pg_typing.MISSING_VALUE)
525
541
  if old_value is value:
@@ -536,12 +552,12 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
536
552
  container_cls = self.__class__
537
553
  raise KeyError(
538
554
  self._error_message(
539
- f'Key \'{key}\' is not allowed for {container_cls}.'))
555
+ f'Key {key!r} is not allowed for {container_cls}.'))
540
556
 
541
557
  # Detach old value from object tree.
542
558
  if isinstance(old_value, base.TopologyAware):
543
559
  old_value.sym_setparent(None)
544
- old_value.sym_setpath(object_utils.KeyPath())
560
+ old_value.sym_setpath(utils.KeyPath())
545
561
 
546
562
  if (pg_typing.MISSING_VALUE == value and
547
563
  (not field or isinstance(field.key, pg_typing.NonConstKey))):
@@ -566,9 +582,11 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
566
582
  return base.FieldUpdate(
567
583
  self.sym_path + key, target, field, old_value, new_value)
568
584
 
569
- def _formalized_value(self, name: str,
570
- field: Optional[pg_typing.Field],
571
- value: Any) -> Any:
585
+ def _formalized_value(
586
+ self, name: Union[str, int],
587
+ field: Optional[pg_typing.Field],
588
+ value: Any
589
+ ) -> Any:
572
590
  """Get transformed (formal) value from user input."""
573
591
  allow_partial = base.accepts_partial(self)
574
592
  if field and pg_typing.MISSING_VALUE == value:
@@ -578,13 +596,15 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
578
596
  value = base.from_json(
579
597
  value,
580
598
  allow_partial=allow_partial,
581
- root_path=object_utils.KeyPath(name, self.sym_path))
599
+ root_path=utils.KeyPath(name, self.sym_path),
600
+ )
582
601
  if field and flags.is_type_check_enabled():
583
602
  value = field.apply(
584
603
  value,
585
604
  allow_partial=allow_partial,
586
605
  transform_fn=base.symbolic_transform_fn(self._allow_partial),
587
- root_path=object_utils.KeyPath(name, self.sym_path))
606
+ root_path=utils.KeyPath(name, self.sym_path),
607
+ )
588
608
  return self._relocate_if_symbolic(name, value)
589
609
 
590
610
  @property
@@ -592,8 +612,9 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
592
612
  """Returns True if current dict subscribes field updates."""
593
613
  return self._onchange_callback is not None
594
614
 
595
- def _on_change(self, field_updates: typing.Dict[object_utils.KeyPath,
596
- base.FieldUpdate]):
615
+ def _on_change(
616
+ self, field_updates: typing.Dict[utils.KeyPath, base.FieldUpdate]
617
+ ):
597
618
  """On change event of Dict."""
598
619
  if self._onchange_callback:
599
620
  self._onchange_callback(field_updates)
@@ -616,14 +637,14 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
616
637
  """Customizes pickle.load."""
617
638
  self.__init__(state['value'], **state['kwargs'])
618
639
 
619
- def __getitem__(self, key: str) -> Any:
640
+ def __getitem__(self, key: Union[str, int]) -> Any:
620
641
  """Get item in this Dict."""
621
642
  try:
622
643
  return self.sym_inferred(key)
623
644
  except AttributeError as e:
624
645
  raise KeyError(key) from e
625
646
 
626
- def __setitem__(self, key: str, value: Any) -> None:
647
+ def __setitem__(self, key: Union[str, int], value: Any) -> None:
627
648
  """Set item in this Dict.
628
649
 
629
650
  Args:
@@ -724,11 +745,11 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
724
745
  """Iterate keys in field declaration order."""
725
746
  return self.sym_keys()
726
747
 
727
- def keys(self) -> Iterator[str]: # pytype: disable=signature-mismatch
748
+ def keys(self) -> Iterator[Union[str, int]]: # pytype: disable=signature-mismatch
728
749
  """Returns an iterator of keys in current dict."""
729
750
  return self.sym_keys()
730
751
 
731
- def items(self) -> Iterator[Tuple[str, Any]]: # pytype: disable=signature-mismatch
752
+ def items(self) -> Iterator[Tuple[Union[str, int], Any]]: # pytype: disable=signature-mismatch
732
753
  """Returns an iterator of (key, value) items in current dict."""
733
754
  return self.sym_items()
734
755
 
@@ -741,7 +762,7 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
741
762
  return self.sym_clone(deep=False)
742
763
 
743
764
  def pop(
744
- self, key: Any, default: Any = base.RAISE_IF_NOT_FOUND # pylint: disable=protected-access
765
+ self, key: Union[str, int], default: Any = base.RAISE_IF_NOT_FOUND # pylint: disable=protected-access
745
766
  ) -> Any:
746
767
  """Pops a key from current dict."""
747
768
  if key in self:
@@ -753,7 +774,7 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
753
774
  raise KeyError(key)
754
775
  return default
755
776
 
756
- def popitem(self) -> Tuple[str, Any]:
777
+ def popitem(self) -> Tuple[Union[str, int], Any]:
757
778
  if self._value_spec is not None:
758
779
  raise ValueError(
759
780
  '\'popitem\' cannot be performed on a Dict with value spec.')
@@ -772,7 +793,7 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
772
793
  if value_spec:
773
794
  self.use_value_spec(value_spec, self._allow_partial)
774
795
 
775
- def setdefault(self, key: str, default: Any = None) -> Any:
796
+ def setdefault(self, key: Union[str, int], default: Any = None) -> Any:
776
797
  """Sets default as the value to key if not present."""
777
798
  value = pg_typing.MISSING_VALUE
778
799
  if key in self:
@@ -782,12 +803,15 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
782
803
  value = default
783
804
  return value
784
805
 
785
- def update(self,
786
- other: Union[
787
- None,
788
- typing.Dict[str, Any],
789
- Iterable[Tuple[str, Any]]] = None,
790
- **kwargs) -> None: # pytype: disable=signature-mismatch
806
+ def update(
807
+ self,
808
+ other: Union[
809
+ None,
810
+ typing.Dict[Union[str, int], Any],
811
+ Iterable[Tuple[Union[str, int], Any]]
812
+ ] = None,
813
+ **kwargs
814
+ ) -> None: # pytype: disable=signature-mismatch
791
815
  """Update Dict with the same semantic as update on standard dict."""
792
816
  updates = dict(other) if other else {}
793
817
  updates.update(kwargs)
@@ -796,10 +820,12 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
796
820
 
797
821
  def sym_jsonify(
798
822
  self,
823
+ hide_frozen: bool = True,
799
824
  hide_default_values: bool = False,
800
- exclude_keys: Optional[Sequence[str]] = None,
825
+ exclude_keys: Optional[Sequence[Union[str, int]]] = None,
801
826
  use_inferred: bool = False,
802
- **kwargs) -> object_utils.JSONValueType:
827
+ **kwargs,
828
+ ) -> utils.JSONValueType:
803
829
  """Converts current object to a dict with plain Python objects."""
804
830
  exclude_keys = set(exclude_keys or [])
805
831
  if self._value_spec and self._value_spec.schema:
@@ -809,26 +835,30 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
809
835
  # NOTE(daiyip): The key values of frozen field can safely be excluded
810
836
  # since they will be the same for a class.
811
837
  field = self._value_spec.schema[key_spec]
812
- if not field.frozen:
813
- for key in keys:
814
- if key not in exclude_keys:
815
- value = self.sym_getattr(key)
816
- if use_inferred and isinstance(value, base.Inferential):
817
- value = self.sym_inferred(key, default=value)
818
- if pg_typing.MISSING_VALUE == value:
819
- continue
820
- if hide_default_values and base.eq(value, field.default_value):
821
- continue
822
- json_repr[key] = base.to_json(
823
- value, hide_default_values=hide_default_values,
824
- use_inferred=use_inferred,
825
- **kwargs)
838
+ if hide_frozen and field.frozen:
839
+ continue
840
+ for key in keys:
841
+ if key not in exclude_keys:
842
+ value = self.sym_getattr(key)
843
+ if use_inferred and isinstance(value, base.Inferential):
844
+ value = self.sym_inferred(key, default=value)
845
+ if pg_typing.MISSING_VALUE == value:
846
+ continue
847
+ if hide_default_values and base.eq(value, field.default_value):
848
+ continue
849
+ json_repr[key] = base.to_json(
850
+ value,
851
+ hide_frozen=hide_frozen,
852
+ hide_default_values=hide_default_values,
853
+ use_inferred=use_inferred,
854
+ **kwargs)
826
855
  return json_repr
827
856
  else:
828
857
  return {
829
858
  k: base.to_json(
830
859
  self.sym_inferred(k, default=v) if (
831
860
  use_inferred and isinstance(v, base.Inferential)) else v,
861
+ hide_frozen=hide_frozen,
832
862
  hide_default_values=hide_default_values,
833
863
  use_inferred=use_inferred,
834
864
  **kwargs)
@@ -838,11 +868,12 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
838
868
 
839
869
  def custom_apply(
840
870
  self,
841
- path: object_utils.KeyPath,
871
+ path: utils.KeyPath,
842
872
  value_spec: pg_typing.ValueSpec,
843
873
  allow_partial: bool,
844
874
  child_transform: Optional[
845
- Callable[[object_utils.KeyPath, pg_typing.Field, Any], Any]] = None
875
+ Callable[[utils.KeyPath, pg_typing.Field, Any], Any]
876
+ ] = None,
846
877
  ) -> Tuple[bool, 'Dict']:
847
878
  """Implement pg.typing.CustomTyping interface.
848
879
 
@@ -861,9 +892,12 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
861
892
  if self._value_spec:
862
893
  if value_spec and not value_spec.is_compatible(self._value_spec):
863
894
  raise ValueError(
864
- object_utils.message_on_path(
895
+ utils.message_on_path(
865
896
  f'Dict (spec={self._value_spec!r}) cannot be assigned to an '
866
- f'incompatible field (spec={value_spec!r}).', path))
897
+ f'incompatible field (spec={value_spec!r}).',
898
+ path,
899
+ )
900
+ )
867
901
  if self._allow_partial == allow_partial:
868
902
  proceed_with_standard_apply = False
869
903
  else:
@@ -879,14 +913,14 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
879
913
  root_indent: int = 0,
880
914
  *,
881
915
  python_format: bool = False,
882
- markdown: bool = False,
916
+ hide_frozen: bool = True,
883
917
  hide_default_values: bool = False,
884
918
  hide_missing_values: bool = False,
885
- include_keys: Optional[Set[str]] = None,
886
- exclude_keys: Optional[Set[str]] = None,
919
+ include_keys: Optional[Set[Union[str, int]]] = None,
920
+ exclude_keys: Optional[Set[Union[str, int]]] = None,
887
921
  use_inferred: bool = False,
888
922
  cls_name: Optional[str] = None,
889
- bracket_type: object_utils.BracketType = object_utils.BracketType.CURLY,
923
+ bracket_type: utils.BracketType = utils.BracketType.CURLY,
890
924
  key_as_attribute: bool = False,
891
925
  extra_blankline_for_field_docstr: bool = False,
892
926
  **kwargs,
@@ -910,6 +944,8 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
910
944
  for key in keys:
911
945
  if _should_include_key(key):
912
946
  field = self._value_spec.schema[key_spec]
947
+ if hide_frozen and field.frozen:
948
+ continue
913
949
  v = self.sym_getattr(key)
914
950
  if use_inferred and isinstance(v, base.Inferential):
915
951
  v = self.sym_inferred(key, default=v)
@@ -926,32 +962,34 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
926
962
  v = self.sym_inferred(k, default=v)
927
963
  field_list.append((None, k, v))
928
964
 
929
- open_bracket, close_bracket = object_utils.bracket_chars(bracket_type)
965
+ open_bracket, close_bracket = utils.bracket_chars(bracket_type)
930
966
  if not field_list:
931
- return object_utils.maybe_markdown_quote(
932
- f'{cls_name}{open_bracket}{close_bracket}', markdown
933
- )
967
+ return f'{cls_name}{open_bracket}{close_bracket}'
934
968
 
935
969
  if compact:
936
970
  s = [f'{cls_name}{open_bracket}']
937
971
  kv_strs = []
938
972
  for _, k, v in field_list:
939
- v_str = object_utils.format(
973
+ v_str = utils.format(
940
974
  v,
941
975
  compact,
942
976
  verbose,
943
977
  root_indent + 1,
978
+ hide_frozen=hide_frozen,
944
979
  hide_default_values=hide_default_values,
945
980
  hide_missing_values=hide_missing_values,
946
981
  python_format=python_format,
947
982
  use_inferred=use_inferred,
948
983
  extra_blankline_for_field_docstr=extra_blankline_for_field_docstr,
949
- **kwargs)
984
+ **kwargs,
985
+ )
950
986
  if not python_format or key_as_attribute:
951
- kv_strs.append(f'{k}={v_str}')
987
+ if isinstance(k, int):
988
+ k = f'[{k}]'
989
+ item = f'{k}={v_str}'
952
990
  else:
953
- kv_strs.append(f'\'{k}\': {v_str}')
954
-
991
+ item = f'{k!r}: {v_str}'
992
+ kv_strs.append(item)
955
993
  s.append(', '.join(kv_strs))
956
994
  s.append(close_bracket)
957
995
  else:
@@ -966,29 +1004,35 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
966
1004
  description = typing.cast(pg_typing.Field, f).description
967
1005
  for line in description.split('\n'):
968
1006
  s.append(_indent(f'# {line}\n', root_indent + 1))
969
- v_str = object_utils.format(
1007
+ v_str = utils.format(
970
1008
  v,
971
1009
  compact,
972
1010
  verbose,
973
1011
  root_indent + 1,
1012
+ hide_frozen=hide_frozen,
974
1013
  hide_default_values=hide_default_values,
975
1014
  hide_missing_values=hide_missing_values,
976
1015
  python_format=python_format,
977
1016
  use_inferred=use_inferred,
978
1017
  extra_blankline_for_field_docstr=extra_blankline_for_field_docstr,
979
- **kwargs)
1018
+ **kwargs,
1019
+ )
1020
+
980
1021
  if not python_format:
981
1022
  # Format in PyGlove's format (default).
982
- s.append(_indent(f'{k} = {v_str}', root_indent + 1))
1023
+ if isinstance(k, int):
1024
+ k = f'[{k}]'
1025
+ item = f'{k} = {v_str}'
983
1026
  elif key_as_attribute:
984
1027
  # Format `pg.Objects` under Python format.
985
- s.append(_indent(f'{k}={v_str}', root_indent + 1))
1028
+ item = f'{k}={v_str}'
986
1029
  else:
987
1030
  # Format regular `pg.Dict` under Python format.
988
- s.append(_indent(f'\'{k}\': {v_str}', root_indent + 1))
1031
+ item = f'{k!r}: {v_str}'
1032
+ s.append(_indent(item, root_indent + 1))
989
1033
  s.append('\n')
990
1034
  s.append(_indent(close_bracket, root_indent))
991
- return object_utils.maybe_markdown_quote(''.join(s), markdown)
1035
+ return ''.join(s)
992
1036
 
993
1037
  def __repr__(self) -> str:
994
1038
  """Operator repr()."""