pyglove 0.4.5.dev202411132359__py3-none-any.whl → 0.4.5.dev202501250807__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 (118) hide show
  1. pyglove/core/__init__.py +40 -21
  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 +312 -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 +53 -38
  14. pyglove/core/geno/base_test.py +2 -4
  15. pyglove/core/geno/categorical.py +36 -27
  16. pyglove/core/geno/custom.py +18 -15
  17. pyglove/core/geno/numerical.py +19 -16
  18. pyglove/core/geno/space.py +3 -4
  19. pyglove/core/hyper/base.py +6 -6
  20. pyglove/core/hyper/categorical.py +91 -52
  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 +3 -5
  25. pyglove/core/hyper/dynamic_evaluation.py +3 -4
  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/logging_test.py +0 -2
  31. pyglove/core/patching/object_factory.py +4 -4
  32. pyglove/core/patching/pattern_based.py +4 -4
  33. pyglove/core/patching/rule_based.py +4 -3
  34. pyglove/core/symbolic/__init__.py +4 -0
  35. pyglove/core/symbolic/base.py +200 -136
  36. pyglove/core/symbolic/base_test.py +17 -19
  37. pyglove/core/symbolic/boilerplate.py +4 -5
  38. pyglove/core/symbolic/class_wrapper.py +10 -14
  39. pyglove/core/symbolic/class_wrapper_test.py +2 -2
  40. pyglove/core/symbolic/compounding.py +2 -2
  41. pyglove/core/symbolic/compounding_test.py +2 -4
  42. pyglove/core/symbolic/contextual_object.py +288 -0
  43. pyglove/core/symbolic/contextual_object_test.py +327 -0
  44. pyglove/core/symbolic/dict.py +115 -87
  45. pyglove/core/symbolic/dict_test.py +188 -131
  46. pyglove/core/symbolic/diff.py +12 -12
  47. pyglove/core/symbolic/flags.py +1 -1
  48. pyglove/core/symbolic/functor.py +16 -15
  49. pyglove/core/symbolic/functor_test.py +2 -4
  50. pyglove/core/symbolic/inferred.py +2 -2
  51. pyglove/core/symbolic/list.py +70 -47
  52. pyglove/core/symbolic/list_test.py +117 -98
  53. pyglove/core/symbolic/object.py +59 -58
  54. pyglove/core/symbolic/object_test.py +143 -90
  55. pyglove/core/symbolic/origin.py +5 -7
  56. pyglove/core/symbolic/pure_symbolic.py +4 -3
  57. pyglove/core/symbolic/ref.py +33 -16
  58. pyglove/core/symbolic/ref_test.py +17 -0
  59. pyglove/core/tuning/local_backend.py +2 -2
  60. pyglove/core/tuning/protocols.py +3 -3
  61. pyglove/core/typing/annotation_conversion.py +8 -3
  62. pyglove/core/typing/annotation_conversion_test.py +8 -0
  63. pyglove/core/typing/callable_ext.py +11 -13
  64. pyglove/core/typing/callable_signature.py +22 -19
  65. pyglove/core/typing/callable_signature_test.py +3 -5
  66. pyglove/core/typing/class_schema.py +93 -54
  67. pyglove/core/typing/class_schema_test.py +4 -5
  68. pyglove/core/typing/custom_typing.py +5 -4
  69. pyglove/core/typing/key_specs.py +5 -7
  70. pyglove/core/typing/key_specs_test.py +4 -4
  71. pyglove/core/typing/type_conversion.py +4 -5
  72. pyglove/core/typing/type_conversion_test.py +12 -12
  73. pyglove/core/typing/typed_missing.py +6 -7
  74. pyglove/core/typing/typed_missing_test.py +7 -8
  75. pyglove/core/typing/value_specs.py +287 -144
  76. pyglove/core/typing/value_specs_test.py +148 -25
  77. pyglove/core/utils/__init__.py +172 -0
  78. pyglove/core/{object_utils → utils}/common_traits.py +2 -2
  79. pyglove/core/{object_utils → utils}/common_traits_test.py +1 -3
  80. pyglove/core/utils/contextual.py +147 -0
  81. pyglove/core/utils/contextual_test.py +88 -0
  82. pyglove/core/{object_utils → utils}/docstr_utils_test.py +1 -3
  83. pyglove/core/{object_utils → utils}/error_utils.py +3 -3
  84. pyglove/core/{object_utils → utils}/error_utils_test.py +1 -1
  85. pyglove/core/{object_utils → utils}/formatting.py +1 -1
  86. pyglove/core/{object_utils → utils}/formatting_test.py +1 -2
  87. pyglove/core/{object_utils → utils}/hierarchical.py +23 -25
  88. pyglove/core/{object_utils → utils}/hierarchical_test.py +3 -5
  89. pyglove/core/{object_utils → utils}/json_conversion.py +1 -1
  90. pyglove/core/{object_utils → utils}/json_conversion_test.py +1 -3
  91. pyglove/core/{object_utils → utils}/missing.py +2 -2
  92. pyglove/core/{object_utils → utils}/missing_test.py +2 -4
  93. pyglove/core/utils/text_color.py +128 -0
  94. pyglove/core/utils/text_color_test.py +94 -0
  95. pyglove/core/{object_utils → utils}/thread_local_test.py +1 -3
  96. pyglove/core/{object_utils → utils}/timing.py +21 -10
  97. pyglove/core/{object_utils → utils}/timing_test.py +14 -12
  98. pyglove/core/{object_utils → utils}/value_location.py +2 -2
  99. pyglove/core/{object_utils → utils}/value_location_test.py +2 -4
  100. pyglove/core/views/base.py +25 -29
  101. pyglove/core/views/html/base.py +15 -16
  102. pyglove/core/views/html/controls/base.py +46 -9
  103. pyglove/core/views/html/controls/label.py +13 -2
  104. pyglove/core/views/html/controls/label_test.py +27 -8
  105. pyglove/core/views/html/controls/progress_bar.py +3 -5
  106. pyglove/core/views/html/controls/progress_bar_test.py +2 -2
  107. pyglove/core/views/html/controls/tab.py +217 -66
  108. pyglove/core/views/html/controls/tab_test.py +46 -15
  109. pyglove/core/views/html/tree_view.py +39 -37
  110. {pyglove-0.4.5.dev202411132359.dist-info → pyglove-0.4.5.dev202501250807.dist-info}/METADATA +17 -3
  111. pyglove-0.4.5.dev202501250807.dist-info/RECORD +218 -0
  112. {pyglove-0.4.5.dev202411132359.dist-info → pyglove-0.4.5.dev202501250807.dist-info}/WHEEL +1 -1
  113. pyglove/core/object_utils/__init__.py +0 -164
  114. pyglove-0.4.5.dev202411132359.dist-info/RECORD +0 -203
  115. /pyglove/core/{object_utils → utils}/docstr_utils.py +0 -0
  116. /pyglove/core/{object_utils → utils}/thread_local.py +0 -0
  117. {pyglove-0.4.5.dev202411132359.dist-info → pyglove-0.4.5.dev202501250807.dist-info}/LICENSE +0 -0
  118. {pyglove-0.4.5.dev202411132359.dist-info → pyglove-0.4.5.dev202501250807.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:
@@ -156,27 +160,31 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
156
160
  {
157
161
  k: base.from_json(
158
162
  v,
159
- root_path=object_utils.KeyPath(k, root_path),
163
+ root_path=utils.KeyPath(k, root_path),
160
164
  allow_partial=allow_partial,
161
- **kwargs
162
- ) for k, v in json_value.items()
165
+ **kwargs,
166
+ )
167
+ for k, v in json_value.items()
163
168
  },
164
169
  value_spec=value_spec,
165
170
  root_path=root_path,
166
171
  allow_partial=allow_partial,
167
172
  )
168
173
 
169
- def __init__(self,
170
- dict_obj: Union[
171
- None,
172
- Iterable[Tuple[str, Any]],
173
- typing.Dict[str, Any]] = None,
174
- *,
175
- value_spec: Optional[pg_typing.Dict] = None,
176
- onchange_callback: Optional[Callable[
177
- [typing.Dict[object_utils.KeyPath, base.FieldUpdate]], None]
178
- ] = None, # pylint: disable=bad-continuation
179
- **kwargs):
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
+ ):
180
188
  """Constructor.
181
189
 
182
190
  Args:
@@ -335,8 +343,8 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
335
343
  return self
336
344
 
337
345
  def _sym_rebind(
338
- self, path_value_pairs: typing.Dict[object_utils.KeyPath, Any]
339
- ) -> List[base.FieldUpdate]:
346
+ self, path_value_pairs: typing.Dict[utils.KeyPath, Any]
347
+ ) -> List[base.FieldUpdate]:
340
348
  """Subclass specific rebind implementation."""
341
349
  updates = []
342
350
  for k, v in path_value_pairs.items():
@@ -345,7 +353,7 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
345
353
  updates.append(update)
346
354
  return updates
347
355
 
348
- def _sym_missing(self) -> typing.Dict[str, Any]:
356
+ def _sym_missing(self) -> typing.Dict[Union[str, int], Any]:
349
357
  """Returns missing values.
350
358
 
351
359
  Returns:
@@ -360,7 +368,7 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
360
368
  if keys:
361
369
  for key in keys:
362
370
  v = self.sym_getattr(key)
363
- if object_utils.MISSING_VALUE == v:
371
+ if utils.MISSING_VALUE == v:
364
372
  missing[key] = field.value.default
365
373
  else:
366
374
  if isinstance(v, base.Symbolic):
@@ -375,7 +383,7 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
375
383
  missing[k] = missing_child
376
384
  return missing
377
385
 
378
- def _sym_nondefault(self) -> typing.Dict[str, Any]:
386
+ def _sym_nondefault(self) -> typing.Dict[Union[str, int], Any]:
379
387
  """Returns non-default values as key/value pairs in a dict."""
380
388
  non_defaults = dict()
381
389
  if self._value_spec is not None and self._value_spec.schema:
@@ -444,7 +452,7 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
444
452
  """Tests if a symbolic attribute exists."""
445
453
  return key in self
446
454
 
447
- def sym_keys(self) -> Iterator[str]:
455
+ def sym_keys(self) -> Iterator[Union[str, int]]:
448
456
  """Iterates the keys of symbolic attributes."""
449
457
  if self._value_spec is None or self._value_spec.schema is None:
450
458
  for key in super().__iter__():
@@ -467,7 +475,7 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
467
475
  yield self._sym_getattr(k)
468
476
 
469
477
  def sym_items(self) -> Iterator[
470
- Tuple[str, Any]]:
478
+ Tuple[Union[str, int], Any]]:
471
479
  """Iterates the (key, value) pairs of symbolic attributes."""
472
480
  for k in self.sym_keys():
473
481
  yield k, self._sym_getattr(k)
@@ -490,7 +498,7 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
490
498
  if v != pg_typing.MISSING_VALUE])))
491
499
 
492
500
  def _sym_getattr( # pytype: disable=signature-mismatch # overriding-parameter-type-checks
493
- self, key: str) -> Any:
501
+ self, key: Union[str, int]) -> Any:
494
502
  """Gets symbolic attribute by key."""
495
503
  return super().__getitem__(key)
496
504
 
@@ -514,21 +522,20 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
514
522
  pass_through=True)
515
523
 
516
524
  def _update_children_paths(
517
- self,
518
- old_path: object_utils.KeyPath,
519
- new_path: object_utils.KeyPath) -> None:
525
+ self, old_path: utils.KeyPath, new_path: utils.KeyPath
526
+ ) -> None:
520
527
  """Update children paths according to root_path of current node."""
521
528
  del old_path
522
529
  for k, v in self.sym_items():
523
530
  if isinstance(v, base.TopologyAware):
524
- v.sym_setpath(object_utils.KeyPath(k, new_path))
531
+ v.sym_setpath(utils.KeyPath(k, new_path))
525
532
 
526
533
  def _set_item_without_permission_check( # pytype: disable=signature-mismatch # overriding-parameter-type-checks
527
- self, key: str, value: Any) -> Optional[base.FieldUpdate]:
534
+ self, key: Union[str, int], value: Any) -> Optional[base.FieldUpdate]:
528
535
  """Set item without permission check."""
529
- if not isinstance(key, str):
536
+ if not isinstance(key, (str, int)):
530
537
  raise KeyError(self._error_message(
531
- f'Key must be string type. Encountered {key!r}.'))
538
+ f'Key must be string or int type. Encountered {key!r}.'))
532
539
 
533
540
  old_value = self.get(key, pg_typing.MISSING_VALUE)
534
541
  if old_value is value:
@@ -545,12 +552,12 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
545
552
  container_cls = self.__class__
546
553
  raise KeyError(
547
554
  self._error_message(
548
- f'Key \'{key}\' is not allowed for {container_cls}.'))
555
+ f'Key {key!r} is not allowed for {container_cls}.'))
549
556
 
550
557
  # Detach old value from object tree.
551
558
  if isinstance(old_value, base.TopologyAware):
552
559
  old_value.sym_setparent(None)
553
- old_value.sym_setpath(object_utils.KeyPath())
560
+ old_value.sym_setpath(utils.KeyPath())
554
561
 
555
562
  if (pg_typing.MISSING_VALUE == value and
556
563
  (not field or isinstance(field.key, pg_typing.NonConstKey))):
@@ -575,9 +582,11 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
575
582
  return base.FieldUpdate(
576
583
  self.sym_path + key, target, field, old_value, new_value)
577
584
 
578
- def _formalized_value(self, name: str,
579
- field: Optional[pg_typing.Field],
580
- value: Any) -> Any:
585
+ def _formalized_value(
586
+ self, name: Union[str, int],
587
+ field: Optional[pg_typing.Field],
588
+ value: Any
589
+ ) -> Any:
581
590
  """Get transformed (formal) value from user input."""
582
591
  allow_partial = base.accepts_partial(self)
583
592
  if field and pg_typing.MISSING_VALUE == value:
@@ -587,13 +596,15 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
587
596
  value = base.from_json(
588
597
  value,
589
598
  allow_partial=allow_partial,
590
- root_path=object_utils.KeyPath(name, self.sym_path))
599
+ root_path=utils.KeyPath(name, self.sym_path),
600
+ )
591
601
  if field and flags.is_type_check_enabled():
592
602
  value = field.apply(
593
603
  value,
594
604
  allow_partial=allow_partial,
595
605
  transform_fn=base.symbolic_transform_fn(self._allow_partial),
596
- root_path=object_utils.KeyPath(name, self.sym_path))
606
+ root_path=utils.KeyPath(name, self.sym_path),
607
+ )
597
608
  return self._relocate_if_symbolic(name, value)
598
609
 
599
610
  @property
@@ -601,8 +612,9 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
601
612
  """Returns True if current dict subscribes field updates."""
602
613
  return self._onchange_callback is not None
603
614
 
604
- def _on_change(self, field_updates: typing.Dict[object_utils.KeyPath,
605
- base.FieldUpdate]):
615
+ def _on_change(
616
+ self, field_updates: typing.Dict[utils.KeyPath, base.FieldUpdate]
617
+ ):
606
618
  """On change event of Dict."""
607
619
  if self._onchange_callback:
608
620
  self._onchange_callback(field_updates)
@@ -625,14 +637,14 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
625
637
  """Customizes pickle.load."""
626
638
  self.__init__(state['value'], **state['kwargs'])
627
639
 
628
- def __getitem__(self, key: str) -> Any:
640
+ def __getitem__(self, key: Union[str, int]) -> Any:
629
641
  """Get item in this Dict."""
630
642
  try:
631
643
  return self.sym_inferred(key)
632
644
  except AttributeError as e:
633
645
  raise KeyError(key) from e
634
646
 
635
- def __setitem__(self, key: str, value: Any) -> None:
647
+ def __setitem__(self, key: Union[str, int], value: Any) -> None:
636
648
  """Set item in this Dict.
637
649
 
638
650
  Args:
@@ -733,11 +745,11 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
733
745
  """Iterate keys in field declaration order."""
734
746
  return self.sym_keys()
735
747
 
736
- def keys(self) -> Iterator[str]: # pytype: disable=signature-mismatch
748
+ def keys(self) -> Iterator[Union[str, int]]: # pytype: disable=signature-mismatch
737
749
  """Returns an iterator of keys in current dict."""
738
750
  return self.sym_keys()
739
751
 
740
- 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
741
753
  """Returns an iterator of (key, value) items in current dict."""
742
754
  return self.sym_items()
743
755
 
@@ -750,7 +762,7 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
750
762
  return self.sym_clone(deep=False)
751
763
 
752
764
  def pop(
753
- 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
754
766
  ) -> Any:
755
767
  """Pops a key from current dict."""
756
768
  if key in self:
@@ -762,7 +774,7 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
762
774
  raise KeyError(key)
763
775
  return default
764
776
 
765
- def popitem(self) -> Tuple[str, Any]:
777
+ def popitem(self) -> Tuple[Union[str, int], Any]:
766
778
  if self._value_spec is not None:
767
779
  raise ValueError(
768
780
  '\'popitem\' cannot be performed on a Dict with value spec.')
@@ -781,7 +793,7 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
781
793
  if value_spec:
782
794
  self.use_value_spec(value_spec, self._allow_partial)
783
795
 
784
- def setdefault(self, key: str, default: Any = None) -> Any:
796
+ def setdefault(self, key: Union[str, int], default: Any = None) -> Any:
785
797
  """Sets default as the value to key if not present."""
786
798
  value = pg_typing.MISSING_VALUE
787
799
  if key in self:
@@ -791,12 +803,15 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
791
803
  value = default
792
804
  return value
793
805
 
794
- def update(self,
795
- other: Union[
796
- None,
797
- typing.Dict[str, Any],
798
- Iterable[Tuple[str, Any]]] = None,
799
- **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
800
815
  """Update Dict with the same semantic as update on standard dict."""
801
816
  updates = dict(other) if other else {}
802
817
  updates.update(kwargs)
@@ -807,9 +822,10 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
807
822
  self,
808
823
  hide_frozen: bool = True,
809
824
  hide_default_values: bool = False,
810
- exclude_keys: Optional[Sequence[str]] = None,
825
+ exclude_keys: Optional[Sequence[Union[str, int]]] = None,
811
826
  use_inferred: bool = False,
812
- **kwargs) -> object_utils.JSONValueType:
827
+ **kwargs,
828
+ ) -> utils.JSONValueType:
813
829
  """Converts current object to a dict with plain Python objects."""
814
830
  exclude_keys = set(exclude_keys or [])
815
831
  if self._value_spec and self._value_spec.schema:
@@ -852,11 +868,12 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
852
868
 
853
869
  def custom_apply(
854
870
  self,
855
- path: object_utils.KeyPath,
871
+ path: utils.KeyPath,
856
872
  value_spec: pg_typing.ValueSpec,
857
873
  allow_partial: bool,
858
874
  child_transform: Optional[
859
- Callable[[object_utils.KeyPath, pg_typing.Field, Any], Any]] = None
875
+ Callable[[utils.KeyPath, pg_typing.Field, Any], Any]
876
+ ] = None,
860
877
  ) -> Tuple[bool, 'Dict']:
861
878
  """Implement pg.typing.CustomTyping interface.
862
879
 
@@ -875,9 +892,12 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
875
892
  if self._value_spec:
876
893
  if value_spec and not value_spec.is_compatible(self._value_spec):
877
894
  raise ValueError(
878
- object_utils.message_on_path(
895
+ utils.message_on_path(
879
896
  f'Dict (spec={self._value_spec!r}) cannot be assigned to an '
880
- f'incompatible field (spec={value_spec!r}).', path))
897
+ f'incompatible field (spec={value_spec!r}).',
898
+ path,
899
+ )
900
+ )
881
901
  if self._allow_partial == allow_partial:
882
902
  proceed_with_standard_apply = False
883
903
  else:
@@ -896,11 +916,11 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
896
916
  hide_frozen: bool = True,
897
917
  hide_default_values: bool = False,
898
918
  hide_missing_values: bool = False,
899
- include_keys: Optional[Set[str]] = None,
900
- exclude_keys: Optional[Set[str]] = None,
919
+ include_keys: Optional[Set[Union[str, int]]] = None,
920
+ exclude_keys: Optional[Set[Union[str, int]]] = None,
901
921
  use_inferred: bool = False,
902
922
  cls_name: Optional[str] = None,
903
- bracket_type: object_utils.BracketType = object_utils.BracketType.CURLY,
923
+ bracket_type: utils.BracketType = utils.BracketType.CURLY,
904
924
  key_as_attribute: bool = False,
905
925
  extra_blankline_for_field_docstr: bool = False,
906
926
  **kwargs,
@@ -942,7 +962,7 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
942
962
  v = self.sym_inferred(k, default=v)
943
963
  field_list.append((None, k, v))
944
964
 
945
- open_bracket, close_bracket = object_utils.bracket_chars(bracket_type)
965
+ open_bracket, close_bracket = utils.bracket_chars(bracket_type)
946
966
  if not field_list:
947
967
  return f'{cls_name}{open_bracket}{close_bracket}'
948
968
 
@@ -950,7 +970,7 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
950
970
  s = [f'{cls_name}{open_bracket}']
951
971
  kv_strs = []
952
972
  for _, k, v in field_list:
953
- v_str = object_utils.format(
973
+ v_str = utils.format(
954
974
  v,
955
975
  compact,
956
976
  verbose,
@@ -961,12 +981,15 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
961
981
  python_format=python_format,
962
982
  use_inferred=use_inferred,
963
983
  extra_blankline_for_field_docstr=extra_blankline_for_field_docstr,
964
- **kwargs)
984
+ **kwargs,
985
+ )
965
986
  if not python_format or key_as_attribute:
966
- kv_strs.append(f'{k}={v_str}')
987
+ if isinstance(k, int):
988
+ k = f'[{k}]'
989
+ item = f'{k}={v_str}'
967
990
  else:
968
- kv_strs.append(f'\'{k}\': {v_str}')
969
-
991
+ item = f'{k!r}: {v_str}'
992
+ kv_strs.append(item)
970
993
  s.append(', '.join(kv_strs))
971
994
  s.append(close_bracket)
972
995
  else:
@@ -981,7 +1004,7 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
981
1004
  description = typing.cast(pg_typing.Field, f).description
982
1005
  for line in description.split('\n'):
983
1006
  s.append(_indent(f'# {line}\n', root_indent + 1))
984
- v_str = object_utils.format(
1007
+ v_str = utils.format(
985
1008
  v,
986
1009
  compact,
987
1010
  verbose,
@@ -992,16 +1015,21 @@ class Dict(dict, base.Symbolic, pg_typing.CustomTyping):
992
1015
  python_format=python_format,
993
1016
  use_inferred=use_inferred,
994
1017
  extra_blankline_for_field_docstr=extra_blankline_for_field_docstr,
995
- **kwargs)
1018
+ **kwargs,
1019
+ )
1020
+
996
1021
  if not python_format:
997
1022
  # Format in PyGlove's format (default).
998
- 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}'
999
1026
  elif key_as_attribute:
1000
1027
  # Format `pg.Objects` under Python format.
1001
- s.append(_indent(f'{k}={v_str}', root_indent + 1))
1028
+ item = f'{k}={v_str}'
1002
1029
  else:
1003
1030
  # Format regular `pg.Dict` under Python format.
1004
- 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))
1005
1033
  s.append('\n')
1006
1034
  s.append(_indent(close_bracket, root_indent))
1007
1035
  return ''.join(s)