pyglove 0.5.0.dev202511040811__tar.gz → 0.5.0.dev202511161718__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 (230) hide show
  1. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/PKG-INFO +1 -1
  2. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/io/file_system.py +105 -2
  3. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/io/file_system_test.py +174 -0
  4. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/type_conversion_test.py +7 -2
  5. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove.egg-info/PKG-INFO +1 -1
  6. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/LICENSE +0 -0
  7. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/README.md +0 -0
  8. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/__init__.py +0 -0
  9. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/__init__.py +0 -0
  10. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/coding/__init__.py +0 -0
  11. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/coding/errors.py +0 -0
  12. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/coding/errors_test.py +0 -0
  13. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/coding/execution.py +0 -0
  14. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/coding/execution_test.py +0 -0
  15. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/coding/function_generation.py +0 -0
  16. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/coding/function_generation_test.py +0 -0
  17. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/coding/parsing.py +0 -0
  18. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/coding/parsing_test.py +0 -0
  19. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/coding/permissions.py +0 -0
  20. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/coding/permissions_test.py +0 -0
  21. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/detouring/__init__.py +0 -0
  22. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/detouring/class_detour.py +0 -0
  23. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/detouring/class_detour_test.py +0 -0
  24. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/geno/__init__.py +0 -0
  25. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/geno/base.py +0 -0
  26. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/geno/base_test.py +0 -0
  27. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/geno/categorical.py +0 -0
  28. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/geno/categorical_test.py +0 -0
  29. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/geno/custom.py +0 -0
  30. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/geno/custom_test.py +0 -0
  31. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/geno/deduping.py +0 -0
  32. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/geno/deduping_test.py +0 -0
  33. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/geno/dna_generator.py +0 -0
  34. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/geno/dna_generator_test.py +0 -0
  35. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/geno/numerical.py +0 -0
  36. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/geno/numerical_test.py +0 -0
  37. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/geno/random.py +0 -0
  38. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/geno/random_test.py +0 -0
  39. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/geno/space.py +0 -0
  40. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/geno/space_test.py +0 -0
  41. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/geno/sweeping.py +0 -0
  42. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/geno/sweeping_test.py +0 -0
  43. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/hyper/__init__.py +0 -0
  44. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/hyper/base.py +0 -0
  45. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/hyper/categorical.py +0 -0
  46. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/hyper/categorical_test.py +0 -0
  47. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/hyper/custom.py +0 -0
  48. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/hyper/custom_test.py +0 -0
  49. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/hyper/derived.py +0 -0
  50. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/hyper/derived_test.py +0 -0
  51. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/hyper/dynamic_evaluation.py +0 -0
  52. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/hyper/dynamic_evaluation_test.py +0 -0
  53. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/hyper/evolvable.py +0 -0
  54. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/hyper/evolvable_test.py +0 -0
  55. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/hyper/iter.py +0 -0
  56. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/hyper/iter_test.py +0 -0
  57. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/hyper/numerical.py +0 -0
  58. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/hyper/numerical_test.py +0 -0
  59. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/hyper/object_template.py +0 -0
  60. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/hyper/object_template_test.py +0 -0
  61. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/io/__init__.py +0 -0
  62. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/io/sequence.py +0 -0
  63. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/io/sequence_test.py +0 -0
  64. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/logging.py +0 -0
  65. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/logging_test.py +0 -0
  66. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/monitoring.py +0 -0
  67. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/monitoring_test.py +0 -0
  68. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/patching/__init__.py +0 -0
  69. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/patching/object_factory.py +0 -0
  70. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/patching/object_factory_test.py +0 -0
  71. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/patching/pattern_based.py +0 -0
  72. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/patching/pattern_based_test.py +0 -0
  73. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/patching/rule_based.py +0 -0
  74. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/patching/rule_based_test.py +0 -0
  75. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/__init__.py +0 -0
  76. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/base.py +0 -0
  77. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/base_test.py +0 -0
  78. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/boilerplate.py +0 -0
  79. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/boilerplate_test.py +0 -0
  80. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/class_wrapper.py +0 -0
  81. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/class_wrapper_test.py +0 -0
  82. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/compounding.py +0 -0
  83. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/compounding_test.py +0 -0
  84. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/contextual_object.py +0 -0
  85. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/contextual_object_test.py +0 -0
  86. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/dict.py +0 -0
  87. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/dict_test.py +0 -0
  88. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/diff.py +0 -0
  89. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/diff_test.py +0 -0
  90. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/error_info.py +0 -0
  91. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/error_info_test.py +0 -0
  92. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/flags.py +0 -0
  93. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/flags_test.py +0 -0
  94. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/functor.py +0 -0
  95. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/functor_test.py +0 -0
  96. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/inferred.py +0 -0
  97. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/inferred_test.py +0 -0
  98. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/list.py +0 -0
  99. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/list_test.py +0 -0
  100. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/object.py +0 -0
  101. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/object_test.py +0 -0
  102. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/origin.py +0 -0
  103. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/origin_test.py +0 -0
  104. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/pure_symbolic.py +0 -0
  105. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/ref.py +0 -0
  106. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/ref_test.py +0 -0
  107. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/symbolize.py +0 -0
  108. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/symbolic/symbolize_test.py +0 -0
  109. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/tuning/__init__.py +0 -0
  110. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/tuning/backend.py +0 -0
  111. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/tuning/backend_test.py +0 -0
  112. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/tuning/early_stopping.py +0 -0
  113. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/tuning/local_backend.py +0 -0
  114. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/tuning/protocols.py +0 -0
  115. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/tuning/protocols_test.py +0 -0
  116. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/tuning/sample.py +0 -0
  117. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/tuning/sample_test.py +0 -0
  118. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/__init__.py +0 -0
  119. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/annotated.py +0 -0
  120. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/annotated_test.py +0 -0
  121. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/annotation_conversion.py +0 -0
  122. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/annotation_conversion_test.py +0 -0
  123. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/annotation_future_test.py +0 -0
  124. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/callable_ext.py +0 -0
  125. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/callable_ext_test.py +0 -0
  126. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/callable_signature.py +0 -0
  127. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/callable_signature_test.py +0 -0
  128. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/class_schema.py +0 -0
  129. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/class_schema_test.py +0 -0
  130. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/custom_typing.py +0 -0
  131. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/inspect.py +0 -0
  132. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/inspect_test.py +0 -0
  133. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/json_schema.py +0 -0
  134. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/json_schema_test.py +0 -0
  135. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/key_specs.py +0 -0
  136. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/key_specs_test.py +0 -0
  137. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/pytype_support.py +0 -0
  138. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/type_conversion.py +0 -0
  139. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/typed_missing.py +0 -0
  140. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/typed_missing_test.py +0 -0
  141. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/value_specs.py +0 -0
  142. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/typing/value_specs_test.py +0 -0
  143. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/__init__.py +0 -0
  144. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/common_traits.py +0 -0
  145. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/common_traits_test.py +0 -0
  146. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/contextual.py +0 -0
  147. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/contextual_test.py +0 -0
  148. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/docstr_utils.py +0 -0
  149. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/docstr_utils_test.py +0 -0
  150. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/error_utils.py +0 -0
  151. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/error_utils_test.py +0 -0
  152. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/formatting.py +0 -0
  153. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/formatting_test.py +0 -0
  154. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/hierarchical.py +0 -0
  155. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/hierarchical_test.py +0 -0
  156. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/json_conversion.py +0 -0
  157. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/json_conversion_test.py +0 -0
  158. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/missing.py +0 -0
  159. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/missing_test.py +0 -0
  160. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/text_color.py +0 -0
  161. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/text_color_test.py +0 -0
  162. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/thread_local.py +0 -0
  163. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/thread_local_test.py +0 -0
  164. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/timing.py +0 -0
  165. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/timing_test.py +0 -0
  166. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/value_location.py +0 -0
  167. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/utils/value_location_test.py +0 -0
  168. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/views/__init__.py +0 -0
  169. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/views/base.py +0 -0
  170. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/views/base_test.py +0 -0
  171. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/views/html/__init__.py +0 -0
  172. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/views/html/base.py +0 -0
  173. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/views/html/base_test.py +0 -0
  174. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/views/html/controls/__init__.py +0 -0
  175. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/views/html/controls/base.py +0 -0
  176. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/views/html/controls/label.py +0 -0
  177. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/views/html/controls/label_test.py +0 -0
  178. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/views/html/controls/progress_bar.py +0 -0
  179. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/views/html/controls/progress_bar_test.py +0 -0
  180. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/views/html/controls/tab.py +0 -0
  181. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/views/html/controls/tab_test.py +0 -0
  182. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/views/html/controls/tooltip.py +0 -0
  183. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/views/html/controls/tooltip_test.py +0 -0
  184. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/views/html/tree_view.py +0 -0
  185. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/core/views/html/tree_view_test.py +0 -0
  186. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/__init__.py +0 -0
  187. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/early_stopping/__init__.py +0 -0
  188. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/early_stopping/base.py +0 -0
  189. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/early_stopping/base_test.py +0 -0
  190. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/early_stopping/step_wise.py +0 -0
  191. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/early_stopping/step_wise_test.py +0 -0
  192. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/evolution/__init__.py +0 -0
  193. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/evolution/base.py +0 -0
  194. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/evolution/base_test.py +0 -0
  195. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/evolution/hill_climb.py +0 -0
  196. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/evolution/hill_climb_test.py +0 -0
  197. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/evolution/mutators.py +0 -0
  198. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/evolution/mutators_test.py +0 -0
  199. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/evolution/neat.py +0 -0
  200. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/evolution/neat_test.py +0 -0
  201. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/evolution/nsga2.py +0 -0
  202. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/evolution/nsga2_test.py +0 -0
  203. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/evolution/recombinators.py +0 -0
  204. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/evolution/recombinators_test.py +0 -0
  205. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/evolution/regularized_evolution.py +0 -0
  206. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/evolution/regularized_evolution_test.py +0 -0
  207. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/evolution/selectors.py +0 -0
  208. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/evolution/selectors_test.py +0 -0
  209. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/evolution/where.py +0 -0
  210. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/evolution/where_test.py +0 -0
  211. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/mutfun/__init__.py +0 -0
  212. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/mutfun/base.py +0 -0
  213. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/mutfun/base_test.py +0 -0
  214. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/mutfun/basic_ops.py +0 -0
  215. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/mutfun/basic_ops_test.py +0 -0
  216. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/scalars/__init__.py +0 -0
  217. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/scalars/base.py +0 -0
  218. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/scalars/base_test.py +0 -0
  219. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/scalars/maths.py +0 -0
  220. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/scalars/maths_test.py +0 -0
  221. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/scalars/randoms.py +0 -0
  222. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/scalars/randoms_test.py +0 -0
  223. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/scalars/step_wise.py +0 -0
  224. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove/ext/scalars/step_wise_test.py +0 -0
  225. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove.egg-info/SOURCES.txt +0 -0
  226. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove.egg-info/dependency_links.txt +0 -0
  227. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove.egg-info/requires.txt +0 -0
  228. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/pyglove.egg-info/top_level.txt +0 -0
  229. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/setup.cfg +0 -0
  230. {pyglove-0.5.0.dev202511040811 → pyglove-0.5.0.dev202511161718}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyglove
3
- Version: 0.5.0.dev202511040811
3
+ Version: 0.5.0.dev202511161718
4
4
  Summary: PyGlove: A library for manipulating Python objects.
5
5
  Home-page: https://github.com/google/pyglove
6
6
  Author: PyGlove Authors
@@ -14,8 +14,12 @@
14
14
  """Pluggable file system."""
15
15
 
16
16
  import abc
17
+ import fnmatch
18
+ import glob as std_glob
17
19
  import io
18
20
  import os
21
+ import re
22
+ import time
19
23
  from typing import Any, Literal, Optional, Union
20
24
 
21
25
 
@@ -84,6 +88,10 @@ class FileSystem(metaclass=abc.ABCMeta):
84
88
  def exists(self, path: Union[str, os.PathLike[str]]) -> bool:
85
89
  """Returns True if a path exists."""
86
90
 
91
+ @abc.abstractmethod
92
+ def glob(self, pattern: Union[str, os.PathLike[str]]) -> list[str]:
93
+ """Lists all files or sub-directories."""
94
+
87
95
  @abc.abstractmethod
88
96
  def listdir(self, path: Union[str, os.PathLike[str]]) -> list[str]:
89
97
  """Lists all files or sub-directories."""
@@ -111,6 +119,14 @@ class FileSystem(metaclass=abc.ABCMeta):
111
119
  def rm(self, path: Union[str, os.PathLike[str]]) -> None:
112
120
  """Removes a file based on a path."""
113
121
 
122
+ @abc.abstractmethod
123
+ def rename(
124
+ self,
125
+ oldpath: Union[str, os.PathLike[str]],
126
+ newpath: Union[str, os.PathLike[str]],
127
+ ) -> None:
128
+ """Renames a file based on a path."""
129
+
114
130
  @abc.abstractmethod
115
131
  def rmdir(self, path: Union[str, os.PathLike[str]]) -> bool:
116
132
  """Removes a directory based on a path."""
@@ -137,9 +153,10 @@ def resolve_path(path: Union[str, os.PathLike[str]]) -> str:
137
153
  class StdFile(File):
138
154
  """The standard file."""
139
155
 
140
- def __init__(self, file_object) -> None:
156
+ def __init__(self, file_object, path: Union[str, os.PathLike[str]]) -> None:
141
157
  super().__init__()
142
158
  self._file_object = file_object
159
+ self._path = path
143
160
 
144
161
  def read(self, size: Optional[int] = None) -> Union[str, bytes]:
145
162
  return self._file_object.read(size)
@@ -162,6 +179,14 @@ class StdFile(File):
162
179
  def close(self) -> None:
163
180
  self._file_object.close()
164
181
 
182
+ # For some file systems, the file might not be immediately available
183
+ # after writing. We retry for a few times to ensure the file is
184
+ # world-readable.
185
+ while True:
186
+ if os.path.exists(self._path):
187
+ break
188
+ time.sleep(0.1)
189
+
165
190
 
166
191
  class StdFileSystem(FileSystem):
167
192
  """The standard file system."""
@@ -169,7 +194,7 @@ class StdFileSystem(FileSystem):
169
194
  def open(
170
195
  self, path: Union[str, os.PathLike[str]], mode: str = 'r', **kwargs
171
196
  ) -> File:
172
- return StdFile(io.open(path, mode, **kwargs))
197
+ return StdFile(io.open(path, mode, **kwargs), path)
173
198
 
174
199
  def chmod(self, path: Union[str, os.PathLike[str]], mode: int) -> None:
175
200
  os.chmod(path, mode)
@@ -177,6 +202,9 @@ class StdFileSystem(FileSystem):
177
202
  def exists(self, path: Union[str, os.PathLike[str]]) -> bool:
178
203
  return os.path.exists(path)
179
204
 
205
+ def glob(self, pattern: Union[str, os.PathLike[str]]) -> list[str]:
206
+ return std_glob.glob(pattern)
207
+
180
208
  def listdir(self, path: Union[str, os.PathLike[str]]) -> list[str]:
181
209
  return os.listdir(path)
182
210
 
@@ -196,6 +224,13 @@ class StdFileSystem(FileSystem):
196
224
  ) -> None:
197
225
  os.makedirs(path, mode, exist_ok)
198
226
 
227
+ def rename(
228
+ self,
229
+ oldpath: Union[str, os.PathLike[str]],
230
+ newpath: Union[str, os.PathLike[str]],
231
+ ) -> None:
232
+ os.rename(oldpath, newpath)
233
+
199
234
  def rm(self, path: Union[str, os.PathLike[str]]) -> None:
200
235
  os.remove(path)
201
236
 
@@ -286,6 +321,21 @@ class MemoryFileSystem(FileSystem):
286
321
  def exists(self, path: Union[str, os.PathLike[str]]) -> bool:
287
322
  return self._locate(path) is not None
288
323
 
324
+ def glob(self, pattern: Union[str, os.PathLike[str]]) -> list[str]:
325
+ pattern = resolve_path(pattern)
326
+ regex = re.compile(fnmatch.translate(pattern))
327
+
328
+ results = []
329
+ def _traverse(node, prefix_parts):
330
+ for k, v in node.items():
331
+ path = self._prefix + '/'.join(prefix_parts + [k])
332
+ if regex.match(path):
333
+ results.append(path)
334
+ if isinstance(v, dict):
335
+ _traverse(v, prefix_parts + [k])
336
+ _traverse(self._root, [])
337
+ return results
338
+
289
339
  def listdir(self, path: Union[str, os.PathLike[str]]) -> list[str]:
290
340
  d = self._locate(path)
291
341
  if not isinstance(d, dict):
@@ -338,6 +388,46 @@ class MemoryFileSystem(FileSystem):
338
388
  raise NotADirectoryError(path)
339
389
  current = entry
340
390
 
391
+ def rename(
392
+ self,
393
+ oldpath: Union[str, os.PathLike[str]],
394
+ newpath: Union[str, os.PathLike[str]],
395
+ ) -> None:
396
+ oldpath_str = resolve_path(oldpath)
397
+ newpath_str = resolve_path(newpath)
398
+
399
+ old_parent_dir, old_name = self._parent_and_name(oldpath_str)
400
+ entry = old_parent_dir.get(old_name)
401
+
402
+ if entry is None:
403
+ raise FileNotFoundError(oldpath_str)
404
+
405
+ new_entry = self._locate(newpath_str)
406
+ if new_entry is not None:
407
+ if isinstance(entry, dict): # oldpath is dir
408
+ if not isinstance(new_entry, dict):
409
+ raise NotADirectoryError(
410
+ f'Cannot rename directory {oldpath_str!r} '
411
+ f'to non-directory {newpath_str!r}')
412
+ elif new_entry:
413
+ raise OSError(f'Directory not empty: {newpath_str!r}')
414
+ else:
415
+ self.rmdir(newpath_str)
416
+ else: # oldpath is file
417
+ if isinstance(new_entry, dict):
418
+ raise IsADirectoryError(
419
+ f'Cannot rename non-directory {oldpath_str!r} '
420
+ f'to directory {newpath_str!r}')
421
+ else:
422
+ self.rm(newpath_str)
423
+ elif isinstance(entry, dict) and newpath_str.startswith(oldpath_str + '/'):
424
+ raise OSError(f'Cannot move directory {oldpath_str!r} '
425
+ f'to a subdirectory of itself {newpath_str!r}')
426
+
427
+ new_parent_dir, new_name = self._parent_and_name(newpath_str)
428
+ new_parent_dir[new_name] = entry
429
+ del old_parent_dir[old_name]
430
+
341
431
  def rm(self, path: Union[str, os.PathLike[str]]) -> None:
342
432
  parent_dir, name = self._parent_and_name(path)
343
433
  entry = parent_dir.get(name)
@@ -458,6 +548,14 @@ def writefile(
458
548
  chmod(path, perms)
459
549
 
460
550
 
551
+ def rename(
552
+ oldpath: Union[str, os.PathLike[str]],
553
+ newpath: Union[str, os.PathLike[str]],
554
+ ) -> None:
555
+ """Renames a file."""
556
+ _fs.get(oldpath).rename(oldpath, newpath)
557
+
558
+
461
559
  def rm(path: Union[str, os.PathLike[str]]) -> None:
462
560
  """Removes a file."""
463
561
  _fs.get(path).rm(path)
@@ -468,6 +566,11 @@ def path_exists(path: Union[str, os.PathLike[str]]) -> bool:
468
566
  return _fs.get(path).exists(path)
469
567
 
470
568
 
569
+ def glob(pattern: Union[str, os.PathLike[str]]) -> list[str]:
570
+ """Lists all files or sub-directories."""
571
+ return _fs.get(pattern).glob(pattern)
572
+
573
+
471
574
  def listdir(
472
575
  path: Union[str, os.PathLike[str]], fullpath: bool = False
473
576
  ) -> list[str]: # pylint: disable=redefined-builtin
@@ -82,6 +82,75 @@ class StdFileSystemTest(unittest.TestCase):
82
82
  fs.rmdirs(os.path.join(dir_a, 'b/c'))
83
83
  self.assertEqual(sorted(fs.listdir(dir_a)), ['file1']) # pylint: disable=g-generic-assert
84
84
 
85
+ def test_rename(self):
86
+ tmp_dir = tempfile.mkdtemp()
87
+ fs = file_system.StdFileSystem()
88
+
89
+ _ = fs.mkdirs(os.path.join(tmp_dir, 'a/b'))
90
+ file_foo = os.path.join(tmp_dir, 'a/foo.txt')
91
+ file_bar = os.path.join(tmp_dir, 'a/bar.txt')
92
+
93
+ with fs.open(file_foo, 'w') as f:
94
+ f.write('foo')
95
+ with fs.open(file_bar, 'w') as f:
96
+ f.write('bar')
97
+
98
+ # Rename file to a new name.
99
+ file_foo_new = os.path.join(tmp_dir, 'a/foo-new.txt')
100
+ fs.rename(file_foo, file_foo_new)
101
+ self.assertFalse(fs.exists(file_foo))
102
+ self.assertTrue(fs.exists(file_foo_new))
103
+
104
+ # Rename file to an existing file name.
105
+ fs.rename(file_foo_new, file_bar)
106
+ self.assertFalse(fs.exists(file_foo_new))
107
+ with fs.open(file_bar, 'r') as f:
108
+ self.assertEqual(f.read(), 'foo')
109
+
110
+ # Rename directory to a new name.
111
+ dir_b = os.path.join(tmp_dir, 'a/b')
112
+ dir_c = os.path.join(tmp_dir, 'a/c')
113
+ fs.rename(dir_b, dir_c)
114
+ self.assertFalse(fs.exists(dir_b))
115
+ self.assertTrue(fs.exists(dir_c))
116
+ self.assertTrue(fs.isdir(dir_c))
117
+
118
+ # Rename directory to an existing empty directory.
119
+ dir_d = os.path.join(tmp_dir, 'a/d')
120
+ fs.mkdirs(dir_d)
121
+ fs.rename(dir_c, dir_d)
122
+ self.assertFalse(fs.exists(dir_c))
123
+ self.assertTrue(fs.exists(dir_d))
124
+
125
+ # Rename directory to a non-empty directory.
126
+ dir_x = os.path.join(tmp_dir, 'x')
127
+ dir_a = os.path.join(tmp_dir, 'a')
128
+ fs.mkdirs(os.path.join(dir_x, 'y'))
129
+ with self.assertRaises(OSError):
130
+ fs.rename(dir_a, dir_x)
131
+ self.assertTrue(fs.exists(dir_a))
132
+ self.assertTrue(fs.exists(os.path.join(dir_x, 'y')))
133
+
134
+ # Errors
135
+ dir_u = os.path.join(tmp_dir, 'u')
136
+ dir_u_v = os.path.join(dir_u, 'v')
137
+ file_u_a = os.path.join(dir_u, 'a.txt')
138
+ fs.mkdirs(dir_u_v)
139
+ with fs.open(file_u_a, 'w') as f:
140
+ f.write('a')
141
+
142
+ with self.assertRaises((OSError, NotADirectoryError)):
143
+ fs.rename(dir_u, file_u_a)
144
+
145
+ with self.assertRaises(IsADirectoryError):
146
+ fs.rename(file_u_a, dir_u_v)
147
+
148
+ with self.assertRaises(FileNotFoundError):
149
+ fs.rename(
150
+ os.path.join(tmp_dir, 'non-existent'),
151
+ os.path.join(tmp_dir, 'y')
152
+ )
153
+
85
154
 
86
155
  class MemoryFileSystemTest(unittest.TestCase):
87
156
 
@@ -180,6 +249,94 @@ class MemoryFileSystemTest(unittest.TestCase):
180
249
  fs.rmdirs(os.path.join(dir_a, 'b/c'))
181
250
  self.assertEqual(fs.listdir(dir_a), ['file1']) # pylint: disable=g-generic-assert
182
251
 
252
+ def test_glob(self):
253
+ fs = file_system.MemoryFileSystem()
254
+ fs.mkdirs('/mem/a/b/c')
255
+ with fs.open('/mem/a/foo.txt', 'w') as f:
256
+ f.write('foo')
257
+ with fs.open('/mem/a/bar.json', 'w') as f:
258
+ f.write('bar')
259
+ with fs.open('/mem/a/b/baz.txt', 'w') as f:
260
+ f.write('baz')
261
+
262
+ self.assertEqual(
263
+ sorted(fs.glob('/mem/a/*')),
264
+ ['/mem/a/b', '/mem/a/b/baz.txt', '/mem/a/b/c',
265
+ '/mem/a/bar.json', '/mem/a/foo.txt'])
266
+ self.assertEqual(
267
+ sorted(fs.glob('/mem/a/*.txt')),
268
+ ['/mem/a/b/baz.txt', '/mem/a/foo.txt'])
269
+ self.assertEqual(
270
+ sorted(fs.glob('/mem/a/b/*')),
271
+ ['/mem/a/b/baz.txt', '/mem/a/b/c'])
272
+ self.assertEqual(fs.glob('/mem/a/b/*.txt'), ['/mem/a/b/baz.txt'])
273
+ self.assertEqual(fs.glob('/mem/a/b/c/*'), [])
274
+ self.assertEqual(fs.glob('/mem/a/???.txt'), ['/mem/a/foo.txt'])
275
+ self.assertEqual(fs.glob('/mem/a/bar.*'), ['/mem/a/bar.json'])
276
+ self.assertEqual(
277
+ sorted(fs.glob('/mem/a/*.*')),
278
+ ['/mem/a/b/baz.txt', '/mem/a/bar.json', '/mem/a/foo.txt'])
279
+
280
+ def test_rename(self):
281
+ fs = file_system.MemoryFileSystem()
282
+ fs.mkdirs('/mem/a/b')
283
+ with fs.open('/mem/a/foo.txt', 'w') as f:
284
+ f.write('foo')
285
+ with fs.open('/mem/a/bar.txt', 'w') as f:
286
+ f.write('bar')
287
+
288
+ # Rename file to a new name.
289
+ fs.rename('/mem/a/foo.txt', '/mem/a/foo-new.txt')
290
+ self.assertFalse(fs.exists('/mem/a/foo.txt'))
291
+ self.assertTrue(fs.exists('/mem/a/foo-new.txt'))
292
+
293
+ # Rename file to an existing file name.
294
+ fs.rename('/mem/a/foo-new.txt', '/mem/a/bar.txt')
295
+ self.assertFalse(fs.exists('/mem/a/foo-new.txt'))
296
+ with fs.open('/mem/a/bar.txt', 'r') as f:
297
+ self.assertEqual(f.read(), 'foo')
298
+
299
+ # Rename directory to a new name.
300
+ fs.rename('/mem/a/b', '/mem/a/c')
301
+ self.assertFalse(fs.exists('/mem/a/b'))
302
+ self.assertTrue(fs.exists('/mem/a/c'))
303
+ self.assertTrue(fs.isdir('/mem/a/c'))
304
+
305
+ # Rename directory to an existing empty directory.
306
+ fs.mkdirs('/mem/a/d')
307
+ fs.rename('/mem/a/c', '/mem/a/d')
308
+ self.assertFalse(fs.exists('/mem/a/c'))
309
+ self.assertTrue(fs.exists('/mem/a/d'))
310
+
311
+ # Rename directory to a non-empty directory.
312
+ fs.mkdirs('/mem/x/y')
313
+ with self.assertRaisesRegex(OSError, "Directory not empty: '/mem/x'"):
314
+ fs.rename('/mem/a', '/mem/x')
315
+ self.assertTrue(fs.exists('/mem/a'))
316
+ self.assertTrue(fs.exists('/mem/x/y'))
317
+
318
+ # Errors
319
+ fs.mkdirs('/mem/u/v')
320
+ with fs.open('/mem/u/a.txt', 'w') as f:
321
+ f.write('a')
322
+
323
+ with self.assertRaisesRegex(
324
+ OSError, "Cannot move directory '/mem/u' to a subdirectory of itself"):
325
+ fs.rename('/mem/u', '/mem/u/v/w')
326
+
327
+ with self.assertRaisesRegex(
328
+ NotADirectoryError,
329
+ "Cannot rename directory '/mem/u' to non-directory '/mem/u/a.txt'"):
330
+ fs.rename('/mem/u', '/mem/u/a.txt')
331
+
332
+ with self.assertRaisesRegex(
333
+ IsADirectoryError,
334
+ "Cannot rename non-directory '/mem/u/a.txt' to directory '/mem/u/v'"):
335
+ fs.rename('/mem/u/a.txt', '/mem/u/v')
336
+
337
+ with self.assertRaises(FileNotFoundError):
338
+ fs.rename('/mem/non-existent', '/mem/y')
339
+
183
340
 
184
341
  class FileIoApiTest(unittest.TestCase):
185
342
 
@@ -217,6 +374,14 @@ class FileIoApiTest(unittest.TestCase):
217
374
  file_system.rm(file2)
218
375
  self.assertFalse(file_system.path_exists(file2))
219
376
 
377
+ # Test glob with standard file system.
378
+ glob_dir = os.path.join(tempfile.mkdtemp(), 'glob')
379
+ file_system.mkdirs(os.path.join(glob_dir, 'a/b'))
380
+ file_system.writefile(os.path.join(glob_dir, 'a/foo.txt'), 'foo')
381
+ self.assertEqual(
382
+ sorted(file_system.glob(os.path.join(glob_dir, 'a/*'))),
383
+ [os.path.join(glob_dir, 'a/b'), os.path.join(glob_dir, 'a/foo.txt')])
384
+
220
385
  def test_memory_filesystem(self):
221
386
  file1 = pathlib.Path('/mem/file1')
222
387
  with self.assertRaises(FileNotFoundError):
@@ -248,6 +413,15 @@ class FileIoApiTest(unittest.TestCase):
248
413
  file_system.rm(file2)
249
414
  self.assertFalse(file_system.path_exists(file2))
250
415
 
416
+ # Test glob with memory file system.
417
+ file_system.mkdirs('/mem/g/a/b')
418
+ file_system.writefile('/mem/g/a/foo.txt', 'foo')
419
+ file_system.rename('/mem/g/a/foo.txt', '/mem/g/a/foo2.txt')
420
+ file_system.writefile('/mem/g/a/b/bar.txt', 'bar')
421
+ self.assertEqual(
422
+ sorted(file_system.glob('/mem/g/a/*')),
423
+ ['/mem/g/a/b', '/mem/g/a/b/bar.txt', '/mem/g/a/foo2.txt'])
424
+
251
425
 
252
426
  if __name__ == '__main__':
253
427
  unittest.main()
@@ -13,6 +13,7 @@
13
13
  # limitations under the License.
14
14
  import calendar
15
15
  import datetime
16
+ import sys
16
17
  import typing
17
18
  import unittest
18
19
 
@@ -130,12 +131,16 @@ class BuiltInConversionsTest(unittest.TestCase):
130
131
  def test_datetime_to_int(self):
131
132
  """Test built-in converter between int and datetime.datetime."""
132
133
  timestamp = calendar.timegm(datetime.datetime.now().timetuple())
133
- now = datetime.datetime.fromtimestamp(timestamp, datetime.UTC)
134
+ if sys.version_info >= (3, 11):
135
+ now = datetime.datetime.fromtimestamp(timestamp, datetime.UTC)
136
+ else:
137
+ now = datetime.datetime.utcfromtimestamp(timestamp)
134
138
  self.assertEqual(vs.Object(datetime.datetime).apply(timestamp), now)
135
139
  self.assertEqual(vs.Int().apply(now), timestamp)
136
140
  self.assertEqual(
137
141
  type_conversion.get_json_value_converter(datetime.datetime)(now),
138
- timestamp)
142
+ timestamp
143
+ )
139
144
 
140
145
  def test_keypath_to_str(self):
141
146
  """Test built-in converter between string and KeyPath."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyglove
3
- Version: 0.5.0.dev202511040811
3
+ Version: 0.5.0.dev202511161718
4
4
  Summary: PyGlove: A library for manipulating Python objects.
5
5
  Home-page: https://github.com/google/pyglove
6
6
  Author: PyGlove Authors