pyglove 0.5.0.dev202510080811__tar.gz → 0.5.0.dev202511030811__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.

Potentially problematic release.


This version of pyglove might be problematic. Click here for more details.

Files changed (231) hide show
  1. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/PKG-INFO +1 -1
  2. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/geno/base.py +7 -3
  3. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/monitoring.py +194 -93
  4. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/monitoring_test.py +75 -35
  5. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/base.py +70 -31
  6. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/base_test.py +3 -3
  7. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/dict.py +31 -12
  8. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/dict_test.py +49 -0
  9. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/list.py +17 -3
  10. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/list_test.py +24 -2
  11. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/object.py +1 -0
  12. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/ref.py +19 -7
  13. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/ref_test.py +94 -7
  14. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/annotation_conversion.py +8 -1
  15. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/annotation_conversion_test.py +14 -19
  16. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/class_schema.py +24 -1
  17. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/json_schema.py +221 -8
  18. pyglove-0.5.0.dev202511030811/pyglove/core/typing/json_schema_test.py +973 -0
  19. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/type_conversion.py +17 -3
  20. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/type_conversion_test.py +1 -1
  21. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/__init__.py +1 -0
  22. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/contextual.py +9 -4
  23. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/contextual_test.py +10 -0
  24. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/json_conversion.py +255 -16
  25. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/json_conversion_test.py +63 -4
  26. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/views/html/controls/tab.py +33 -0
  27. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/views/html/controls/tab_test.py +37 -0
  28. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/evolution/base_test.py +1 -1
  29. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove.egg-info/PKG-INFO +1 -1
  30. pyglove-0.5.0.dev202510080811/pyglove/core/typing/json_schema_test.py +0 -477
  31. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/LICENSE +0 -0
  32. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/README.md +0 -0
  33. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/__init__.py +0 -0
  34. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/__init__.py +0 -0
  35. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/coding/__init__.py +0 -0
  36. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/coding/errors.py +0 -0
  37. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/coding/errors_test.py +0 -0
  38. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/coding/execution.py +0 -0
  39. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/coding/execution_test.py +0 -0
  40. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/coding/function_generation.py +0 -0
  41. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/coding/function_generation_test.py +0 -0
  42. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/coding/parsing.py +0 -0
  43. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/coding/parsing_test.py +0 -0
  44. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/coding/permissions.py +0 -0
  45. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/coding/permissions_test.py +0 -0
  46. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/detouring/__init__.py +0 -0
  47. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/detouring/class_detour.py +0 -0
  48. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/detouring/class_detour_test.py +0 -0
  49. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/geno/__init__.py +0 -0
  50. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/geno/base_test.py +0 -0
  51. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/geno/categorical.py +0 -0
  52. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/geno/categorical_test.py +0 -0
  53. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/geno/custom.py +0 -0
  54. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/geno/custom_test.py +0 -0
  55. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/geno/deduping.py +0 -0
  56. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/geno/deduping_test.py +0 -0
  57. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/geno/dna_generator.py +0 -0
  58. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/geno/dna_generator_test.py +0 -0
  59. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/geno/numerical.py +0 -0
  60. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/geno/numerical_test.py +0 -0
  61. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/geno/random.py +0 -0
  62. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/geno/random_test.py +0 -0
  63. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/geno/space.py +0 -0
  64. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/geno/space_test.py +0 -0
  65. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/geno/sweeping.py +0 -0
  66. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/geno/sweeping_test.py +0 -0
  67. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/hyper/__init__.py +0 -0
  68. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/hyper/base.py +0 -0
  69. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/hyper/categorical.py +0 -0
  70. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/hyper/categorical_test.py +0 -0
  71. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/hyper/custom.py +0 -0
  72. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/hyper/custom_test.py +0 -0
  73. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/hyper/derived.py +0 -0
  74. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/hyper/derived_test.py +0 -0
  75. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/hyper/dynamic_evaluation.py +0 -0
  76. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/hyper/dynamic_evaluation_test.py +0 -0
  77. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/hyper/evolvable.py +0 -0
  78. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/hyper/evolvable_test.py +0 -0
  79. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/hyper/iter.py +0 -0
  80. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/hyper/iter_test.py +0 -0
  81. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/hyper/numerical.py +0 -0
  82. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/hyper/numerical_test.py +0 -0
  83. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/hyper/object_template.py +0 -0
  84. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/hyper/object_template_test.py +0 -0
  85. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/io/__init__.py +0 -0
  86. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/io/file_system.py +0 -0
  87. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/io/file_system_test.py +0 -0
  88. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/io/sequence.py +0 -0
  89. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/io/sequence_test.py +0 -0
  90. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/logging.py +0 -0
  91. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/logging_test.py +0 -0
  92. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/patching/__init__.py +0 -0
  93. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/patching/object_factory.py +0 -0
  94. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/patching/object_factory_test.py +0 -0
  95. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/patching/pattern_based.py +0 -0
  96. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/patching/pattern_based_test.py +0 -0
  97. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/patching/rule_based.py +0 -0
  98. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/patching/rule_based_test.py +0 -0
  99. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/__init__.py +0 -0
  100. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/boilerplate.py +0 -0
  101. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/boilerplate_test.py +0 -0
  102. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/class_wrapper.py +0 -0
  103. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/class_wrapper_test.py +0 -0
  104. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/compounding.py +0 -0
  105. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/compounding_test.py +0 -0
  106. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/contextual_object.py +0 -0
  107. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/contextual_object_test.py +0 -0
  108. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/diff.py +0 -0
  109. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/diff_test.py +0 -0
  110. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/error_info.py +0 -0
  111. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/error_info_test.py +0 -0
  112. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/flags.py +0 -0
  113. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/flags_test.py +0 -0
  114. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/functor.py +0 -0
  115. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/functor_test.py +0 -0
  116. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/inferred.py +0 -0
  117. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/inferred_test.py +0 -0
  118. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/object_test.py +0 -0
  119. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/origin.py +0 -0
  120. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/origin_test.py +0 -0
  121. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/pure_symbolic.py +0 -0
  122. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/symbolize.py +0 -0
  123. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/symbolic/symbolize_test.py +0 -0
  124. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/tuning/__init__.py +0 -0
  125. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/tuning/backend.py +0 -0
  126. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/tuning/backend_test.py +0 -0
  127. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/tuning/early_stopping.py +0 -0
  128. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/tuning/local_backend.py +0 -0
  129. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/tuning/protocols.py +0 -0
  130. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/tuning/protocols_test.py +0 -0
  131. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/tuning/sample.py +0 -0
  132. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/tuning/sample_test.py +0 -0
  133. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/__init__.py +0 -0
  134. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/annotated.py +0 -0
  135. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/annotated_test.py +0 -0
  136. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/annotation_future_test.py +0 -0
  137. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/callable_ext.py +0 -0
  138. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/callable_ext_test.py +0 -0
  139. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/callable_signature.py +0 -0
  140. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/callable_signature_test.py +0 -0
  141. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/class_schema_test.py +0 -0
  142. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/custom_typing.py +0 -0
  143. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/inspect.py +0 -0
  144. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/inspect_test.py +0 -0
  145. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/key_specs.py +0 -0
  146. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/key_specs_test.py +0 -0
  147. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/pytype_support.py +0 -0
  148. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/typed_missing.py +0 -0
  149. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/typed_missing_test.py +0 -0
  150. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/value_specs.py +0 -0
  151. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/typing/value_specs_test.py +0 -0
  152. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/common_traits.py +0 -0
  153. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/common_traits_test.py +0 -0
  154. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/docstr_utils.py +0 -0
  155. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/docstr_utils_test.py +0 -0
  156. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/error_utils.py +0 -0
  157. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/error_utils_test.py +0 -0
  158. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/formatting.py +0 -0
  159. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/formatting_test.py +0 -0
  160. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/hierarchical.py +0 -0
  161. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/hierarchical_test.py +0 -0
  162. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/missing.py +0 -0
  163. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/missing_test.py +0 -0
  164. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/text_color.py +0 -0
  165. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/text_color_test.py +0 -0
  166. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/thread_local.py +0 -0
  167. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/thread_local_test.py +0 -0
  168. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/timing.py +0 -0
  169. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/timing_test.py +0 -0
  170. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/value_location.py +0 -0
  171. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/utils/value_location_test.py +0 -0
  172. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/views/__init__.py +0 -0
  173. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/views/base.py +0 -0
  174. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/views/base_test.py +0 -0
  175. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/views/html/__init__.py +0 -0
  176. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/views/html/base.py +0 -0
  177. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/views/html/base_test.py +0 -0
  178. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/views/html/controls/__init__.py +0 -0
  179. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/views/html/controls/base.py +0 -0
  180. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/views/html/controls/label.py +0 -0
  181. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/views/html/controls/label_test.py +0 -0
  182. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/views/html/controls/progress_bar.py +0 -0
  183. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/views/html/controls/progress_bar_test.py +0 -0
  184. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/views/html/controls/tooltip.py +0 -0
  185. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/views/html/controls/tooltip_test.py +0 -0
  186. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/views/html/tree_view.py +0 -0
  187. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/core/views/html/tree_view_test.py +0 -0
  188. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/__init__.py +0 -0
  189. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/early_stopping/__init__.py +0 -0
  190. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/early_stopping/base.py +0 -0
  191. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/early_stopping/base_test.py +0 -0
  192. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/early_stopping/step_wise.py +0 -0
  193. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/early_stopping/step_wise_test.py +0 -0
  194. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/evolution/__init__.py +0 -0
  195. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/evolution/base.py +0 -0
  196. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/evolution/hill_climb.py +0 -0
  197. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/evolution/hill_climb_test.py +0 -0
  198. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/evolution/mutators.py +0 -0
  199. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/evolution/mutators_test.py +0 -0
  200. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/evolution/neat.py +0 -0
  201. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/evolution/neat_test.py +0 -0
  202. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/evolution/nsga2.py +0 -0
  203. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/evolution/nsga2_test.py +0 -0
  204. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/evolution/recombinators.py +0 -0
  205. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/evolution/recombinators_test.py +0 -0
  206. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/evolution/regularized_evolution.py +0 -0
  207. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/evolution/regularized_evolution_test.py +0 -0
  208. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/evolution/selectors.py +0 -0
  209. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/evolution/selectors_test.py +0 -0
  210. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/evolution/where.py +0 -0
  211. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/evolution/where_test.py +0 -0
  212. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/mutfun/__init__.py +0 -0
  213. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/mutfun/base.py +0 -0
  214. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/mutfun/base_test.py +0 -0
  215. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/mutfun/basic_ops.py +0 -0
  216. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/mutfun/basic_ops_test.py +0 -0
  217. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/scalars/__init__.py +0 -0
  218. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/scalars/base.py +0 -0
  219. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/scalars/base_test.py +0 -0
  220. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/scalars/maths.py +0 -0
  221. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/scalars/maths_test.py +0 -0
  222. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/scalars/randoms.py +0 -0
  223. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/scalars/randoms_test.py +0 -0
  224. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/scalars/step_wise.py +0 -0
  225. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove/ext/scalars/step_wise_test.py +0 -0
  226. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove.egg-info/SOURCES.txt +0 -0
  227. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove.egg-info/dependency_links.txt +0 -0
  228. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove.egg-info/requires.txt +0 -0
  229. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/pyglove.egg-info/top_level.txt +0 -0
  230. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/setup.cfg +0 -0
  231. {pyglove-0.5.0.dev202510080811 → pyglove-0.5.0.dev202511030811}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyglove
3
- Version: 0.5.0.dev202510080811
3
+ Version: 0.5.0.dev202511030811
4
4
  Summary: PyGlove: A library for manipulating Python objects.
5
5
  Home-page: https://github.com/google/pyglove
6
6
  Author: PyGlove Authors
@@ -1447,6 +1447,7 @@ class DNA(symbolic.Object):
1447
1447
  *,
1448
1448
  allow_partial: bool = False,
1449
1449
  root_path: Optional[utils.KeyPath] = None,
1450
+ **kwargs,
1450
1451
  ) -> 'DNA':
1451
1452
  """Class method that load a DNA from a JSON value.
1452
1453
 
@@ -1454,6 +1455,7 @@ class DNA(symbolic.Object):
1454
1455
  json_value: Input JSON value, only JSON dict is acceptable.
1455
1456
  allow_partial: Whether to allow elements of the list to be partial.
1456
1457
  root_path: KeyPath of loaded object in its object tree.
1458
+ **kwargs: Keyword arguments that will be passed to symbolic.from_json.
1457
1459
 
1458
1460
  Returns:
1459
1461
  A DNA object.
@@ -1463,16 +1465,18 @@ class DNA(symbolic.Object):
1463
1465
  # NOTE(daiyip): DNA.parse will validate the input. Therefore, we can
1464
1466
  # disable runtime type check during constructing the DNA objects.
1465
1467
  with symbolic.enable_type_check(False):
1466
- dna = DNA.parse(symbolic.from_json(json_value.get('value')))
1468
+ dna = DNA.parse(symbolic.from_json(json_value.get('value'), **kwargs))
1467
1469
  if 'metadata' in json_value:
1468
1470
  dna.rebind(
1469
- metadata=symbolic.from_json(json_value.get('metadata')),
1471
+ metadata=symbolic.from_json(json_value.get('metadata'), **kwargs),
1470
1472
  raise_on_no_change=False, skip_notification=True)
1471
1473
  else:
1472
1474
  dna = super(DNA, cls).from_json(
1473
1475
  json_value,
1474
1476
  allow_partial=allow_partial,
1475
- root_path=root_path) # pytype: disable=bad-return-type
1477
+ root_path=root_path,
1478
+ **kwargs,
1479
+ ) # pytype: disable=bad-return-type
1476
1480
  assert isinstance(dna, DNA)
1477
1481
  if cloneable_metadata_keys:
1478
1482
  dna._cloneable_metadata_keys = set(cloneable_metadata_keys) # pylint: disable=protected-access
@@ -14,7 +14,17 @@
14
14
  """Pluggable metric systems for monitoring.
15
15
 
16
16
  This module allows PyGlove to plugin metrics to monitor the execution of
17
- programs.
17
+ programs. There are three common metrics for monitoring: counters, scalars, and
18
+ distributions.
19
+
20
+ * Counters are metrics that track the number of times an event occurs. It's
21
+ monotonically increasing over time.
22
+
23
+ * Scalars are metrics that track a single value at a given time, for example,
24
+ available memory size. It does not accumulate over time like counters.
25
+
26
+ * Distributions are metrics that track the distribution of a numerical value.
27
+ For example, the latency of an operation.
18
28
  """
19
29
 
20
30
  import abc
@@ -24,7 +34,7 @@ import math
24
34
  import threading
25
35
  import time
26
36
  import typing
27
- from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple, Type, Union
37
+ from typing import Any, Dict, Generic, Iterator, List, Optional, Tuple, Type, TypeVar, Union
28
38
  from pyglove.core.utils import error_utils
29
39
 
30
40
  try:
@@ -33,7 +43,10 @@ except ImportError:
33
43
  numpy = None
34
44
 
35
45
 
36
- class Metric(metaclass=abc.ABCMeta):
46
+ MetricValueType = TypeVar('MetricValueType')
47
+
48
+
49
+ class Metric(Generic[MetricValueType], metaclass=abc.ABCMeta):
37
50
  """Base class for metrics."""
38
51
 
39
52
  def __init__(
@@ -41,12 +54,23 @@ class Metric(metaclass=abc.ABCMeta):
41
54
  namespace: str,
42
55
  name: str,
43
56
  description: str,
44
- parameter_definitions: Dict[str, Type[Union[int, str, bool]]]
57
+ parameter_definitions: Dict[str, Type[Union[int, str, bool]]],
58
+ **additional_flags,
45
59
  ) -> None:
60
+ """Initializes the metric.
61
+
62
+ Args:
63
+ namespace: The namespace of the metric.
64
+ name: The name of the metric.
65
+ description: The description of the metric.
66
+ parameter_definitions: The definitions of the parameters for the metric.
67
+ **additional_flags: Additional flags for the metric.
68
+ """
46
69
  self._namespace = namespace
47
70
  self._name = name
48
71
  self._description = description
49
72
  self._parameter_definitions = parameter_definitions
73
+ self._flags = additional_flags
50
74
 
51
75
  @property
52
76
  def namespace(self) -> str:
@@ -73,6 +97,11 @@ class Metric(metaclass=abc.ABCMeta):
73
97
  """Returns the parameter definitions of the metric."""
74
98
  return self._parameter_definitions
75
99
 
100
+ @property
101
+ def flags(self) -> Dict[str, Any]:
102
+ """Returns the flags of the metric."""
103
+ return self._flags
104
+
76
105
  def _parameters_key(self, **parameters) -> Tuple[Any, ...]:
77
106
  """Returns the parameters tuple for the metric."""
78
107
  for k, t in self._parameter_definitions.items():
@@ -96,9 +125,24 @@ class Metric(metaclass=abc.ABCMeta):
96
125
  )
97
126
  return tuple(parameters[k] for k in self._parameter_definitions)
98
127
 
128
+ @abc.abstractmethod
129
+ def value(self, **parameters) -> MetricValueType:
130
+ """Returns the value of the metric for the given parameters.
131
+
132
+ Args:
133
+ **parameters: Parameters for parameterized counters.
134
+
135
+ Returns:
136
+ The value of the metric.
137
+ """
138
+
139
+
140
+ class Counter(Metric[int]):
141
+ """Base class for counters.
99
142
 
100
- class Counter(Metric):
101
- """Base class for counters."""
143
+ Counters are metrics that track the number of times an event occurs. It's
144
+ monotonically increasing over time.
145
+ """
102
146
 
103
147
  @abc.abstractmethod
104
148
  def increment(self, delta: int = 1, **parameters) -> int:
@@ -112,20 +156,42 @@ class Counter(Metric):
112
156
  The new value of the counter.
113
157
  """
114
158
 
159
+
160
+ class Scalar(Metric[MetricValueType]):
161
+ """Base class for scalar values.
162
+
163
+ Scalar values are metrics that track a single value at a given time, for
164
+ example, available memory size. It does not accumulate over time like
165
+ counters.
166
+ """
167
+
115
168
  @abc.abstractmethod
116
- def value(self, **parameters) -> int:
117
- """Returns the value of the counter for the given parameters.
169
+ def set(self, value: MetricValueType, **parameters) -> None:
170
+ """Sets the value of the scalar.
171
+
172
+ Args:
173
+ value: The value to record.
174
+ **parameters: Parameters for parameterized scalars.
175
+ """
176
+
177
+ @abc.abstractmethod
178
+ def increment(
179
+ self,
180
+ delta: MetricValueType = 1, **parameters
181
+ ) -> MetricValueType:
182
+ """Increments the scalar by delta and returns the new value.
118
183
 
119
184
  Args:
185
+ delta: The amount to increment the counter by.
120
186
  **parameters: Parameters for parameterized counters.
121
187
 
122
188
  Returns:
123
- The value of the counter.
189
+ The new value of the metric.
124
190
  """
125
191
 
126
192
 
127
- class Distribution(metaclass=abc.ABCMeta):
128
- """Distribution of scalar values."""
193
+ class DistributionValue(metaclass=abc.ABCMeta):
194
+ """Base for distribution value."""
129
195
 
130
196
  @property
131
197
  @abc.abstractmethod
@@ -173,27 +239,20 @@ class Distribution(metaclass=abc.ABCMeta):
173
239
  """Returns the fraction of values in the distribution less than value."""
174
240
 
175
241
 
176
- class Scalar(Metric):
177
- """Base class for scalar values."""
242
+ class Distribution(Metric[DistributionValue]):
243
+ """Base class for distributional metrics.
178
244
 
179
- @abc.abstractmethod
180
- def record(self, value: int, **parameters) -> None:
181
- """Records a value to the scalar.
182
-
183
- Args:
184
- value: The value to record.
185
- **parameters: Parameters for parameterized scalars.
186
- """
245
+ Distributions are metrics that track the distribution of a numerical value.
246
+ For example, the latency of an operation.
247
+ """
187
248
 
188
249
  @abc.abstractmethod
189
- def distribution(self, **parameters) -> Distribution:
190
- """Returns the distribution of the scalar.
250
+ def record(self, value: float, **parameters) -> None:
251
+ """Records a value to the distribution.
191
252
 
192
253
  Args:
193
- **parameters: Parameters for parameterized scalars.
194
-
195
- Returns:
196
- The distribution of the scalar.
254
+ value: The value to record.
255
+ **parameters: Parameters for parameterized distributions.
197
256
  """
198
257
 
199
258
  @contextlib.contextmanager
@@ -203,14 +262,14 @@ class Scalar(Metric):
203
262
  scale: int = 1000,
204
263
  error_parameter: str = 'error',
205
264
  **parameters) -> Iterator[None]:
206
- """Context manager that records the duration of code block to the scalar.
265
+ """Context manager that records the duration of code block.
207
266
 
208
267
  Args:
209
268
  scale: The scale of the duration.
210
269
  error_parameter: The parameter name for recording the error. If the name
211
- is not defined as a parameter for the scalar, the error tag will not be
212
- recorded.
213
- **parameters: Parameters for parameterized scalars.
270
+ is not defined as a parameter for the distribution, the error tag will
271
+ not be recorded.
272
+ **parameters: Parameters for parameterized distributions.
214
273
  """
215
274
  start_time = time.time()
216
275
  error = None
@@ -226,12 +285,16 @@ class Scalar(Metric):
226
285
  error_utils.ErrorInfo.from_exception(error).tag
227
286
  if error is not None else ''
228
287
  )
229
- self.record(int(duration), **parameters)
288
+ self.record(duration, **parameters)
230
289
 
231
290
 
232
291
  class MetricCollection(metaclass=abc.ABCMeta):
233
292
  """Base class for counter collections."""
234
293
 
294
+ _COUNTER_CLASS = Counter
295
+ _SCALAR_CLASS = Scalar
296
+ _DISTRIBUTION_CLASS = Distribution
297
+
235
298
  def __init__(
236
299
  self,
237
300
  namespace: str,
@@ -266,13 +329,10 @@ class MetricCollection(metaclass=abc.ABCMeta):
266
329
  def _get_or_create_metric(
267
330
  self,
268
331
  metric_cls: Type[Metric],
269
- create_metric_fn: Callable[
270
- [str, str, Dict[str, Type[Union[int, str, bool]]]],
271
- Metric
272
- ],
273
332
  name: str,
274
333
  description: str,
275
- parameter_definitions: Dict[str, Type[Union[int, str, bool]]]
334
+ parameter_definitions: Dict[str, Type[Union[int, str, bool]]],
335
+ **additional_flags,
276
336
  ) -> Metric:
277
337
  """Gets or creates a metric with the given name."""
278
338
  full_name = f'{self._namespace}/{name}'
@@ -294,7 +354,13 @@ class MetricCollection(metaclass=abc.ABCMeta):
294
354
  f'definitions ({metric.parameter_definitions!r}).'
295
355
  )
296
356
  else:
297
- metric = create_metric_fn(name, description, parameter_definitions)
357
+ metric = metric_cls(
358
+ self.namespace,
359
+ name,
360
+ description,
361
+ parameter_definitions,
362
+ **additional_flags
363
+ )
298
364
  self._metrics[full_name] = metric
299
365
  return metric
300
366
 
@@ -303,7 +369,7 @@ class MetricCollection(metaclass=abc.ABCMeta):
303
369
  name: str,
304
370
  description: str,
305
371
  parameters: Optional[Dict[str, Type[Union[int, str, bool]]]] = None,
306
- **kwargs
372
+ **additional_flags
307
373
  ) -> Counter:
308
374
  """Gets or creates a counter with the given name.
309
375
 
@@ -312,7 +378,9 @@ class MetricCollection(metaclass=abc.ABCMeta):
312
378
  description: The description of the counter.
313
379
  parameters: The definitions of the parameters for the counter.
314
380
  `default_parameters` from the collection will be used if not specified.
315
- **kwargs: Additional arguments for creating the counter.
381
+ **additional_flags: Additional arguments for creating the counter.
382
+ Subclasses can use these arguments to provide additional information for
383
+ creating the counter.
316
384
 
317
385
  Returns:
318
386
  The counter with the given name.
@@ -320,28 +388,23 @@ class MetricCollection(metaclass=abc.ABCMeta):
320
388
  if parameters is None:
321
389
  parameters = self._default_parameter_definitions
322
390
  return typing.cast(
323
- Counter, self._get_or_create_metric(
324
- Counter, self._create_counter, name, description, parameters,
325
- **kwargs
391
+ Counter,
392
+ self._get_or_create_metric(
393
+ self._COUNTER_CLASS,
394
+ name,
395
+ description,
396
+ parameters,
397
+ **additional_flags
326
398
  )
327
399
  )
328
400
 
329
- @abc.abstractmethod
330
- def _create_counter(
331
- self,
332
- name: str,
333
- description: str,
334
- parameter_definitions: Dict[str, Type[Union[int, str, bool]]],
335
- **kwargs
336
- ) -> Counter:
337
- """Creates a counter with the given name."""
338
-
339
401
  def get_scalar(
340
402
  self,
341
403
  name: str,
342
404
  description: str,
343
405
  parameters: Optional[Dict[str, Type[Union[int, str, bool]]]] = None,
344
- **kwargs
406
+ value_type: Type[Union[int, float]] = int,
407
+ **additional_flags
345
408
  ) -> Scalar:
346
409
  """Gets or creates a scalar with the given name.
347
410
 
@@ -350,7 +413,8 @@ class MetricCollection(metaclass=abc.ABCMeta):
350
413
  description: The description of the counter.
351
414
  parameters: The definitions of the parameters for the counter.
352
415
  `default_parameters` from the collection will be used if not specified.
353
- **kwargs: Additional arguments for creating the scalar.
416
+ value_type: The type of the value for the scalar.
417
+ **additional_flags: Additional arguments for creating the scalar.
354
418
 
355
419
  Returns:
356
420
  The counter with the given name.
@@ -360,19 +424,46 @@ class MetricCollection(metaclass=abc.ABCMeta):
360
424
  return typing.cast(
361
425
  Scalar,
362
426
  self._get_or_create_metric(
363
- Scalar, self._create_scalar, name, description, parameters, **kwargs
427
+ self._SCALAR_CLASS,
428
+ name,
429
+ description,
430
+ parameters,
431
+ value_type=value_type,
432
+ **additional_flags
364
433
  )
365
434
  )
366
435
 
367
- @abc.abstractmethod
368
- def _create_scalar(
436
+ def get_distribution(
369
437
  self,
370
438
  name: str,
371
439
  description: str,
372
- parameter_definitions: Dict[str, Type[Union[int, str, bool]]],
373
- **kwargs
374
- ) -> Scalar:
375
- """Creates a counter with the given name."""
440
+ parameters: Optional[Dict[str, Type[Union[int, str, bool]]]] = None,
441
+ **additional_flags
442
+ ) -> Distribution:
443
+ """Gets or creates a distribution with the given name.
444
+
445
+ Args:
446
+ name: The name of the distribution.
447
+ description: The description of the distribution.
448
+ parameters: The definitions of the parameters for the distribution.
449
+ `default_parameters` from the collection will be used if not specified.
450
+ **additional_flags: Additional arguments for creating the distribution.
451
+
452
+ Returns:
453
+ The distribution with the given name.
454
+ """
455
+ if parameters is None:
456
+ parameters = self._default_parameter_definitions
457
+ return typing.cast(
458
+ Distribution,
459
+ self._get_or_create_metric(
460
+ self._DISTRIBUTION_CLASS,
461
+ name,
462
+ description,
463
+ parameters,
464
+ **additional_flags
465
+ )
466
+ )
376
467
 
377
468
  #
378
469
  # InMemoryMetricCollection.
@@ -401,14 +492,46 @@ class _InMemoryCounter(Counter):
401
492
  return self._counter[self._parameters_key(**parameters)]
402
493
 
403
494
 
404
- class _InMemoryScalar(Scalar):
495
+ class _InMemoryScalar(Scalar[MetricValueType]):
405
496
  """In-memory scalar."""
406
497
 
498
+ def __init__(self, *args, **kwargs):
499
+ super().__init__(*args, **kwargs)
500
+ self._values = collections.defaultdict(self.flags['value_type'])
501
+ self._lock = threading.Lock()
502
+
503
+ def increment(
504
+ self,
505
+ delta: MetricValueType = 1,
506
+ **parameters
507
+ ) -> MetricValueType:
508
+ """Increments the scalar by delta and returns the new value."""
509
+ parameters_key = self._parameters_key(**parameters)
510
+ with self._lock:
511
+ value = self._values[parameters_key]
512
+ value += delta
513
+ self._values[parameters_key] = value
514
+ return value
515
+
516
+ def set(self, value: MetricValueType, **parameters) -> None:
517
+ """Sets the value of the scalar."""
518
+ parameters_key = self._parameters_key(**parameters)
519
+ with self._lock:
520
+ self._values[parameters_key] = value
521
+
522
+ def value(self, **parameters) -> MetricValueType:
523
+ """Returns the distribution of the scalar."""
524
+ return self._values[self._parameters_key(**parameters)]
525
+
526
+
527
+ class _InMemoryDistribution(Distribution):
528
+ """In-memory distribution."""
529
+
407
530
  def __init__(self, *args, window_size: int = 1024 * 1024, **kwargs):
408
531
  super().__init__(*args, **kwargs)
409
532
  self._window_size = window_size
410
533
  self._distributions = collections.defaultdict(
411
- lambda: _InMemoryDistribution(self._window_size)
534
+ lambda: _InMemoryDistributionValue(self._window_size)
412
535
  )
413
536
  self._lock = threading.Lock()
414
537
 
@@ -417,14 +540,13 @@ class _InMemoryScalar(Scalar):
417
540
  parameters_key = self._parameters_key(**parameters)
418
541
  self._distributions[parameters_key].add(value)
419
542
 
420
- def distribution(self, **parameters) -> Distribution:
543
+ def value(self, **parameters) -> DistributionValue:
421
544
  """Returns the distribution of the scalar."""
422
- parameters_key = self._parameters_key(**parameters)
423
- return self._distributions[parameters_key]
545
+ return self._distributions[self._parameters_key(**parameters)]
424
546
 
425
547
 
426
- class _InMemoryDistribution(Distribution):
427
- """In memory distribution of scalar values."""
548
+ class _InMemoryDistributionValue(DistributionValue):
549
+ """In memory distribution value."""
428
550
 
429
551
  def __init__(self, window_size: int = 1024 * 1024):
430
552
  self._window_size = window_size
@@ -511,30 +633,9 @@ class _InMemoryDistribution(Distribution):
511
633
  class InMemoryMetricCollection(MetricCollection):
512
634
  """In-memory counter."""
513
635
 
514
- def _create_counter(
515
- self,
516
- name: str,
517
- description: str,
518
- parameter_definitions: Dict[str, Type[Union[int, str, bool]]],
519
- **kwargs
520
- ) -> Counter:
521
- return _InMemoryCounter(
522
- self._namespace, name, description, parameter_definitions
523
- )
524
-
525
- def _create_scalar(
526
- self,
527
- name: str,
528
- description: str,
529
- parameter_definitions: Dict[str, Type[Union[int, str, bool]]],
530
- *,
531
- window_size: int = 1024 * 1024,
532
- **kwargs
533
- ) -> Scalar:
534
- return _InMemoryScalar(
535
- self._namespace, name, description, parameter_definitions,
536
- window_size=window_size, **kwargs
537
- )
636
+ _COUNTER_CLASS = _InMemoryCounter
637
+ _SCALAR_CLASS = _InMemoryScalar
638
+ _DISTRIBUTION_CLASS = _InMemoryDistribution
538
639
 
539
640
 
540
641
  _METRIC_COLLECTION_CLS = InMemoryMetricCollection # pylint: disable=invalid-name
@@ -55,7 +55,7 @@ class MetricCollectionTest(unittest.TestCase):
55
55
  with self.assertRaisesRegex(
56
56
  ValueError, 'Metric .* already exists with a different type'
57
57
  ):
58
- collection.get_scalar('counter', 'counter description')
58
+ collection.get_distribution('counter', 'counter description')
59
59
 
60
60
  with self.assertRaisesRegex(
61
61
  ValueError, 'Metric .* already exists with a different description'
@@ -71,11 +71,11 @@ class MetricCollectionTest(unittest.TestCase):
71
71
  )
72
72
 
73
73
 
74
- class InMemoryDistributionTest(unittest.TestCase):
75
- """Tests for in memory distribution."""
74
+ class InMemoryDistributionValueTest(unittest.TestCase):
75
+ """Tests for in memory distribution value."""
76
76
 
77
77
  def test_empty_distribution(self):
78
- dist = monitoring._InMemoryDistribution()
78
+ dist = monitoring._InMemoryDistributionValue()
79
79
  self.assertEqual(dist.count, 0)
80
80
  self.assertEqual(dist.sum, 0.0)
81
81
  self.assertEqual(dist.mean, 0.0)
@@ -86,7 +86,7 @@ class InMemoryDistributionTest(unittest.TestCase):
86
86
  self.assertEqual(dist.fraction_less_than(100), 0.0)
87
87
 
88
88
  def test_add_value(self):
89
- dist = monitoring._InMemoryDistribution()
89
+ dist = monitoring._InMemoryDistributionValue()
90
90
  dist.add(1)
91
91
  dist.add(3)
92
92
  dist.add(10)
@@ -106,7 +106,7 @@ class InMemoryDistributionTest(unittest.TestCase):
106
106
  def test_add_value_no_numpy(self):
107
107
  numpy = monitoring.numpy
108
108
  monitoring.numpy = None
109
- dist = monitoring._InMemoryDistribution()
109
+ dist = monitoring._InMemoryDistributionValue()
110
110
  dist.add(1)
111
111
  dist.add(3)
112
112
  dist.add(10)
@@ -125,7 +125,7 @@ class InMemoryDistributionTest(unittest.TestCase):
125
125
  monitoring.numpy = numpy
126
126
 
127
127
  def test_window_size(self):
128
- dist = monitoring._InMemoryDistribution(window_size=3)
128
+ dist = monitoring._InMemoryDistributionValue(window_size=3)
129
129
  dist.add(1)
130
130
  dist.add(3)
131
131
  dist.add(10)
@@ -199,50 +199,90 @@ class InMemoryScalarTest(unittest.TestCase):
199
199
  self.assertEqual(scalar.description, 'scalar description')
200
200
  self.assertEqual(scalar.parameter_definitions, {})
201
201
  self.assertEqual(scalar.full_name, '/test/scalar')
202
- dist = scalar.distribution()
203
- self.assertEqual(dist.count, 0)
204
- scalar.record(1)
205
- scalar.record(2)
206
- scalar.record(3)
207
- scalar.distribution()
208
- self.assertEqual(dist.count, 3)
209
-
210
- scalar = collection.get_scalar('scalar2', 'scalar description')
211
- with scalar.record_duration():
212
- time.sleep(0.1)
213
- self.assertGreaterEqual(scalar.distribution().mean, 100)
202
+ self.assertEqual(scalar.value(), 0)
203
+ self.assertEqual(scalar.increment(), 1)
204
+ self.assertEqual(scalar.value(), 1)
205
+ scalar.set(3)
206
+ self.assertEqual(scalar.increment(2), 5)
207
+ self.assertEqual(scalar.value(), 5)
214
208
 
215
209
  def test_scalar_with_parameters(self):
216
210
  collection = monitoring.InMemoryMetricCollection('/test')
217
211
  scalar = collection.get_scalar(
218
- 'scalar', 'scalar description', {'field1': str}
212
+ 'scalar', 'scalar description', {'field1': str}, float
219
213
  )
220
214
  self.assertEqual(scalar.namespace, '/test')
221
215
  self.assertEqual(scalar.name, 'scalar')
222
216
  self.assertEqual(scalar.description, 'scalar description')
223
217
  self.assertEqual(scalar.parameter_definitions, {'field1': str})
224
218
  self.assertEqual(scalar.full_name, '/test/scalar')
225
- dist = scalar.distribution(field1='foo')
226
- self.assertEqual(dist.count, 0)
227
- scalar.record(1, field1='foo')
228
- scalar.record(2, field1='foo')
229
- scalar.record(3, field1='bar')
230
- dist = scalar.distribution(field1='foo')
231
- self.assertEqual(dist.count, 2)
232
- dist = scalar.distribution(field1='bar')
233
- self.assertEqual(dist.count, 1)
219
+ self.assertEqual(scalar.value(field1='foo'), 0.0)
220
+ scalar.set(2.5, field1='bar')
221
+ self.assertEqual(scalar.value(field1='bar'), 2.5)
222
+ self.assertEqual(scalar.increment(1.1, field1='bar'), 3.6)
223
+ self.assertEqual(scalar.value(field1='bar'), 3.6)
224
+ self.assertEqual(scalar.value(field1='foo'), 0.0)
234
225
 
235
- scalar = collection.get_scalar(
236
- 'scalar2', 'scalar description', {'error': str}
226
+
227
+ class InMemoryDistributionTest(unittest.TestCase):
228
+ """Tests for in memory distribution."""
229
+
230
+ def test_distribution_without_parameters(self):
231
+ collection = monitoring.InMemoryMetricCollection('/test')
232
+ dist = collection.get_distribution(
233
+ 'distribution', 'distribution description'
234
+ )
235
+ self.assertEqual(dist.namespace, '/test')
236
+ self.assertEqual(dist.name, 'distribution')
237
+ self.assertEqual(dist.description, 'distribution description')
238
+ self.assertEqual(dist.parameter_definitions, {})
239
+ self.assertEqual(dist.full_name, '/test/distribution')
240
+ v = dist.value()
241
+ self.assertEqual(v.count, 0)
242
+ dist.record(1)
243
+ dist.record(2)
244
+ dist.record(3)
245
+ v = dist.value()
246
+ self.assertEqual(v.count, 3)
247
+
248
+ dist = collection.get_distribution(
249
+ 'distribution2', 'distribution description'
250
+ )
251
+ with dist.record_duration():
252
+ time.sleep(0.1)
253
+ self.assertGreaterEqual(dist.value().mean, 100)
254
+
255
+ def test_distribution_with_parameters(self):
256
+ collection = monitoring.InMemoryMetricCollection('/test')
257
+ dist = collection.get_distribution(
258
+ 'distribution', 'distribution description', {'field1': str}
259
+ )
260
+ self.assertEqual(dist.namespace, '/test')
261
+ self.assertEqual(dist.name, 'distribution')
262
+ self.assertEqual(dist.description, 'distribution description')
263
+ self.assertEqual(dist.parameter_definitions, {'field1': str})
264
+ self.assertEqual(dist.full_name, '/test/distribution')
265
+ value = dist.value(field1='foo')
266
+ self.assertEqual(value.count, 0)
267
+ dist.record(1, field1='foo')
268
+ dist.record(2, field1='foo')
269
+ dist.record(3, field1='bar')
270
+ value = dist.value(field1='foo')
271
+ self.assertEqual(value.count, 2)
272
+ value = dist.value(field1='bar')
273
+ self.assertEqual(value.count, 1)
274
+
275
+ dist = collection.get_distribution(
276
+ 'distribution2', 'distribution description', {'error': str}
237
277
  )
238
278
  with self.assertRaises(ValueError):
239
- with scalar.record_duration():
279
+ with dist.record_duration():
240
280
  time.sleep(0.1)
241
281
  raise ValueError()
242
- self.assertGreaterEqual(scalar.distribution(error='ValueError').mean, 100)
243
- with scalar.record_duration():
282
+ self.assertGreaterEqual(dist.value(error='ValueError').mean, 100)
283
+ with dist.record_duration():
244
284
  time.sleep(0.1)
245
- self.assertGreaterEqual(scalar.distribution(error='').mean, 100)
285
+ self.assertGreaterEqual(dist.value(error='').mean, 100)
246
286
 
247
287
 
248
288
  if __name__ == '__main__':