pyglove 0.4.5.dev202502040809__tar.gz → 0.4.5.dev202502060809__tar.gz

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 (224) hide show
  1. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/PKG-INFO +1 -1
  2. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/object.py +4 -1
  3. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/typing/__init__.py +3 -0
  4. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/typing/annotation_conversion.py +236 -6
  5. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/typing/annotation_conversion_test.py +180 -9
  6. pyglove-0.4.5.dev202502060809/pyglove/core/typing/annotation_future_test.py +135 -0
  7. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/typing/class_schema.py +36 -18
  8. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/typing/class_schema_test.py +15 -7
  9. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove.egg-info/PKG-INFO +1 -1
  10. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove.egg-info/SOURCES.txt +1 -0
  11. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/LICENSE +0 -0
  12. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/README.md +0 -0
  13. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/__init__.py +0 -0
  14. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/__init__.py +0 -0
  15. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/coding/__init__.py +0 -0
  16. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/coding/errors.py +0 -0
  17. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/coding/errors_test.py +0 -0
  18. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/coding/execution.py +0 -0
  19. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/coding/execution_test.py +0 -0
  20. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/coding/function_generation.py +0 -0
  21. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/coding/function_generation_test.py +0 -0
  22. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/coding/parsing.py +0 -0
  23. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/coding/parsing_test.py +0 -0
  24. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/coding/permissions.py +0 -0
  25. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/coding/permissions_test.py +0 -0
  26. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/detouring/__init__.py +0 -0
  27. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/detouring/class_detour.py +0 -0
  28. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/detouring/class_detour_test.py +0 -0
  29. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/geno/__init__.py +0 -0
  30. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/geno/base.py +0 -0
  31. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/geno/base_test.py +0 -0
  32. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/geno/categorical.py +0 -0
  33. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/geno/categorical_test.py +0 -0
  34. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/geno/custom.py +0 -0
  35. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/geno/custom_test.py +0 -0
  36. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/geno/deduping.py +0 -0
  37. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/geno/deduping_test.py +0 -0
  38. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/geno/dna_generator.py +0 -0
  39. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/geno/dna_generator_test.py +0 -0
  40. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/geno/numerical.py +0 -0
  41. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/geno/numerical_test.py +0 -0
  42. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/geno/random.py +0 -0
  43. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/geno/random_test.py +0 -0
  44. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/geno/space.py +0 -0
  45. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/geno/space_test.py +0 -0
  46. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/geno/sweeping.py +0 -0
  47. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/geno/sweeping_test.py +0 -0
  48. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/hyper/__init__.py +0 -0
  49. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/hyper/base.py +0 -0
  50. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/hyper/categorical.py +0 -0
  51. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/hyper/categorical_test.py +0 -0
  52. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/hyper/custom.py +0 -0
  53. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/hyper/custom_test.py +0 -0
  54. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/hyper/derived.py +0 -0
  55. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/hyper/derived_test.py +0 -0
  56. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/hyper/dynamic_evaluation.py +0 -0
  57. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/hyper/dynamic_evaluation_test.py +0 -0
  58. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/hyper/evolvable.py +0 -0
  59. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/hyper/evolvable_test.py +0 -0
  60. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/hyper/iter.py +0 -0
  61. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/hyper/iter_test.py +0 -0
  62. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/hyper/numerical.py +0 -0
  63. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/hyper/numerical_test.py +0 -0
  64. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/hyper/object_template.py +0 -0
  65. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/hyper/object_template_test.py +0 -0
  66. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/io/__init__.py +0 -0
  67. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/io/file_system.py +0 -0
  68. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/io/file_system_test.py +0 -0
  69. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/io/sequence.py +0 -0
  70. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/io/sequence_test.py +0 -0
  71. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/logging.py +0 -0
  72. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/logging_test.py +0 -0
  73. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/patching/__init__.py +0 -0
  74. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/patching/object_factory.py +0 -0
  75. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/patching/object_factory_test.py +0 -0
  76. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/patching/pattern_based.py +0 -0
  77. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/patching/pattern_based_test.py +0 -0
  78. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/patching/rule_based.py +0 -0
  79. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/patching/rule_based_test.py +0 -0
  80. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/__init__.py +0 -0
  81. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/base.py +0 -0
  82. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/base_test.py +0 -0
  83. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/boilerplate.py +0 -0
  84. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/boilerplate_test.py +0 -0
  85. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/class_wrapper.py +0 -0
  86. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/class_wrapper_test.py +0 -0
  87. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/compounding.py +0 -0
  88. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/compounding_test.py +0 -0
  89. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/contextual_object.py +0 -0
  90. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/contextual_object_test.py +0 -0
  91. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/dict.py +0 -0
  92. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/dict_test.py +0 -0
  93. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/diff.py +0 -0
  94. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/diff_test.py +0 -0
  95. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/flags.py +0 -0
  96. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/flags_test.py +0 -0
  97. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/functor.py +0 -0
  98. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/functor_test.py +0 -0
  99. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/inferred.py +0 -0
  100. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/inferred_test.py +0 -0
  101. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/list.py +0 -0
  102. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/list_test.py +0 -0
  103. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/object_test.py +0 -0
  104. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/origin.py +0 -0
  105. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/origin_test.py +0 -0
  106. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/pure_symbolic.py +0 -0
  107. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/ref.py +0 -0
  108. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/ref_test.py +0 -0
  109. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/symbolize.py +0 -0
  110. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/symbolic/symbolize_test.py +0 -0
  111. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/tuning/__init__.py +0 -0
  112. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/tuning/backend.py +0 -0
  113. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/tuning/backend_test.py +0 -0
  114. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/tuning/early_stopping.py +0 -0
  115. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/tuning/local_backend.py +0 -0
  116. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/tuning/protocols.py +0 -0
  117. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/tuning/protocols_test.py +0 -0
  118. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/tuning/sample.py +0 -0
  119. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/tuning/sample_test.py +0 -0
  120. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/typing/annotated.py +0 -0
  121. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/typing/annotated_test.py +0 -0
  122. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/typing/callable_ext.py +0 -0
  123. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/typing/callable_ext_test.py +0 -0
  124. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/typing/callable_signature.py +0 -0
  125. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/typing/callable_signature_test.py +0 -0
  126. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/typing/custom_typing.py +0 -0
  127. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/typing/inspect.py +0 -0
  128. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/typing/inspect_test.py +0 -0
  129. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/typing/key_specs.py +0 -0
  130. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/typing/key_specs_test.py +0 -0
  131. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/typing/pytype_support.py +0 -0
  132. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/typing/type_conversion.py +0 -0
  133. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/typing/type_conversion_test.py +0 -0
  134. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/typing/typed_missing.py +0 -0
  135. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/typing/typed_missing_test.py +0 -0
  136. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/typing/value_specs.py +0 -0
  137. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/typing/value_specs_test.py +0 -0
  138. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/__init__.py +0 -0
  139. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/common_traits.py +0 -0
  140. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/common_traits_test.py +0 -0
  141. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/contextual.py +0 -0
  142. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/contextual_test.py +0 -0
  143. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/docstr_utils.py +0 -0
  144. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/docstr_utils_test.py +0 -0
  145. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/error_utils.py +0 -0
  146. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/error_utils_test.py +0 -0
  147. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/formatting.py +0 -0
  148. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/formatting_test.py +0 -0
  149. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/hierarchical.py +0 -0
  150. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/hierarchical_test.py +0 -0
  151. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/json_conversion.py +0 -0
  152. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/json_conversion_test.py +0 -0
  153. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/missing.py +0 -0
  154. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/missing_test.py +0 -0
  155. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/text_color.py +0 -0
  156. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/text_color_test.py +0 -0
  157. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/thread_local.py +0 -0
  158. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/thread_local_test.py +0 -0
  159. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/timing.py +0 -0
  160. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/timing_test.py +0 -0
  161. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/value_location.py +0 -0
  162. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/utils/value_location_test.py +0 -0
  163. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/views/__init__.py +0 -0
  164. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/views/base.py +0 -0
  165. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/views/base_test.py +0 -0
  166. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/views/html/__init__.py +0 -0
  167. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/views/html/base.py +0 -0
  168. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/views/html/base_test.py +0 -0
  169. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/views/html/controls/__init__.py +0 -0
  170. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/views/html/controls/base.py +0 -0
  171. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/views/html/controls/label.py +0 -0
  172. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/views/html/controls/label_test.py +0 -0
  173. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/views/html/controls/progress_bar.py +0 -0
  174. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/views/html/controls/progress_bar_test.py +0 -0
  175. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/views/html/controls/tab.py +0 -0
  176. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/views/html/controls/tab_test.py +0 -0
  177. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/views/html/controls/tooltip.py +0 -0
  178. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/views/html/controls/tooltip_test.py +0 -0
  179. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/views/html/tree_view.py +0 -0
  180. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/core/views/html/tree_view_test.py +0 -0
  181. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/__init__.py +0 -0
  182. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/early_stopping/__init__.py +0 -0
  183. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/early_stopping/base.py +0 -0
  184. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/early_stopping/base_test.py +0 -0
  185. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/early_stopping/step_wise.py +0 -0
  186. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/early_stopping/step_wise_test.py +0 -0
  187. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/evolution/__init__.py +0 -0
  188. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/evolution/base.py +0 -0
  189. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/evolution/base_test.py +0 -0
  190. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/evolution/hill_climb.py +0 -0
  191. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/evolution/hill_climb_test.py +0 -0
  192. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/evolution/mutators.py +0 -0
  193. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/evolution/mutators_test.py +0 -0
  194. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/evolution/neat.py +0 -0
  195. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/evolution/neat_test.py +0 -0
  196. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/evolution/nsga2.py +0 -0
  197. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/evolution/nsga2_test.py +0 -0
  198. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/evolution/recombinators.py +0 -0
  199. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/evolution/recombinators_test.py +0 -0
  200. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/evolution/regularized_evolution.py +0 -0
  201. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/evolution/regularized_evolution_test.py +0 -0
  202. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/evolution/selectors.py +0 -0
  203. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/evolution/selectors_test.py +0 -0
  204. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/evolution/where.py +0 -0
  205. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/evolution/where_test.py +0 -0
  206. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/mutfun/__init__.py +0 -0
  207. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/mutfun/base.py +0 -0
  208. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/mutfun/base_test.py +0 -0
  209. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/mutfun/basic_ops.py +0 -0
  210. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/mutfun/basic_ops_test.py +0 -0
  211. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/scalars/__init__.py +0 -0
  212. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/scalars/base.py +0 -0
  213. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/scalars/base_test.py +0 -0
  214. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/scalars/maths.py +0 -0
  215. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/scalars/maths_test.py +0 -0
  216. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/scalars/randoms.py +0 -0
  217. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/scalars/randoms_test.py +0 -0
  218. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/scalars/step_wise.py +0 -0
  219. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove/ext/scalars/step_wise_test.py +0 -0
  220. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove.egg-info/dependency_links.txt +0 -0
  221. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove.egg-info/requires.txt +0 -0
  222. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/pyglove.egg-info/top_level.txt +0 -0
  223. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/setup.cfg +0 -0
  224. {pyglove-0.4.5.dev202502040809 → pyglove-0.4.5.dev202502060809}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pyglove
3
- Version: 0.4.5.dev202502040809
3
+ Version: 0.4.5.dev202502060809
4
4
  Summary: PyGlove: A library for manipulating Python objects.
5
5
  Home-page: https://github.com/google/pyglove
6
6
  Author: PyGlove Authors
@@ -16,6 +16,7 @@
16
16
  import abc
17
17
  import functools
18
18
  import inspect
19
+ import sys
19
20
  import typing
20
21
  from typing import Any, Dict, Iterator, List, Optional, Sequence, Union
21
22
 
@@ -154,7 +155,9 @@ class ObjectMeta(abc.ABCMeta):
154
155
  if typing.get_origin(attr_annotation) is typing.ClassVar:
155
156
  continue
156
157
 
157
- field = pg_typing.Field.from_annotation(key, attr_annotation)
158
+ field = pg_typing.Field.from_annotation(
159
+ key, attr_annotation, parent_module=sys.modules[cls.__module__]
160
+ )
158
161
  if isinstance(key, pg_typing.ConstStrKey):
159
162
  attr_value = cls.__dict__.get(attr_name, pg_typing.MISSING_VALUE)
160
163
  if attr_value != pg_typing.MISSING_VALUE:
@@ -375,6 +375,9 @@ import pyglove.core.typing.annotation_conversion # pylint: disable=unused-impor
375
375
  # Interface for custom typing.
376
376
  from pyglove.core.typing.custom_typing import CustomTyping
377
377
 
378
+ # Annotation conversion
379
+ from pyglove.core.typing.annotation_conversion import annotation_from_str
380
+
378
381
  # Callable signature.
379
382
  from pyglove.core.typing.callable_signature import Argument
380
383
  from pyglove.core.typing.callable_signature import CallableType
@@ -13,11 +13,13 @@
13
13
  # limitations under the License.
14
14
  """Conversion from annotations to PyGlove value specs."""
15
15
 
16
+ import builtins
16
17
  import collections
17
18
  import inspect
18
19
  import types
19
20
  import typing
20
21
 
22
+ from pyglove.core import coding
21
23
  from pyglove.core import utils
22
24
  from pyglove.core.typing import annotated
23
25
  from pyglove.core.typing import class_schema
@@ -35,6 +37,231 @@ _Annotated = getattr(typing, 'Annotated', None) # pylint: disable=invalid-name
35
37
  _UnionType = getattr(types, 'UnionType', None) # pylint: disable=invalid-name
36
38
 
37
39
 
40
+ def annotation_from_str(
41
+ annotation_str: str,
42
+ parent_module: typing.Optional[types.ModuleType] = None,
43
+ ) -> typing.Any:
44
+ """Parses annotations from str.
45
+
46
+ BNF for PyType annotations:
47
+
48
+ ```
49
+ <maybe_union> ::= <type> | <type> "|" <maybe_union>
50
+ <type> ::= <literal_type> | <non_literal_type>
51
+
52
+ <literal_type> ::= "Literal"<literal_params>
53
+ <literal_params> ::= "["<python_values>"]" (parsed by `pg.coding.evaluate`)
54
+
55
+ <non_literal_type> ::= <type_id> | <type_id>"["<type_arg>"]"
56
+ <type_arg> ::= <maybe_type_list> | <maybe_type_list>","<maybe_type_list>
57
+ <maybe_type_list> ::= "["<type_arg>"]" | <maybe_union>
58
+ <type_id> ::= 'aAz_.1-9'
59
+ ```
60
+
61
+ Args:
62
+ annotation_str: String form of type annotations. E.g. "list[str]"
63
+ parent_module: The module where the annotation was defined.
64
+
65
+ Returns:
66
+ Object form of the annotation.
67
+
68
+ Raises:
69
+ SyntaxError: If the annotation string is invalid.
70
+ """
71
+ s = annotation_str
72
+ context = dict(pos=0)
73
+
74
+ def _eof() -> bool:
75
+ return context['pos'] == len(s)
76
+
77
+ def _pos() -> int:
78
+ return context['pos']
79
+
80
+ def _next(n: int = 1, offset: int = 0) -> str:
81
+ if _eof():
82
+ return '<EOF>'
83
+ return s[_pos() + offset:_pos() + offset + n]
84
+
85
+ def _advance(n: int) -> None:
86
+ context['pos'] += n
87
+
88
+ def _error_illustration() -> str:
89
+ return f'{s}\n{" " * _pos()}' + '^'
90
+
91
+ def _match(ch) -> bool:
92
+ if _next(len(ch)) == ch:
93
+ _advance(len(ch))
94
+ return True
95
+ return False
96
+
97
+ def _skip_whitespaces() -> None:
98
+ while _next() in ' \t':
99
+ _advance(1)
100
+
101
+ def _maybe_union():
102
+ t = _type()
103
+ while not _eof():
104
+ _skip_whitespaces()
105
+ if _match('|'):
106
+ t = t | _type()
107
+ else:
108
+ break
109
+ return t
110
+
111
+ def _type():
112
+ type_id = _type_id()
113
+ t = _resolve(type_id)
114
+ if t is typing.Literal:
115
+ return t[_literal_params()]
116
+ elif _match('['):
117
+ arg = _type_arg()
118
+ if not _match(']'):
119
+ raise SyntaxError(
120
+ f'Expected "]" at position {_pos()}.\n\n' + _error_illustration()
121
+ )
122
+ return t[arg]
123
+ return t
124
+
125
+ def _literal_params():
126
+ if not _match('['):
127
+ raise SyntaxError(
128
+ f'Expected "[" at position {_pos()}.\n\n' + _error_illustration()
129
+ )
130
+ arg_start = _pos()
131
+ in_str = False
132
+ escape_mode = False
133
+ num_open_bracket = 1
134
+
135
+ while num_open_bracket > 0:
136
+ ch = _next()
137
+ if _eof():
138
+ raise SyntaxError(
139
+ f'Unexpected end of annotation at position {_pos()}.\n\n'
140
+ + _error_illustration()
141
+ )
142
+ if ch == '\\':
143
+ escape_mode = not escape_mode
144
+ else:
145
+ escape_mode = False
146
+
147
+ if ch == "'" and not escape_mode:
148
+ in_str = not in_str
149
+ elif not in_str:
150
+ if ch == '[':
151
+ num_open_bracket += 1
152
+ elif ch == ']':
153
+ num_open_bracket -= 1
154
+ _advance(1)
155
+
156
+ arg_str = s[arg_start:_pos() - 1]
157
+ return coding.evaluate(
158
+ '(' + arg_str + ')', permission=coding.CodePermission.BASIC
159
+ )
160
+
161
+ def _type_arg():
162
+ t_args = []
163
+ t_args.append(_maybe_type_list())
164
+ while _match(','):
165
+ t_args.append(_maybe_type_list())
166
+ return tuple(t_args) if len(t_args) > 1 else t_args[0]
167
+
168
+ def _maybe_type_list():
169
+ if _match('['):
170
+ ret = _type_arg()
171
+ if not _match(']'):
172
+ raise SyntaxError(
173
+ f'Expected "]" at position {_pos()}.\n\n' + _error_illustration()
174
+ )
175
+ return list(ret) if isinstance(ret, tuple) else [ret]
176
+ return _maybe_union()
177
+
178
+ def _type_id() -> str:
179
+ _skip_whitespaces()
180
+ if _match('...'):
181
+ return '...'
182
+ start = _pos()
183
+ while not _eof():
184
+ c = _next()
185
+ if c.isalnum() or c in '_.':
186
+ _advance(1)
187
+ else:
188
+ break
189
+ t_id = s[start:_pos()]
190
+ if not all(x.isidentifier() for x in t_id.split('.')):
191
+ raise SyntaxError(
192
+ f'Expected type identifier, got {t_id!r} at position {start}.\n\n'
193
+ + _error_illustration()
194
+ )
195
+ return t_id
196
+
197
+ def _resolve(type_id: str):
198
+
199
+ def _as_forward_ref() -> typing.ForwardRef:
200
+ return typing.ForwardRef(type_id, False, parent_module) # pytype: disable=not-callable
201
+
202
+ def _resolve_name(name: str, parent_obj: typing.Any):
203
+ if name == 'None':
204
+ return None, True
205
+ if parent_obj is not None and hasattr(parent_obj, name):
206
+ return getattr(parent_obj, name), False
207
+ if hasattr(builtins, name):
208
+ return getattr(builtins, name), True
209
+ if type_id == '...':
210
+ return ..., True
211
+ return utils.MISSING_VALUE, False
212
+
213
+ names = type_id.split('.')
214
+ if len(names) == 1:
215
+ reference, is_builtin = _resolve_name(names[0], parent_module)
216
+ if is_builtin:
217
+ return reference
218
+ if not is_builtin and (
219
+ # When reference is not found, we should treat it as a forward
220
+ # reference.
221
+ reference == utils.MISSING_VALUE
222
+ # When module is being reloaded, we should treat all non-builtin
223
+ # references as forward references.
224
+ or getattr(parent_module, '__reloading__', False)
225
+ ):
226
+ return _as_forward_ref()
227
+ return reference
228
+
229
+ root_obj, _ = _resolve_name(names[0], parent_module)
230
+ # When root object is not found, we should treat it as a forward reference.
231
+ if root_obj == utils.MISSING_VALUE:
232
+ return _as_forward_ref()
233
+
234
+ parent_obj = root_obj
235
+ # When root object is a module, we should treat reference to its children
236
+ # as non-forward references.
237
+ if inspect.ismodule(root_obj):
238
+ for name in names[1:]:
239
+ parent_obj, _ = _resolve_name(name, parent_obj)
240
+ if parent_obj == utils.MISSING_VALUE:
241
+ raise TypeError(f'{type_id!r} does not exist.')
242
+ return parent_obj
243
+ # When root object is non-module variable of current module, and when the
244
+ # module is being reloaded, we should treat reference to its children as
245
+ # forward references.
246
+ elif getattr(parent_module, '__reloading__', False):
247
+ return _as_forward_ref()
248
+ # When root object is non-module variable of current module, we should treat
249
+ # unresolved reference to its children as forward references.
250
+ else:
251
+ for name in names[1:]:
252
+ parent_obj, _ = _resolve_name(name, parent_obj)
253
+ if parent_obj == utils.MISSING_VALUE:
254
+ return _as_forward_ref()
255
+ return parent_obj
256
+
257
+ root = _maybe_union()
258
+ if _pos() != len(s):
259
+ raise SyntaxError(
260
+ 'Unexpected end of annotation.\n\n' + _error_illustration()
261
+ )
262
+ return root
263
+
264
+
38
265
  def _field_from_annotation(
39
266
  key: typing.Union[str, class_schema.KeySpec],
40
267
  annotation: typing.Any,
@@ -91,7 +318,12 @@ def _value_spec_from_type_annotation(
91
318
  parent_module: typing.Optional[types.ModuleType] = None
92
319
  ) -> class_schema.ValueSpec:
93
320
  """Creates a value spec from type annotation."""
94
- if annotation is bool:
321
+ if isinstance(annotation, str) and not accept_value_as_annotation:
322
+ annotation = annotation_from_str(annotation, parent_module)
323
+
324
+ if annotation is None:
325
+ return vs.Object(type(None))
326
+ elif annotation is bool:
95
327
  return vs.Bool()
96
328
  elif annotation is int:
97
329
  return vs.Int()
@@ -193,10 +425,7 @@ def _value_spec_from_type_annotation(
193
425
  elif (
194
426
  inspect.isclass(annotation)
195
427
  or pg_inspect.is_generic(annotation)
196
- or (isinstance(annotation, str) and not accept_value_as_annotation)
197
428
  ):
198
- if isinstance(annotation, str) and parent_module is not None:
199
- annotation = class_schema.ForwardRef(parent_module, annotation)
200
429
  return vs.Object(annotation)
201
430
 
202
431
  if accept_value_as_annotation:
@@ -227,11 +456,12 @@ def _value_spec_from_annotation(
227
456
  return annotation
228
457
  elif annotation == inspect.Parameter.empty:
229
458
  return vs.Any()
230
- elif annotation is None:
459
+
460
+ if annotation is None:
231
461
  if accept_value_as_annotation:
232
462
  return vs.Any().noneable()
233
463
  else:
234
- return vs.Any().freeze(None)
464
+ return vs.Object(type(None))
235
465
 
236
466
  if auto_typing:
237
467
  return _value_spec_from_type_annotation(
@@ -1,4 +1,4 @@
1
- # Copyright 2023 The PyGlove Authors
1
+ # Copyright 2025 The PyGlove Authors
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -11,13 +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.core.typing.annotation_conversion."""
15
-
16
14
  import inspect
17
15
  import sys
18
16
  import typing
19
17
  import unittest
20
18
 
19
+ from pyglove.core import coding
21
20
  from pyglove.core.typing import annotated
22
21
  from pyglove.core.typing import annotation_conversion
23
22
  from pyglove.core.typing import key_specs as ks
@@ -28,7 +27,168 @@ from pyglove.core.typing.class_schema import ValueSpec
28
27
 
29
28
 
30
29
  class Foo:
31
- pass
30
+ class Bar:
31
+ pass
32
+
33
+
34
+ _MODULE = sys.modules[__name__]
35
+
36
+
37
+ class AnnotationFromStrTest(unittest.TestCase):
38
+ """Tests for annotation_from_str."""
39
+
40
+ def test_basic_types(self):
41
+ self.assertIsNone(annotation_conversion.annotation_from_str('None'))
42
+ self.assertEqual(annotation_conversion.annotation_from_str('str'), str)
43
+ self.assertEqual(annotation_conversion.annotation_from_str('int'), int)
44
+ self.assertEqual(annotation_conversion.annotation_from_str('float'), float)
45
+ self.assertEqual(annotation_conversion.annotation_from_str('bool'), bool)
46
+ self.assertEqual(annotation_conversion.annotation_from_str('list'), list)
47
+ self.assertEqual(
48
+ annotation_conversion.annotation_from_str('list[int]'), list[int]
49
+ )
50
+ self.assertEqual(annotation_conversion.annotation_from_str('tuple'), tuple)
51
+ self.assertEqual(
52
+ annotation_conversion.annotation_from_str('tuple[int]'), tuple[int]
53
+ )
54
+ self.assertEqual(
55
+ annotation_conversion.annotation_from_str('tuple[int, ...]'),
56
+ tuple[int, ...]
57
+ )
58
+ self.assertEqual(
59
+ annotation_conversion.annotation_from_str('tuple[int, str]'),
60
+ tuple[int, str]
61
+ )
62
+ self.assertEqual(
63
+ annotation_conversion.annotation_from_str('list[Foo]', _MODULE),
64
+ list[Foo]
65
+ )
66
+ self.assertEqual(
67
+ annotation_conversion.annotation_from_str('list[Foo.Bar]', _MODULE),
68
+ list[Foo.Bar]
69
+ )
70
+ self.assertEqual(
71
+ annotation_conversion.annotation_from_str('list[Foo.Baz]', _MODULE),
72
+ list[typing.ForwardRef('Foo.Baz', False, _MODULE)]
73
+ )
74
+
75
+ def test_generic_types(self):
76
+ self.assertEqual(
77
+ annotation_conversion.annotation_from_str('typing.List[str]', _MODULE),
78
+ typing.List[str]
79
+ )
80
+
81
+ def test_union(self):
82
+ self.assertEqual(
83
+ annotation_conversion.annotation_from_str(
84
+ 'typing.Union[str, typing.Union[int, float]]', _MODULE),
85
+ typing.Union[str, int, float]
86
+ )
87
+ if sys.version_info >= (3, 10):
88
+ self.assertEqual(
89
+ annotation_conversion.annotation_from_str(
90
+ 'str | int | float', _MODULE),
91
+ typing.Union[str, int, float]
92
+ )
93
+
94
+ def test_literal(self):
95
+ self.assertEqual(
96
+ annotation_conversion.annotation_from_str(
97
+ 'typing.Literal[1, True, "a", \'"b"\', "\\"c\\"", "\\\\"]',
98
+ _MODULE
99
+ ),
100
+ typing.Literal[1, True, 'a', '"b"', '"c"', '\\']
101
+ )
102
+ self.assertEqual(
103
+ annotation_conversion.annotation_from_str(
104
+ 'typing.Literal[(1, 1), f"A {[1]}"]', _MODULE),
105
+ typing.Literal[(1, 1), 'A [1]']
106
+ )
107
+ with self.assertRaisesRegex(SyntaxError, 'Expected "\\["'):
108
+ annotation_conversion.annotation_from_str('typing.Literal', _MODULE)
109
+
110
+ with self.assertRaisesRegex(SyntaxError, 'Unexpected end of annotation'):
111
+ annotation_conversion.annotation_from_str('typing.Literal[1', _MODULE)
112
+
113
+ with self.assertRaisesRegex(
114
+ coding.CodeError, 'Function definition is not allowed'
115
+ ):
116
+ annotation_conversion.annotation_from_str(
117
+ 'typing.Literal[lambda x: x]', _MODULE
118
+ )
119
+
120
+ def test_callable(self):
121
+ self.assertEqual(
122
+ annotation_conversion.annotation_from_str(
123
+ 'typing.Callable[int, int]', _MODULE),
124
+ typing.Callable[[int], int]
125
+ )
126
+ self.assertEqual(
127
+ annotation_conversion.annotation_from_str(
128
+ 'typing.Callable[[int], int]', _MODULE),
129
+ typing.Callable[[int], int]
130
+ )
131
+ self.assertEqual(
132
+ annotation_conversion.annotation_from_str(
133
+ 'typing.Callable[..., None]', _MODULE),
134
+ typing.Callable[..., None]
135
+ )
136
+
137
+ def test_forward_ref(self):
138
+ self.assertEqual(
139
+ annotation_conversion.annotation_from_str(
140
+ 'AAA', _MODULE),
141
+ typing.ForwardRef(
142
+ 'AAA', False, _MODULE
143
+ )
144
+ )
145
+ self.assertEqual(
146
+ annotation_conversion.annotation_from_str(
147
+ 'typing.List[AAA]', _MODULE),
148
+ typing.List[
149
+ typing.ForwardRef(
150
+ 'AAA', False, _MODULE
151
+ )
152
+ ]
153
+ )
154
+
155
+ def test_reloading(self):
156
+ setattr(_MODULE, '__reloading__', True)
157
+ self.assertEqual(
158
+ annotation_conversion.annotation_from_str(
159
+ 'typing.List[Foo]', _MODULE),
160
+ typing.List[
161
+ typing.ForwardRef(
162
+ 'Foo', False, _MODULE
163
+ )
164
+ ]
165
+ )
166
+ self.assertEqual(
167
+ annotation_conversion.annotation_from_str(
168
+ 'typing.List[Foo.Bar]', _MODULE),
169
+ typing.List[
170
+ typing.ForwardRef(
171
+ 'Foo.Bar', False, _MODULE
172
+ )
173
+ ]
174
+ )
175
+ delattr(_MODULE, '__reloading__')
176
+
177
+ def test_bad_annotation(self):
178
+ with self.assertRaisesRegex(SyntaxError, 'Expected type identifier'):
179
+ annotation_conversion.annotation_from_str('typing.List[]')
180
+
181
+ with self.assertRaisesRegex(SyntaxError, 'Expected "]"'):
182
+ annotation_conversion.annotation_from_str('typing.List[int')
183
+
184
+ with self.assertRaisesRegex(SyntaxError, 'Unexpected end of annotation'):
185
+ annotation_conversion.annotation_from_str('typing.List[int]1', _MODULE)
186
+
187
+ with self.assertRaisesRegex(SyntaxError, 'Expected "]"'):
188
+ annotation_conversion.annotation_from_str('typing.Callable[[x')
189
+
190
+ with self.assertRaisesRegex(TypeError, '.* does not exist'):
191
+ annotation_conversion.annotation_from_str('typing.Foo', _MODULE)
32
192
 
33
193
 
34
194
  class FieldFromAnnotationTest(unittest.TestCase):
@@ -132,17 +292,24 @@ class ValueSpecFromAnnotationTest(unittest.TestCase):
132
292
 
133
293
  def test_none(self):
134
294
  self.assertEqual(
135
- ValueSpec.from_annotation(None, False), vs.Any().freeze(None))
295
+ ValueSpec.from_annotation(None, False), vs.Object(type(None)))
136
296
  self.assertEqual(
137
- ValueSpec.from_annotation(None, True), vs.Any().freeze(None))
297
+ ValueSpec.from_annotation('None', True), vs.Object(type(None)))
138
298
  self.assertEqual(
139
- ValueSpec.from_annotation(
140
- None, accept_value_as_annotation=True), vs.Any().noneable())
299
+ ValueSpec.from_annotation(None, True), vs.Object(type(None)))
300
+ self.assertEqual(
301
+ ValueSpec.from_annotation(None, accept_value_as_annotation=True),
302
+ vs.Any().noneable()
303
+ )
141
304
 
142
305
  def test_any(self):
143
306
  self.assertEqual(
144
307
  ValueSpec.from_annotation(typing.Any, False),
145
308
  vs.Any(annotation=typing.Any))
309
+ self.assertEqual(
310
+ ValueSpec.from_annotation('typing.Any', True, parent_module=_MODULE),
311
+ vs.Any(annotation=typing.Any)
312
+ )
146
313
  self.assertEqual(
147
314
  ValueSpec.from_annotation(typing.Any, True),
148
315
  vs.Any(annotation=typing.Any))
@@ -152,6 +319,7 @@ class ValueSpecFromAnnotationTest(unittest.TestCase):
152
319
 
153
320
  def test_bool(self):
154
321
  self.assertEqual(ValueSpec.from_annotation(bool, True), vs.Bool())
322
+ self.assertEqual(ValueSpec.from_annotation('bool', True), vs.Bool())
155
323
  self.assertEqual(
156
324
  ValueSpec.from_annotation(bool, False), vs.Any(annotation=bool))
157
325
  self.assertEqual(
@@ -159,6 +327,7 @@ class ValueSpecFromAnnotationTest(unittest.TestCase):
159
327
 
160
328
  def test_int(self):
161
329
  self.assertEqual(ValueSpec.from_annotation(int, True), vs.Int())
330
+ self.assertEqual(ValueSpec.from_annotation('int', True), vs.Int())
162
331
  self.assertEqual(ValueSpec.from_annotation(int, True, True), vs.Int())
163
332
  self.assertEqual(
164
333
  ValueSpec.from_annotation(int, False), vs.Any(annotation=int))
@@ -182,7 +351,9 @@ class ValueSpecFromAnnotationTest(unittest.TestCase):
182
351
  ValueSpec.from_annotation(str, False, True), vs.Any(annotation=str))
183
352
 
184
353
  self.assertEqual(
185
- ValueSpec.from_annotation('A', False, False), vs.Any(annotation='A'))
354
+ ValueSpec.from_annotation('A', False, False),
355
+ vs.Any(annotation='A')
356
+ )
186
357
  self.assertEqual(
187
358
  ValueSpec.from_annotation('A', False, True), vs.Str('A'))
188
359
  self.assertEqual(
@@ -0,0 +1,135 @@
1
+ # Copyright 2025 The PyGlove Authors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+ import sys
17
+ import typing
18
+ from typing import List, Literal, Union
19
+ import unittest
20
+
21
+ from pyglove.core import symbolic as pg
22
+ from pyglove.core.typing import key_specs as ks
23
+ from pyglove.core.typing import value_specs as vs
24
+
25
+
26
+ class AnnotationFutureConversionTest(unittest.TestCase):
27
+
28
+ # Class with forward declaration must not be defined in functions.
29
+ class A(pg.Object):
30
+ a: typing.Optional[AnnotationFutureConversionTest.A]
31
+ b: List[AnnotationFutureConversionTest.A]
32
+
33
+ def assert_value_spec(self, cls, field_name, expected_value_spec):
34
+ self.assertEqual(cls.__schema__[field_name].value, expected_value_spec)
35
+
36
+ def test_basics(self):
37
+
38
+ class Foo(pg.Object):
39
+ a: int
40
+ b: float
41
+ c: bool
42
+ d: str
43
+ e: typing.Any
44
+ f: typing.Dict[str, typing.Any]
45
+ g: typing.List[str]
46
+ h: typing.Tuple[int, int]
47
+ i: typing.Callable[[int, int], None]
48
+
49
+ self.assert_value_spec(Foo, 'a', vs.Int())
50
+ self.assert_value_spec(Foo, 'b', vs.Float())
51
+ self.assert_value_spec(Foo, 'c', vs.Bool())
52
+ self.assert_value_spec(Foo, 'd', vs.Str())
53
+ self.assert_value_spec(Foo, 'e', vs.Any(annotation=typing.Any))
54
+ self.assert_value_spec(
55
+ Foo, 'f', vs.Dict([(ks.StrKey(), vs.Any(annotation=typing.Any))])
56
+ )
57
+ self.assert_value_spec(Foo, 'g', vs.List(vs.Str()))
58
+ self.assert_value_spec(Foo, 'h', vs.Tuple([vs.Int(), vs.Int()]))
59
+ self.assert_value_spec(
60
+ Foo, 'i',
61
+ vs.Callable([vs.Int(), vs.Int()], returns=vs.Object(type(None)))
62
+ )
63
+
64
+ def test_list(self):
65
+ if sys.version_info >= (3, 10):
66
+
67
+ class Bar(pg.Object):
68
+ x: list[int | None]
69
+
70
+ self.assert_value_spec(Bar, 'x', vs.List(vs.Int().noneable()))
71
+
72
+ def test_var_length_tuple(self):
73
+
74
+ class Foo(pg.Object):
75
+ x: typing.Tuple[int, ...]
76
+
77
+ self.assert_value_spec(Foo, 'x', vs.Tuple(vs.Int()))
78
+
79
+ if sys.version_info >= (3, 10):
80
+
81
+ class Bar(pg.Object):
82
+ x: tuple[int, ...]
83
+
84
+ self.assert_value_spec(Bar, 'x', vs.Tuple(vs.Int()))
85
+
86
+ def test_optional(self):
87
+
88
+ class Foo(pg.Object):
89
+ x: typing.Optional[int]
90
+
91
+ self.assert_value_spec(Foo, 'x', vs.Int().noneable())
92
+
93
+ if sys.version_info >= (3, 10):
94
+ class Bar(pg.Object):
95
+ x: int | None
96
+
97
+ self.assert_value_spec(Bar, 'x', vs.Int().noneable())
98
+
99
+ def test_union(self):
100
+
101
+ class Foo(pg.Object):
102
+ x: Union[int, typing.Union[str, bool], None]
103
+
104
+ self.assert_value_spec(
105
+ Foo, 'x', vs.Union([vs.Int(), vs.Str(), vs.Bool()]).noneable()
106
+ )
107
+
108
+ if sys.version_info >= (3, 10):
109
+
110
+ class Bar(pg.Object):
111
+ x: int | str | bool
112
+
113
+ self.assert_value_spec(
114
+ Bar, 'x', vs.Union([vs.Int(), vs.Str(), vs.Bool()])
115
+ )
116
+
117
+ def test_literal(self):
118
+
119
+ class Foo(pg.Object):
120
+ x: Literal[1, True, 'abc']
121
+
122
+ self.assert_value_spec(
123
+ Foo, 'x', vs.Enum(vs.MISSING_VALUE, [1, True, 'abc'])
124
+ )
125
+
126
+ def test_self_referencial(self):
127
+ self.assert_value_spec(
128
+ self.A, 'a', vs.Object(self.A).noneable()
129
+ )
130
+ self.assert_value_spec(
131
+ self.A, 'b', vs.List(vs.Object(self.A))
132
+ )
133
+
134
+ if __name__ == '__main__':
135
+ unittest.main()