pyglove 0.4.5.dev202410020809__tar.gz → 0.4.5.dev202410090808__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 (200) hide show
  1. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/PKG-INFO +1 -1
  2. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/__init__.py +9 -0
  3. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/object_utils/__init__.py +1 -0
  4. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/object_utils/value_location.py +10 -0
  5. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/object_utils/value_location_test.py +22 -0
  6. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/base.py +40 -2
  7. pyglove-0.4.5.dev202410090808/pyglove/core/symbolic/base_test.py +156 -0
  8. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/diff.py +190 -1
  9. pyglove-0.4.5.dev202410090808/pyglove/core/symbolic/diff_test.py +632 -0
  10. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/object_test.py +3 -8
  11. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/ref.py +29 -0
  12. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/ref_test.py +143 -0
  13. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/typing/__init__.py +4 -0
  14. pyglove-0.4.5.dev202410090808/pyglove/core/typing/callable_ext.py +290 -0
  15. pyglove-0.4.5.dev202410090808/pyglove/core/typing/callable_ext_test.py +327 -0
  16. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/typing/inspect.py +63 -0
  17. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/typing/inspect_test.py +39 -0
  18. pyglove-0.4.5.dev202410090808/pyglove/core/views/__init__.py +30 -0
  19. pyglove-0.4.5.dev202410090808/pyglove/core/views/base.py +906 -0
  20. pyglove-0.4.5.dev202410090808/pyglove/core/views/base_test.py +615 -0
  21. pyglove-0.4.5.dev202410090808/pyglove/core/views/html/__init__.py +27 -0
  22. pyglove-0.4.5.dev202410090808/pyglove/core/views/html/base.py +529 -0
  23. pyglove-0.4.5.dev202410090808/pyglove/core/views/html/base_test.py +804 -0
  24. pyglove-0.4.5.dev202410090808/pyglove/core/views/html/tree_view.py +1052 -0
  25. pyglove-0.4.5.dev202410090808/pyglove/core/views/html/tree_view_test.py +748 -0
  26. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove.egg-info/PKG-INFO +1 -1
  27. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove.egg-info/SOURCES.txt +8 -0
  28. pyglove-0.4.5.dev202410020809/pyglove/core/symbolic/base_test.py +0 -89
  29. pyglove-0.4.5.dev202410020809/pyglove/core/symbolic/diff_test.py +0 -342
  30. pyglove-0.4.5.dev202410020809/pyglove/core/typing/callable_ext.py +0 -51
  31. pyglove-0.4.5.dev202410020809/pyglove/core/typing/callable_ext_test.py +0 -72
  32. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/LICENSE +0 -0
  33. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/README.md +0 -0
  34. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/__init__.py +0 -0
  35. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/detouring/__init__.py +0 -0
  36. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/detouring/class_detour.py +0 -0
  37. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/detouring/class_detour_test.py +0 -0
  38. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/geno/__init__.py +0 -0
  39. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/geno/base.py +0 -0
  40. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/geno/base_test.py +0 -0
  41. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/geno/categorical.py +0 -0
  42. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/geno/categorical_test.py +0 -0
  43. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/geno/custom.py +0 -0
  44. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/geno/custom_test.py +0 -0
  45. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/geno/deduping.py +0 -0
  46. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/geno/deduping_test.py +0 -0
  47. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/geno/dna_generator.py +0 -0
  48. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/geno/dna_generator_test.py +0 -0
  49. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/geno/numerical.py +0 -0
  50. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/geno/numerical_test.py +0 -0
  51. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/geno/random.py +0 -0
  52. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/geno/random_test.py +0 -0
  53. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/geno/space.py +0 -0
  54. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/geno/space_test.py +0 -0
  55. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/geno/sweeping.py +0 -0
  56. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/geno/sweeping_test.py +0 -0
  57. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/hyper/__init__.py +0 -0
  58. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/hyper/base.py +0 -0
  59. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/hyper/categorical.py +0 -0
  60. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/hyper/categorical_test.py +0 -0
  61. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/hyper/custom.py +0 -0
  62. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/hyper/custom_test.py +0 -0
  63. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/hyper/derived.py +0 -0
  64. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/hyper/derived_test.py +0 -0
  65. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/hyper/dynamic_evaluation.py +0 -0
  66. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/hyper/dynamic_evaluation_test.py +0 -0
  67. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/hyper/evolvable.py +0 -0
  68. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/hyper/evolvable_test.py +0 -0
  69. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/hyper/iter.py +0 -0
  70. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/hyper/iter_test.py +0 -0
  71. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/hyper/numerical.py +0 -0
  72. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/hyper/numerical_test.py +0 -0
  73. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/hyper/object_template.py +0 -0
  74. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/hyper/object_template_test.py +0 -0
  75. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/io/__init__.py +0 -0
  76. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/io/file_system.py +0 -0
  77. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/io/file_system_test.py +0 -0
  78. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/logging.py +0 -0
  79. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/logging_test.py +0 -0
  80. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/object_utils/codegen.py +0 -0
  81. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/object_utils/codegen_test.py +0 -0
  82. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/object_utils/common_traits.py +0 -0
  83. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/object_utils/common_traits_test.py +0 -0
  84. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/object_utils/docstr_utils.py +0 -0
  85. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/object_utils/docstr_utils_test.py +0 -0
  86. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/object_utils/error_utils.py +0 -0
  87. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/object_utils/error_utils_test.py +0 -0
  88. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/object_utils/formatting.py +0 -0
  89. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/object_utils/formatting_test.py +0 -0
  90. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/object_utils/hierarchical.py +0 -0
  91. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/object_utils/hierarchical_test.py +0 -0
  92. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/object_utils/json_conversion.py +0 -0
  93. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/object_utils/json_conversion_test.py +0 -0
  94. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/object_utils/missing.py +0 -0
  95. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/object_utils/missing_test.py +0 -0
  96. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/object_utils/profiling.py +0 -0
  97. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/object_utils/profiling_test.py +0 -0
  98. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/object_utils/thread_local.py +0 -0
  99. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/object_utils/thread_local_test.py +0 -0
  100. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/patching/__init__.py +0 -0
  101. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/patching/object_factory.py +0 -0
  102. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/patching/object_factory_test.py +0 -0
  103. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/patching/pattern_based.py +0 -0
  104. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/patching/pattern_based_test.py +0 -0
  105. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/patching/rule_based.py +0 -0
  106. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/patching/rule_based_test.py +0 -0
  107. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/__init__.py +0 -0
  108. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/boilerplate.py +0 -0
  109. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/boilerplate_test.py +0 -0
  110. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/class_wrapper.py +0 -0
  111. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/class_wrapper_test.py +0 -0
  112. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/compounding.py +0 -0
  113. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/compounding_test.py +0 -0
  114. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/dict.py +0 -0
  115. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/dict_test.py +0 -0
  116. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/flags.py +0 -0
  117. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/flags_test.py +0 -0
  118. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/functor.py +0 -0
  119. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/functor_test.py +0 -0
  120. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/inferred.py +0 -0
  121. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/inferred_test.py +0 -0
  122. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/list.py +0 -0
  123. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/list_test.py +0 -0
  124. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/object.py +0 -0
  125. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/origin.py +0 -0
  126. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/origin_test.py +0 -0
  127. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/pure_symbolic.py +0 -0
  128. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/symbolize.py +0 -0
  129. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/symbolic/symbolize_test.py +0 -0
  130. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/tuning/__init__.py +0 -0
  131. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/tuning/backend.py +0 -0
  132. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/tuning/backend_test.py +0 -0
  133. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/tuning/early_stopping.py +0 -0
  134. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/tuning/local_backend.py +0 -0
  135. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/tuning/protocols.py +0 -0
  136. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/tuning/protocols_test.py +0 -0
  137. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/tuning/sample.py +0 -0
  138. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/tuning/sample_test.py +0 -0
  139. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/typing/annotated.py +0 -0
  140. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/typing/annotated_test.py +0 -0
  141. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/typing/annotation_conversion.py +0 -0
  142. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/typing/annotation_conversion_test.py +0 -0
  143. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/typing/callable_signature.py +0 -0
  144. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/typing/callable_signature_test.py +0 -0
  145. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/typing/class_schema.py +0 -0
  146. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/typing/class_schema_test.py +0 -0
  147. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/typing/custom_typing.py +0 -0
  148. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/typing/key_specs.py +0 -0
  149. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/typing/key_specs_test.py +0 -0
  150. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/typing/pytype_support.py +0 -0
  151. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/typing/type_conversion.py +0 -0
  152. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/typing/type_conversion_test.py +0 -0
  153. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/typing/typed_missing.py +0 -0
  154. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/typing/typed_missing_test.py +0 -0
  155. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/typing/value_specs.py +0 -0
  156. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/core/typing/value_specs_test.py +0 -0
  157. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/__init__.py +0 -0
  158. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/early_stopping/__init__.py +0 -0
  159. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/early_stopping/base.py +0 -0
  160. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/early_stopping/base_test.py +0 -0
  161. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/early_stopping/step_wise.py +0 -0
  162. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/early_stopping/step_wise_test.py +0 -0
  163. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/evolution/__init__.py +0 -0
  164. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/evolution/base.py +0 -0
  165. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/evolution/base_test.py +0 -0
  166. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/evolution/hill_climb.py +0 -0
  167. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/evolution/hill_climb_test.py +0 -0
  168. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/evolution/mutators.py +0 -0
  169. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/evolution/mutators_test.py +0 -0
  170. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/evolution/neat.py +0 -0
  171. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/evolution/neat_test.py +0 -0
  172. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/evolution/nsga2.py +0 -0
  173. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/evolution/nsga2_test.py +0 -0
  174. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/evolution/recombinators.py +0 -0
  175. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/evolution/recombinators_test.py +0 -0
  176. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/evolution/regularized_evolution.py +0 -0
  177. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/evolution/regularized_evolution_test.py +0 -0
  178. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/evolution/selectors.py +0 -0
  179. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/evolution/selectors_test.py +0 -0
  180. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/evolution/where.py +0 -0
  181. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/evolution/where_test.py +0 -0
  182. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/mutfun/__init__.py +0 -0
  183. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/mutfun/base.py +0 -0
  184. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/mutfun/base_test.py +0 -0
  185. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/mutfun/basic_ops.py +0 -0
  186. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/mutfun/basic_ops_test.py +0 -0
  187. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/scalars/__init__.py +0 -0
  188. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/scalars/base.py +0 -0
  189. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/scalars/base_test.py +0 -0
  190. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/scalars/maths.py +0 -0
  191. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/scalars/maths_test.py +0 -0
  192. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/scalars/randoms.py +0 -0
  193. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/scalars/randoms_test.py +0 -0
  194. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/scalars/step_wise.py +0 -0
  195. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove/ext/scalars/step_wise_test.py +0 -0
  196. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove.egg-info/dependency_links.txt +0 -0
  197. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove.egg-info/requires.txt +0 -0
  198. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/pyglove.egg-info/top_level.txt +0 -0
  199. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/setup.cfg +0 -0
  200. {pyglove-0.4.5.dev202410020809 → pyglove-0.4.5.dev202410090808}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyglove
3
- Version: 0.4.5.dev202410020809
3
+ Version: 0.4.5.dev202410090808
4
4
  Summary: PyGlove: A library for manipulating Python objects.
5
5
  Home-page: https://github.com/google/pyglove
6
6
  Author: PyGlove Authors
@@ -297,6 +297,15 @@ docstr = object_utils.docstr
297
297
  catch_errors = object_utils.catch_errors
298
298
  timeit = object_utils.timeit
299
299
 
300
+ # Symbols from 'views' sub-module.
301
+
302
+ from pyglove.core import views
303
+ view = views.view
304
+ View = views.View
305
+ Html = views.Html
306
+ to_html = views.to_html
307
+ to_html_str = views.to_html_str
308
+
300
309
  #
301
310
  # Symbols from `io` sub-module.
302
311
  #
@@ -137,6 +137,7 @@ from pyglove.core.object_utils.thread_local import thread_local_increment
137
137
  from pyglove.core.object_utils.thread_local import thread_local_decrement
138
138
  from pyglove.core.object_utils.thread_local import thread_local_push
139
139
  from pyglove.core.object_utils.thread_local import thread_local_pop
140
+ from pyglove.core.object_utils.thread_local import thread_local_peek
140
141
 
141
142
  # Handling docstrings.
142
143
  from pyglove.core.object_utils.docstr_utils import DocStr
@@ -400,6 +400,16 @@ class KeyPath(formatting.Formattable):
400
400
  except KeyError:
401
401
  return False
402
402
 
403
+ def is_relative_to(self, other: Union[int, str, 'KeyPath']) -> bool:
404
+ """Returns whether current path is relative to another path."""
405
+ other = KeyPath.from_value(other)
406
+ if len(self) < len(other):
407
+ return False
408
+ for i in range(len(other)):
409
+ if self.keys[i] != other.keys[i]:
410
+ return False
411
+ return True
412
+
403
413
  @property
404
414
  def path(self) -> str:
405
415
  """JSONPath representation of current path."""
@@ -226,6 +226,28 @@ class KeyPathTest(unittest.TestCase):
226
226
  with self.assertRaisesRegex(TypeError, 'Cannot subtract KeyPath'):
227
227
  _ = value_location.KeyPath.parse('a.b') - 1.0
228
228
 
229
+ def test_is_relative_to(self):
230
+ self.assertTrue(
231
+ value_location.KeyPath.parse('a.b.c').is_relative_to(
232
+ value_location.KeyPath())
233
+ )
234
+ self.assertTrue(
235
+ value_location.KeyPath.parse('a.b.c').is_relative_to(
236
+ value_location.KeyPath.parse('a.b'))
237
+ )
238
+ self.assertTrue(
239
+ value_location.KeyPath.parse('a.b.c').is_relative_to(
240
+ value_location.KeyPath.parse('a.b.c'))
241
+ )
242
+ self.assertFalse(
243
+ value_location.KeyPath.parse('a.b').is_relative_to(
244
+ value_location.KeyPath.parse('a.b.c'))
245
+ )
246
+ self.assertFalse(
247
+ value_location.KeyPath.parse('a.b.d').is_relative_to(
248
+ value_location.KeyPath.parse('a.b.c'))
249
+ )
250
+
229
251
  def test_hash(self):
230
252
  self.assertIn(value_location.KeyPath.parse('a.b.c'), {'a.b.c': 1})
231
253
  self.assertNotIn(value_location.KeyPath.parse('a.b.c'), {'a.b': 1})
@@ -31,6 +31,7 @@ from pyglove.core.symbolic import flags
31
31
  from pyglove.core.symbolic.origin import Origin
32
32
  from pyglove.core.symbolic.pure_symbolic import NonDeterministic
33
33
  from pyglove.core.symbolic.pure_symbolic import PureSymbolic
34
+ from pyglove.core.views import html
34
35
 
35
36
 
36
37
  class WritePermissionError(Exception):
@@ -171,9 +172,10 @@ RAISE_IF_NOT_FOUND = (pg_typing.MISSING_VALUE,)
171
172
 
172
173
  class Symbolic(
173
174
  TopologyAware,
175
+ object_utils.Formattable,
174
176
  object_utils.JSONConvertible,
175
177
  object_utils.MaybePartial,
176
- object_utils.Formattable,
178
+ html.HtmlTreeView.Extension
177
179
  ):
178
180
  """Base for all symbolic types.
179
181
 
@@ -525,7 +527,7 @@ class Symbolic(
525
527
  def sym_contains(
526
528
  self,
527
529
  value: Any = None,
528
- type: Union[None, Type[Any], Tuple[Type[Any]]] = None # pylint: disable=redefined-builtin
530
+ type: Union[None, Type[Any], Tuple[Type[Any], ...]] = None # pylint: disable=redefined-builtin
529
531
  ) -> bool:
530
532
  """Returns True if the object contains sub-nodes of given value or type."""
531
533
  return contains(self, value, type)
@@ -947,6 +949,42 @@ class Symbolic(
947
949
  """Serializes current object into a JSON string."""
948
950
  return to_json_str(self, json_indent=json_indent, **kwargs)
949
951
 
952
+ # pytype: disable=annotation-type-mismatch
953
+ def _html_tree_view_content(
954
+ self,
955
+ *,
956
+ view: html.HtmlTreeView,
957
+ name: Optional[str],
958
+ parent: Any,
959
+ root_path: object_utils.KeyPath,
960
+ hide_frozen: bool = html.HtmlView.PresetArgValue(True),
961
+ hide_default_values: bool = html.HtmlView.PresetArgValue(False),
962
+ use_inferred: bool = html.HtmlView.PresetArgValue(True),
963
+ **kwargs,
964
+ ) -> html.Html:
965
+ # pytype: enable=annotation-type-mismatch
966
+ """Returns the content HTML for a symbolic object.."""
967
+ kv = {}
968
+ for k, v in self.sym_items():
969
+ # Apply frozen filter.
970
+ field = self.sym_attr_field(k)
971
+ if hide_frozen and field and field.frozen:
972
+ continue
973
+
974
+ # Apply inferred value.
975
+ if use_inferred and isinstance(v, Inferential):
976
+ v = self.sym_inferred(k, default=v)
977
+
978
+ # Apply default value filter.
979
+ if field and hide_default_values and eq(v, field.default_value):
980
+ continue
981
+ kv[k] = v
982
+ return view.complex_value(
983
+ kv, name=name, parent=self, root_path=root_path,
984
+ hide_frozen=hide_frozen, hide_default_values=hide_default_values,
985
+ use_inferred=use_inferred, **kwargs
986
+ )
987
+
950
988
  @classmethod
951
989
  def load(cls, *args, **kwargs) -> Any:
952
990
  """Loads an instance of this type using the global load handler."""
@@ -0,0 +1,156 @@
1
+ # Copyright 2022 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
+ """Tests for pyglove.symbolic.base."""
15
+
16
+ import copy
17
+ import inspect
18
+ from typing import Any
19
+ import unittest
20
+
21
+ from pyglove.core import object_utils
22
+ from pyglove.core import typing as pg_typing
23
+ from pyglove.core.symbolic import base
24
+ from pyglove.core.symbolic.dict import Dict
25
+ from pyglove.core.symbolic.inferred import ValueFromParentChain
26
+ from pyglove.core.symbolic.object import Object
27
+
28
+
29
+ class FieldUpdateTest(unittest.TestCase):
30
+ """Tests for `pg.symbolic.FieldUpdate`."""
31
+
32
+ def test_basics(self):
33
+ x = Dict(x=1)
34
+ f = pg_typing.Field('x', pg_typing.Int())
35
+ update = base.FieldUpdate(object_utils.KeyPath('x'), x, f, 1, 2)
36
+ self.assertEqual(update.path, 'x')
37
+ self.assertIs(update.target, x)
38
+ self.assertIs(update.field, f)
39
+ self.assertEqual(update.old_value, 1)
40
+ self.assertEqual(update.new_value, 2)
41
+
42
+ def test_format(self):
43
+ self.assertEqual(
44
+ base.FieldUpdate(
45
+ object_utils.KeyPath('x'), Dict(x=1), None, 1, 2
46
+ ).format(compact=True),
47
+ 'FieldUpdate(parent_path=, path=x, old_value=1, new_value=2)',
48
+ )
49
+
50
+ self.assertEqual(
51
+ base.FieldUpdate(
52
+ object_utils.KeyPath('a'), Dict(x=Dict(a=1)).x, None, 1, 2
53
+ ).format(compact=True),
54
+ 'FieldUpdate(parent_path=x, path=a, old_value=1, new_value=2)',
55
+ )
56
+
57
+ def test_eq_ne(self):
58
+ x = Dict()
59
+ f = pg_typing.Field('x', pg_typing.Int())
60
+ self.assertEqual(
61
+ base.FieldUpdate(object_utils.KeyPath('a'), x, f, 1, 2),
62
+ base.FieldUpdate(object_utils.KeyPath('a'), x, f, 1, 2),
63
+ )
64
+
65
+ # Targets are not the same instance.
66
+ self.assertNotEqual(
67
+ base.FieldUpdate(object_utils.KeyPath('a'), x, f, 1, 2),
68
+ base.FieldUpdate(object_utils.KeyPath('a'), Dict(), f, 1, 2),
69
+ )
70
+
71
+ # Fields are not the same instance.
72
+ self.assertNotEqual(
73
+ base.FieldUpdate(object_utils.KeyPath('a'), x, f, 1, 2),
74
+ base.FieldUpdate(object_utils.KeyPath('b'), x, copy.copy(f), 1, 2),
75
+ )
76
+
77
+ self.assertNotEqual(
78
+ base.FieldUpdate(object_utils.KeyPath('a'), x, f, 1, 2),
79
+ base.FieldUpdate(object_utils.KeyPath('a'), x, f, 0, 2),
80
+ )
81
+
82
+ self.assertNotEqual(
83
+ base.FieldUpdate(object_utils.KeyPath('a'), x, f, 1, 2),
84
+ base.FieldUpdate(object_utils.KeyPath('a'), x, f, 1, 1),
85
+ )
86
+
87
+ self.assertNotEqual(
88
+ base.FieldUpdate(object_utils.KeyPath('a'), x, f, 1, 2), Dict()
89
+ )
90
+
91
+
92
+ class HtmlFormattableTest(unittest.TestCase):
93
+
94
+ def assert_content(self, html, expected):
95
+ expected = inspect.cleandoc(expected).strip()
96
+ actual = html.content.strip()
97
+ if actual != expected:
98
+ print(actual)
99
+ self.assertEqual(actual.strip(), expected)
100
+
101
+ def test_to_html(self):
102
+
103
+ class Foo(Object):
104
+ x: int
105
+ y: Any = 'foo'
106
+ z: pg_typing.Int().freeze(1)
107
+
108
+ # Disable tooltip.
109
+ self.assert_content(
110
+ Foo(x=1, y='foo').to_html(
111
+ enable_summary_tooltip=False,
112
+ enable_key_tooltip=False
113
+ ),
114
+ """
115
+ <details open class="pyglove foo"><summary><div class="summary_title">Foo(...)</div></summary><div class="complex_value foo"><table><tr><td><span class="object_key">x</span></td><td><span class="simple_value int">1</span></td></tr><tr><td><span class="object_key">y</span></td><td><span class="simple_value str">&#x27;foo&#x27;</span></td></tr></table></div></details>
116
+ """
117
+ )
118
+ # Hide frozen and default values.
119
+ self.assert_content(
120
+ Foo(x=1, y='foo').to_html(
121
+ enable_summary_tooltip=False,
122
+ enable_key_tooltip=False,
123
+ collapse_level=0,
124
+ hide_frozen=True,
125
+ hide_default_values=True
126
+ ),
127
+ """
128
+ <details class="pyglove foo"><summary><div class="summary_title">Foo(...)</div></summary><div class="complex_value foo"><table><tr><td><span class="object_key">x</span></td><td><span class="simple_value int">1</span></td></tr></table></div></details>
129
+ """
130
+ )
131
+ # Use inferred values.
132
+ x = Dict(x=Dict(y=ValueFromParentChain()), y=2)
133
+ self.assert_content(
134
+ x.x.to_html(
135
+ enable_summary_tooltip=False,
136
+ enable_key_tooltip=False,
137
+ use_inferred=False
138
+ ),
139
+ """
140
+ <details open class="pyglove dict"><summary><div class="summary_title">Dict(...)</div></summary><div class="complex_value dict"><table><tr><td><span class="object_key">y</span></td><td><details class="pyglove value-from-parent-chain"><summary><div class="summary_title">ValueFromParentChain(...)</div></summary><div class="complex_value value-from-parent-chain"><span class="empty_container"></span></div></details></td></tr></table></div></details>
141
+ """
142
+ )
143
+ self.assert_content(
144
+ x.x.to_html(
145
+ enable_summary_tooltip=False,
146
+ enable_key_tooltip=False,
147
+ use_inferred=True
148
+ ),
149
+ """
150
+ <details open class="pyglove dict"><summary><div class="summary_title">Dict(...)</div></summary><div class="complex_value dict"><table><tr><td><span class="object_key">y</span></td><td><span class="simple_value int">2</span></td></tr></table></div></details>
151
+ """
152
+ )
153
+
154
+
155
+ if __name__ == '__main__':
156
+ unittest.main()
@@ -13,7 +13,7 @@
13
13
  # limitations under the License.
14
14
  """Symbolic differences."""
15
15
 
16
- from typing import Any, Callable, Tuple, Union
16
+ from typing import Any, Callable, Optional, Sequence, Tuple, Union
17
17
 
18
18
  from pyglove.core import object_utils
19
19
  from pyglove.core import typing as pg_typing
@@ -21,6 +21,7 @@ from pyglove.core.symbolic import base
21
21
  from pyglove.core.symbolic import list as pg_list
22
22
  from pyglove.core.symbolic import object as pg_object
23
23
  from pyglove.core.symbolic.pure_symbolic import PureSymbolic
24
+ from pyglove.core.views import html
24
25
 
25
26
 
26
27
  class Diff(PureSymbolic, pg_object.Object):
@@ -144,6 +145,194 @@ class Diff(PureSymbolic, pg_object.Object):
144
145
  **kwargs,
145
146
  )
146
147
 
148
+ # pytype: disable=annotation-type-mismatch
149
+ def _html_tree_view_summary(
150
+ self,
151
+ *,
152
+ view: html.HtmlTreeView,
153
+ title: Optional[str] = None,
154
+ max_summary_len_for_str: int = html.HtmlView.PresetArgValue(40),
155
+ **kwargs,
156
+ ) -> Optional[html.Html]:
157
+ # pytype: enable=annotation-type-mismatch
158
+ if not bool(self):
159
+ v = self.value
160
+ if (isinstance(v, (int, float, bool, type(None)))
161
+ or (isinstance(v, str) and len(v) <= max_summary_len_for_str)):
162
+ return None
163
+
164
+ if v == Diff.MISSING:
165
+ return view.summary(
166
+ self,
167
+ title=title or 'Diff',
168
+ max_summary_len_for_str=max_summary_len_for_str,
169
+ **kwargs
170
+ )
171
+ return view.summary(
172
+ self.value,
173
+ title=title,
174
+ max_summary_len_for_str=max_summary_len_for_str,
175
+ **kwargs
176
+ )
177
+ elif self.is_leaf:
178
+ return None
179
+ else:
180
+ assert isinstance(self.left, type), self.left
181
+ assert isinstance(self.right, type), self.right
182
+ if self.left is self.right:
183
+ diff_title = self.left.__name__
184
+ else:
185
+ diff_title = f'{self.left.__name__} | {self.right.__name__}'
186
+ return view.summary(
187
+ self,
188
+ title=title or diff_title,
189
+ max_summary_len_for_str=max_summary_len_for_str,
190
+ **kwargs
191
+ )
192
+
193
+ def _html_tree_view_content(
194
+ self,
195
+ *,
196
+ view: html.HtmlTreeView,
197
+ parent: Any,
198
+ root_path: object_utils.KeyPath,
199
+ css_class: Optional[Sequence[str]] = None,
200
+ **kwargs
201
+ ) -> html.Html:
202
+ del parent
203
+ user_specified_css_class = css_class or []
204
+ if not bool(self):
205
+ if self.value == Diff.MISSING:
206
+ root = html.Html.element(
207
+ 'span',
208
+ # CSS class already defined in HtmlTreeView.
209
+ css_class=['diff_empty'] + user_specified_css_class,
210
+ )
211
+ else:
212
+ # When there is no diff, but the same value needs to be displayed
213
+ # we simply return the value.
214
+ root = view.content(
215
+ self.value,
216
+ css_class=['diff_left', 'diff_right'] + user_specified_css_class,
217
+ root_path=root_path, **kwargs
218
+ )
219
+ elif self.is_leaf:
220
+ root = html.Html.element(
221
+ 'div',
222
+ [
223
+ # Render left value.
224
+ lambda: html.Html.element( # pylint: disable=g-long-ternary
225
+ 'div',
226
+ [
227
+ view.render(
228
+ self.left, root_path=root_path + 'left', **kwargs
229
+ )
230
+ ],
231
+ css_class=['diff_left'],
232
+ ) if self.left != Diff.MISSING else None,
233
+ # Render right value.
234
+ lambda: html.Html.element( # pylint: disable=g-long-ternary
235
+ 'div',
236
+ [
237
+ view.render(
238
+ self.right, root_path=root_path + 'right', **kwargs
239
+ )
240
+ ],
241
+ css_class=['diff_right'],
242
+ ) if self.right != Diff.MISSING else None,
243
+ ],
244
+ css_class=['diff_value'] + user_specified_css_class,
245
+ )
246
+ else:
247
+ assert isinstance(self.left, type)
248
+ assert isinstance(self.right, type)
249
+
250
+ if issubclass(self.left, list):
251
+ key_fn = int
252
+ else:
253
+ key_fn = lambda k: k
254
+
255
+ s = html.Html()
256
+ for k, v in self.children.items():
257
+ k = key_fn(k)
258
+ child_path = root_path + k
259
+
260
+ s.write('<tr><td>')
261
+ # Print Key.
262
+ s.write(
263
+ view.object_key(
264
+ k, parent=v, root_path=child_path,
265
+ css_class=None if bool(v) else ['no_diff_key'],
266
+ **kwargs
267
+ ),
268
+ )
269
+ # Print Value.
270
+ s.write(
271
+ '</td><td>',
272
+ view.render(v, root_path=child_path, **kwargs),
273
+ '</td></tr>'
274
+ )
275
+ root = html.Html.element(
276
+ 'div',
277
+ [
278
+ '<table>', s, '</table>'
279
+ ],
280
+ css_class=[
281
+ 'complex_value', view.css_class_name(self.left)
282
+ ] + user_specified_css_class,
283
+ )
284
+ return root.add_style(
285
+ """
286
+ .diff .summary_title::after {
287
+ content: ' (diff)';
288
+ color: #aaa;
289
+ }
290
+ .diff .summary_title {
291
+ background-color: yellow;
292
+ }
293
+ .diff table td {
294
+ padding: 4px;
295
+ }
296
+ .diff_value {
297
+ display: inline-block;
298
+ align-items: center;
299
+ }
300
+ .no_diff_key {
301
+ opacity: 0.4;
302
+ }
303
+ .diff_left.diff_right::after {
304
+ content: '(no diff)';
305
+ margin-left: 0.5em;
306
+ color: #aaa;
307
+ font-style: italic;
308
+ }
309
+ .diff_empty::before {
310
+ content: '(empty)';
311
+ font-style: italic;
312
+ margin-left: 0.5em;
313
+ color: #aaa;
314
+ }
315
+ .diff_value .diff_left::before {
316
+ content: '🇱';
317
+ }
318
+ .diff_value .diff_left > .simple_value {
319
+ background-color: #ffcccc;
320
+ }
321
+ .diff_value .diff_left > details {
322
+ background-color: #ffcccc;
323
+ }
324
+ .diff_value .diff_right::before {
325
+ content: '🇷';
326
+ }
327
+ .diff_value .diff_right > .simple_value {
328
+ background-color: #ccffcc;
329
+ }
330
+ .diff_value .diff_right > details {
331
+ background-color: #ccffcc;
332
+ }
333
+ """
334
+ )
335
+
147
336
 
148
337
  # NOTE(daiyip): we add the symbolic attribute to Diff after its declaration
149
338
  # since we need to access Diff.MISSING as the default value for `left` and