icechunk 0.2.2__tar.gz → 0.2.4__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 (169) hide show
  1. {icechunk-0.2.2 → icechunk-0.2.4}/PKG-INFO +2 -2
  2. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/Cargo.toml +1 -1
  3. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/config.rs +13 -0
  4. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/repository.rs +10 -0
  5. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/storage/logging.rs +7 -0
  6. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/storage/mod.rs +19 -2
  7. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/storage/object_store.rs +87 -3
  8. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/storage/s3.rs +55 -13
  9. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/Cargo.toml +2 -2
  10. icechunk-0.2.4/icechunk-python/examples/mpwrite.py +48 -0
  11. {icechunk-0.2.2 → icechunk-0.2.4/icechunk-python}/python/icechunk/__init__.py +18 -0
  12. {icechunk-0.2.2 → icechunk-0.2.4/icechunk-python}/python/icechunk/_icechunk_python.pyi +4 -0
  13. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/python/icechunk/repository.py +10 -0
  14. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/src/config.rs +4 -0
  15. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/src/repository.rs +24 -0
  16. icechunk-0.2.4/icechunk-python/tests/test_debuginfo.py +6 -0
  17. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_pickle.py +12 -0
  18. {icechunk-0.2.2 → icechunk-0.2.4}/pyproject.toml +1 -1
  19. {icechunk-0.2.2/icechunk-python → icechunk-0.2.4}/python/icechunk/__init__.py +18 -0
  20. {icechunk-0.2.2/icechunk-python → icechunk-0.2.4}/python/icechunk/_icechunk_python.pyi +4 -0
  21. {icechunk-0.2.2 → icechunk-0.2.4}/python/icechunk/repository.py +10 -0
  22. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/examples/low_level_dataset.rs +0 -0
  23. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/examples/multithreaded_get_chunk_refs.rs +0 -0
  24. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/examples/multithreaded_store.rs +0 -0
  25. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/flatbuffers/all.fbs +0 -0
  26. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/flatbuffers/manifest.fbs +0 -0
  27. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/flatbuffers/object_ids.fbs +0 -0
  28. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/flatbuffers/snapshot.fbs +0 -0
  29. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/flatbuffers/transaction_log.fbs +0 -0
  30. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/proptest-regressions/dataset.txt +0 -0
  31. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/proptest-regressions/format/manifest.txt +0 -0
  32. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/proptest-regressions/manifest.txt +0 -0
  33. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/proptest-regressions/session.txt +0 -0
  34. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/proptest-regressions/storage/mod.txt +0 -0
  35. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/proptest-regressions/storage/s3.txt +0 -0
  36. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/proptest-regressions/storage/virtual_ref.txt +0 -0
  37. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/asset_manager.rs +0 -0
  38. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/change_set.rs +0 -0
  39. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/conflicts/basic_solver.rs +0 -0
  40. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/conflicts/detector.rs +0 -0
  41. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/conflicts/mod.rs +0 -0
  42. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/error.rs +0 -0
  43. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/format/attributes.rs +0 -0
  44. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/format/flatbuffers/all_generated.rs +0 -0
  45. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/format/manifest.rs +0 -0
  46. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/format/mod.rs +0 -0
  47. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/format/serializers/mod.rs +0 -0
  48. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/format/snapshot.rs +0 -0
  49. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/format/transaction_log.rs +0 -0
  50. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/lib.rs +0 -0
  51. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/ops/gc.rs +0 -0
  52. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/ops/mod.rs +0 -0
  53. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/refs.rs +0 -0
  54. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/session.rs +0 -0
  55. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/store.rs +0 -0
  56. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/strategies.rs +0 -0
  57. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/src/virtual_chunks.rs +0 -0
  58. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/tests/test_concurrency.rs +0 -0
  59. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/tests/test_distributed_writes.rs +0 -0
  60. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/tests/test_gc.rs +0 -0
  61. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/tests/test_storage.rs +0 -0
  62. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk/tests/test_virtual_refs.rs +0 -0
  63. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/.gitignore +0 -0
  64. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/Cargo.lock +0 -0
  65. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/benchmarks/__init__.py +0 -0
  66. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/benchmarks/coiled_runner.py +0 -0
  67. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/benchmarks/conftest.py +0 -0
  68. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/benchmarks/create_era5.py +0 -0
  69. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/benchmarks/datasets.py +0 -0
  70. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/benchmarks/envs/icechunk-alpha-12.txt +0 -0
  71. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/benchmarks/envs/icechunk-alpha-release.txt +0 -0
  72. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/benchmarks/helpers.py +0 -0
  73. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/benchmarks/lib.py +0 -0
  74. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/benchmarks/most_recent.sh +0 -0
  75. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/benchmarks/runner.py +0 -0
  76. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/benchmarks/tasks.py +0 -0
  77. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/benchmarks/test_benchmark_reads.py +0 -0
  78. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/benchmarks/test_benchmark_writes.py +0 -0
  79. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/examples/dask_write.py +0 -0
  80. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/notebooks/conflict.ipynb +0 -0
  81. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/notebooks/demo-azure-blob.ipynb +0 -0
  82. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/notebooks/demo-dummy-data.ipynb +0 -0
  83. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/notebooks/demo-gcs.ipynb +0 -0
  84. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/notebooks/demo-s3.ipynb +0 -0
  85. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/notebooks/environment.yml +0 -0
  86. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/notebooks/memorystore.ipynb +0 -0
  87. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/notebooks/performance/era5_xarray-Icechunk.ipynb +0 -0
  88. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/notebooks/performance/era5_xarray-zarr2.ipynb +0 -0
  89. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/notebooks/performance/era5_xarray-zarr3.ipynb +0 -0
  90. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/notebooks/version-control.ipynb +0 -0
  91. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/python/icechunk/credentials.py +0 -0
  92. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/python/icechunk/dask.py +0 -0
  93. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/python/icechunk/distributed.py +0 -0
  94. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/python/icechunk/py.typed +0 -0
  95. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/python/icechunk/session.py +0 -0
  96. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/python/icechunk/storage.py +0 -0
  97. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/python/icechunk/store.py +0 -0
  98. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/python/icechunk/vendor/__init__.py +0 -0
  99. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/python/icechunk/vendor/xarray.py +0 -0
  100. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/python/icechunk/xarray.py +0 -0
  101. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/src/conflicts.rs +0 -0
  102. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/src/errors.rs +0 -0
  103. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/src/lib.rs +0 -0
  104. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/src/session.rs +0 -0
  105. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/src/store.rs +0 -0
  106. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/src/streams.rs +0 -0
  107. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/__init__.py +0 -0
  108. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/conftest.py +0 -0
  109. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/chunks/09HEW2P03CSMHFAZY7DG +0 -0
  110. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/chunks/52H0E4NSPN8SVRK9EVGG +0 -0
  111. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/chunks/DWQ75SDC624XF9H326RG +0 -0
  112. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/chunks/RW938N1KP2R4BHMW62QG +0 -0
  113. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/config.yaml +0 -0
  114. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/manifests/CMYVHDWMSTG9R25780YG +0 -0
  115. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/manifests/G3W2W8V6ZG09J6C21WE0 +0 -0
  116. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/manifests/Q04J7QW5RQ8D17TPA10G +0 -0
  117. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/manifests/SHTEAP8C784YMZSJKBM0 +0 -0
  118. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/refs/branch.main/ref.json +0 -0
  119. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/refs/branch.my-branch/ref.json +0 -0
  120. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/refs/tag.deleted/ref.json +0 -0
  121. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/refs/tag.deleted/ref.json.deleted +0 -0
  122. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/refs/tag.it also works!/ref.json +0 -0
  123. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/refs/tag.it works!/ref.json +0 -0
  124. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/snapshots/4QF8JA0YPDN51MHSSYVG +0 -0
  125. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/snapshots/7XAF0Q905SH4938DN9CG +0 -0
  126. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/snapshots/GC4YVH5SKBPEZCENYQE0 +0 -0
  127. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/snapshots/NXH3M0HJ7EEJ0699DPP0 +0 -0
  128. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/snapshots/P874YS3J196959RDHX7G +0 -0
  129. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/snapshots/XDZ162T1TYBEJMK99NPG +0 -0
  130. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/transactions/4QF8JA0YPDN51MHSSYVG +0 -0
  131. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/transactions/7XAF0Q905SH4938DN9CG +0 -0
  132. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/transactions/GC4YVH5SKBPEZCENYQE0 +0 -0
  133. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/transactions/NXH3M0HJ7EEJ0699DPP0 +0 -0
  134. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/data/test-repo/transactions/XDZ162T1TYBEJMK99NPG +0 -0
  135. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/run_xarray_backends_tests.py +0 -0
  136. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_can_read_old.py +0 -0
  137. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_commit_properties.py +0 -0
  138. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_concurrency.py +0 -0
  139. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_config.py +0 -0
  140. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_conflicts.py +0 -0
  141. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_credentials.py +0 -0
  142. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_dask.py +0 -0
  143. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_distributed_writers.py +0 -0
  144. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_error.py +0 -0
  145. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_gc.py +0 -0
  146. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_regressions.py +0 -0
  147. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_stateful_repo_ops.py +0 -0
  148. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_store.py +0 -0
  149. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_timetravel.py +0 -0
  150. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_virtual_ref.py +0 -0
  151. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_xarray.py +0 -0
  152. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_zarr/__init__.py +0 -0
  153. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_zarr/test_array.py +0 -0
  154. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_zarr/test_group.py +0 -0
  155. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_zarr/test_properties.py +0 -0
  156. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_zarr/test_stateful.py +0 -0
  157. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_zarr/test_store/__init__.py +0 -0
  158. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_zarr/test_store/test_core.py +0 -0
  159. {icechunk-0.2.2 → icechunk-0.2.4}/icechunk-python/tests/test_zarr/test_store/test_icechunk_store.py +0 -0
  160. {icechunk-0.2.2 → icechunk-0.2.4}/python/icechunk/credentials.py +0 -0
  161. {icechunk-0.2.2 → icechunk-0.2.4}/python/icechunk/dask.py +0 -0
  162. {icechunk-0.2.2 → icechunk-0.2.4}/python/icechunk/distributed.py +0 -0
  163. {icechunk-0.2.2 → icechunk-0.2.4}/python/icechunk/py.typed +0 -0
  164. {icechunk-0.2.2 → icechunk-0.2.4}/python/icechunk/session.py +0 -0
  165. {icechunk-0.2.2 → icechunk-0.2.4}/python/icechunk/storage.py +0 -0
  166. {icechunk-0.2.2 → icechunk-0.2.4}/python/icechunk/store.py +0 -0
  167. {icechunk-0.2.2 → icechunk-0.2.4}/python/icechunk/vendor/__init__.py +0 -0
  168. {icechunk-0.2.2 → icechunk-0.2.4}/python/icechunk/vendor/xarray.py +0 -0
  169. {icechunk-0.2.2 → icechunk-0.2.4}/python/icechunk/xarray.py +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: icechunk
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
7
7
  Classifier: Programming Language :: Python :: 3
8
8
  Classifier: Programming Language :: Python :: 3.11
9
9
  Classifier: Programming Language :: Python :: 3.12
10
- Requires-Dist: zarr>=3
10
+ Requires-Dist: zarr>=3,!=3.0.3
11
11
  Requires-Dist: boto3 ; extra == 'test'
12
12
  Requires-Dist: coverage ; extra == 'test'
13
13
  Requires-Dist: mypy ; extra == 'test'
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "icechunk"
3
- version = "0.2.2"
3
+ version = "0.2.4"
4
4
  description = "Transactional storage engine for Zarr designed for use on cloud object storage"
5
5
  readme = "README.md"
6
6
  repository = "https://github.com/earth-mover/icechunk"
@@ -24,6 +24,19 @@ pub struct S3Options {
24
24
  pub allow_http: bool,
25
25
  }
26
26
 
27
+ impl fmt::Display for S3Options {
28
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29
+ write!(
30
+ f,
31
+ "S3Options(region={}, endpoint_url={}, anonymous={}, allow_http={})",
32
+ self.region.as_deref().unwrap_or("None"),
33
+ self.endpoint_url.as_deref().unwrap_or("None"),
34
+ self.anonymous,
35
+ self.allow_http
36
+ )
37
+ }
38
+ }
39
+
27
40
  #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
28
41
  #[serde(rename_all = "snake_case")]
29
42
  pub enum ObjectStoreConfig {
@@ -331,6 +331,16 @@ impl Repository {
331
331
  )
332
332
  }
333
333
 
334
+ #[instrument(skip(bytes))]
335
+ pub fn from_bytes(bytes: Vec<u8>) -> RepositoryResult<Self> {
336
+ rmp_serde::from_slice(&bytes).err_into()
337
+ }
338
+
339
+ #[instrument(skip(self))]
340
+ pub fn as_bytes(&self) -> RepositoryResult<Vec<u8>> {
341
+ rmp_serde::to_vec(self).err_into()
342
+ }
343
+
334
344
  #[instrument(skip_all)]
335
345
  pub async fn fetch_config(
336
346
  storage: &(dyn Storage + Send + Sync),
@@ -1,4 +1,5 @@
1
1
  use std::{
2
+ fmt,
2
3
  ops::Range,
3
4
  sync::{Arc, Mutex},
4
5
  };
@@ -37,6 +38,12 @@ impl LoggingStorage {
37
38
  }
38
39
  }
39
40
 
41
+ impl fmt::Display for LoggingStorage {
42
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43
+ write!(f, "LoggingStorage(backend={})", self.backend)
44
+ }
45
+ }
46
+
40
47
  impl private::Sealed for LoggingStorage {}
41
48
 
42
49
  #[async_trait]
@@ -298,7 +298,7 @@ pub enum WriteRefResult {
298
298
  /// Implementations are free to assume files are never overwritten.
299
299
  #[async_trait]
300
300
  #[typetag::serde(tag = "type")]
301
- pub trait Storage: fmt::Debug + private::Sealed + Sync + Send {
301
+ pub trait Storage: fmt::Debug + fmt::Display + private::Sealed + Sync + Send {
302
302
  fn default_settings(&self) -> Settings {
303
303
  Default::default()
304
304
  }
@@ -712,10 +712,27 @@ pub async fn new_gcs_storage(
712
712
  #[allow(clippy::unwrap_used, clippy::panic)]
713
713
  mod tests {
714
714
 
715
- use std::collections::HashSet;
715
+ use std::{collections::HashSet, fs::File, io::Write, path::PathBuf};
716
716
 
717
717
  use super::*;
718
718
  use proptest::prelude::*;
719
+ use tempfile::TempDir;
720
+
721
+ #[tokio::test]
722
+ async fn test_is_clean() {
723
+ let repo_dir = TempDir::new().unwrap();
724
+ let s = new_local_filesystem_storage(repo_dir.path()).await.unwrap();
725
+ assert!(s.root_is_clean().await.unwrap());
726
+
727
+ let mut file = File::create(repo_dir.path().join("foo.txt")).unwrap();
728
+ write!(file, "hello").unwrap();
729
+ assert!(!s.root_is_clean().await.unwrap());
730
+
731
+ let inside_existing =
732
+ PathBuf::from_iter([repo_dir.path().as_os_str().to_str().unwrap(), "foo"]);
733
+ let s = new_local_filesystem_storage(&inside_existing).await.unwrap();
734
+ assert!(s.root_is_clean().await.unwrap());
735
+ }
719
736
 
720
737
  proptest! {
721
738
  #![proptest_config(ProptestConfig {
@@ -27,7 +27,7 @@ use object_store::{
27
27
  use serde::{Deserialize, Serialize};
28
28
  use std::{
29
29
  collections::HashMap,
30
- fmt::Debug,
30
+ fmt::{self, Debug, Display},
31
31
  fs::create_dir_all,
32
32
  future::ready,
33
33
  num::{NonZeroU16, NonZeroU64},
@@ -83,7 +83,6 @@ impl ObjectStorage {
83
83
  Arc::new(LocalFileSystemObjectStoreBackend { path: prefix.to_path_buf() });
84
84
  let client = backend.mk_object_store().await?;
85
85
  let storage = ObjectStorage { backend, client: OnceCell::new_with(Some(client)) };
86
-
87
86
  Ok(storage)
88
87
  }
89
88
 
@@ -286,6 +285,12 @@ impl ObjectStorage {
286
285
  }
287
286
  }
288
287
 
288
+ impl fmt::Display for ObjectStorage {
289
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
290
+ write!(f, "ObjectStorage(backend={})", self.backend)
291
+ }
292
+ }
293
+
289
294
  impl private::Sealed for ObjectStorage {}
290
295
 
291
296
  #[async_trait]
@@ -622,7 +627,7 @@ impl Storage for ObjectStorage {
622
627
 
623
628
  #[async_trait]
624
629
  #[typetag::serde(tag = "object_store_provider_type")]
625
- pub trait ObjectStoreBackend: Debug + Sync + Send {
630
+ pub trait ObjectStoreBackend: Debug + Display + Sync + Send {
626
631
  async fn mk_object_store(&self) -> Result<Arc<dyn ObjectStore>, StorageError>;
627
632
 
628
633
  /// The prefix for the object store.
@@ -640,6 +645,12 @@ pub trait ObjectStoreBackend: Debug + Sync + Send {
640
645
  #[derive(Debug, Serialize, Deserialize)]
641
646
  pub struct InMemoryObjectStoreBackend;
642
647
 
648
+ impl fmt::Display for InMemoryObjectStoreBackend {
649
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
650
+ write!(f, "InMemoryObjectStoreBackend")
651
+ }
652
+ }
653
+
643
654
  #[async_trait]
644
655
  #[typetag::serde(name = "in_memory_object_store_provider")]
645
656
  impl ObjectStoreBackend for InMemoryObjectStoreBackend {
@@ -672,6 +683,12 @@ pub struct LocalFileSystemObjectStoreBackend {
672
683
  path: PathBuf,
673
684
  }
674
685
 
686
+ impl fmt::Display for LocalFileSystemObjectStoreBackend {
687
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
688
+ write!(f, "LocalFileSystemObjectStoreBackend(path={})", self.path.display())
689
+ }
690
+ }
691
+
675
692
  #[async_trait]
676
693
  #[typetag::serde(name = "local_file_system_object_store_provider")]
677
694
  impl ObjectStoreBackend for LocalFileSystemObjectStoreBackend {
@@ -720,6 +737,18 @@ pub struct S3ObjectStoreBackend {
720
737
  config: Option<S3Options>,
721
738
  }
722
739
 
740
+ impl fmt::Display for S3ObjectStoreBackend {
741
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
742
+ write!(
743
+ f,
744
+ "S3ObjectStoreBackend(bucket={}, prefix={}, config={})",
745
+ self.bucket,
746
+ self.prefix.as_deref().unwrap_or(""),
747
+ self.config.as_ref().map(|c| c.to_string()).unwrap_or("None".to_string())
748
+ )
749
+ }
750
+ }
751
+
723
752
  #[async_trait]
724
753
  #[typetag::serde(name = "s3_object_store_provider")]
725
754
  impl ObjectStoreBackend for S3ObjectStoreBackend {
@@ -794,6 +823,18 @@ pub struct AzureObjectStoreBackend {
794
823
  config: Option<HashMap<AzureConfigKey, String>>,
795
824
  }
796
825
 
826
+ impl fmt::Display for AzureObjectStoreBackend {
827
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
828
+ write!(
829
+ f,
830
+ "AzureObjectStoreBackend(account={}, container={}, prefix={})",
831
+ self.account,
832
+ self.container,
833
+ self.prefix.as_deref().unwrap_or("")
834
+ )
835
+ }
836
+ }
837
+
797
838
  #[async_trait]
798
839
  #[typetag::serde(name = "azure_object_store_provider")]
799
840
  impl ObjectStoreBackend for AzureObjectStoreBackend {
@@ -846,6 +887,17 @@ pub struct GcsObjectStoreBackend {
846
887
  config: Option<HashMap<GoogleConfigKey, String>>,
847
888
  }
848
889
 
890
+ impl fmt::Display for GcsObjectStoreBackend {
891
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
892
+ write!(
893
+ f,
894
+ "GcsObjectStoreBackend(bucket={}, prefix={})",
895
+ self.bucket,
896
+ self.prefix.as_deref().unwrap_or("")
897
+ )
898
+ }
899
+ }
900
+
849
901
  #[async_trait]
850
902
  #[typetag::serde(name = "gcs_object_store_provider")]
851
903
  impl ObjectStoreBackend for GcsObjectStoreBackend {
@@ -969,6 +1021,8 @@ mod tests {
969
1021
 
970
1022
  use tempfile::TempDir;
971
1023
 
1024
+ use crate::format::{ChunkId, ManifestId, SnapshotId};
1025
+
972
1026
  use super::ObjectStorage;
973
1027
 
974
1028
  #[tokio::test]
@@ -1018,4 +1072,34 @@ mod tests {
1018
1072
  ObjectStorage::new_local_filesystem(PathBuf::from(&rel_path).as_path()).await;
1019
1073
  assert!(store.is_ok());
1020
1074
  }
1075
+
1076
+ #[tokio::test]
1077
+ async fn test_object_store_paths() {
1078
+ let store = ObjectStorage::new_local_filesystem(PathBuf::from(".").as_path())
1079
+ .await
1080
+ .unwrap();
1081
+
1082
+ let ref_key = "ref_key";
1083
+ let ref_path = store.ref_key(ref_key);
1084
+ assert_eq!(ref_path.to_string(), format!("refs/{ref_key}"));
1085
+
1086
+ let snapshot_id = SnapshotId::random();
1087
+ let snapshot_path = store.get_snapshot_path(&snapshot_id);
1088
+ assert_eq!(snapshot_path.to_string(), format!("snapshots/{snapshot_id}"));
1089
+
1090
+ let manifest_id = ManifestId::random();
1091
+ let manifest_path = store.get_manifest_path(&manifest_id);
1092
+ assert_eq!(manifest_path.to_string(), format!("manifests/{manifest_id}"));
1093
+
1094
+ let chunk_id = ChunkId::random();
1095
+ let chunk_path = store.get_chunk_path(&chunk_id);
1096
+ assert_eq!(chunk_path.to_string(), format!("chunks/{chunk_id}"));
1097
+
1098
+ let transaction_id = SnapshotId::random();
1099
+ let transaction_path = store.get_transaction_path(&transaction_id);
1100
+ assert_eq!(
1101
+ transaction_path.to_string(),
1102
+ format!("transactions/{transaction_id}")
1103
+ );
1104
+ }
1021
1105
  }
@@ -1,4 +1,5 @@
1
1
  use std::{
2
+ fmt,
2
3
  future::ready,
3
4
  ops::Range,
4
5
  path::{Path, PathBuf},
@@ -29,7 +30,6 @@ use aws_sdk_s3::{
29
30
  use aws_smithy_types_convert::{date_time::DateTimeExt, stream::PaginationStreamExt};
30
31
  use bytes::{Buf, Bytes};
31
32
  use chrono::{DateTime, Utc};
32
- use err_into::ErrorInto as _;
33
33
  use futures::{
34
34
  stream::{self, BoxStream},
35
35
  StreamExt, TryStreamExt,
@@ -59,6 +59,16 @@ pub struct S3Storage {
59
59
  client: OnceCell<Arc<Client>>,
60
60
  }
61
61
 
62
+ impl fmt::Display for S3Storage {
63
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64
+ write!(
65
+ f,
66
+ "S3Storage(bucket={}, prefix={}, config={})",
67
+ self.bucket, self.prefix, self.config,
68
+ )
69
+ }
70
+ }
71
+
62
72
  #[instrument(skip(credentials))]
63
73
  pub async fn mk_client(config: &S3Options, credentials: S3Credentials) -> Client {
64
74
  let region = config
@@ -139,10 +149,10 @@ impl S3Storage {
139
149
 
140
150
  fn get_path_str(&self, file_prefix: &str, id: &str) -> StorageResult<String> {
141
151
  let path = PathBuf::from_iter([self.prefix.as_str(), file_prefix, id]);
142
- path.into_os_string()
143
- .into_string()
144
- .map_err(StorageErrorKind::BadPrefix)
145
- .err_into()
152
+ let path_str =
153
+ path.into_os_string().into_string().map_err(StorageErrorKind::BadPrefix)?;
154
+
155
+ Ok(path_str.replace("\\", "/"))
146
156
  }
147
157
 
148
158
  fn get_path<const SIZE: usize, T: FileTypeTag>(
@@ -176,10 +186,10 @@ impl S3Storage {
176
186
 
177
187
  fn ref_key(&self, ref_key: &str) -> StorageResult<String> {
178
188
  let path = PathBuf::from_iter([self.prefix.as_str(), REF_PREFIX, ref_key]);
179
- path.into_os_string()
180
- .into_string()
181
- .map_err(StorageErrorKind::BadPrefix)
182
- .err_into()
189
+ let path_str =
190
+ path.into_os_string().into_string().map_err(StorageErrorKind::BadPrefix)?;
191
+
192
+ Ok(path_str.replace("\\", "/"))
183
193
  }
184
194
 
185
195
  async fn get_object_reader(
@@ -601,10 +611,7 @@ impl Storage for S3Storage {
601
611
  _settings: &Settings,
602
612
  prefix: &str,
603
613
  ) -> StorageResult<BoxStream<'a, StorageResult<ListInfo<String>>>> {
604
- let prefix = PathBuf::from_iter([self.prefix.as_str(), prefix])
605
- .into_os_string()
606
- .into_string()
607
- .map_err(StorageErrorKind::BadPrefix)?;
614
+ let prefix = format!("{}/{}", self.prefix, prefix).replace("//", "/");
608
615
  let stream = self
609
616
  .get_client()
610
617
  .await
@@ -787,4 +794,39 @@ mod tests {
787
794
  let deserialized: S3Storage = serde_json::from_str(&serialized).unwrap();
788
795
  assert_eq!(storage.config, deserialized.config);
789
796
  }
797
+
798
+ #[tokio::test]
799
+ async fn test_s3_paths() {
800
+ let storage = S3Storage::new(
801
+ S3Options {
802
+ region: Some("us-west-2".to_string()),
803
+ endpoint_url: None,
804
+ allow_http: true,
805
+ anonymous: false,
806
+ },
807
+ "bucket".to_string(),
808
+ Some("prefix".to_string()),
809
+ S3Credentials::FromEnv,
810
+ )
811
+ .unwrap();
812
+
813
+ let ref_path = storage.ref_key("ref_key").unwrap();
814
+ assert_eq!(ref_path, "prefix/refs/ref_key");
815
+
816
+ let snapshot_id = SnapshotId::random();
817
+ let snapshot_path = storage.get_snapshot_path(&snapshot_id).unwrap();
818
+ assert_eq!(snapshot_path, format!("prefix/snapshots/{snapshot_id}"));
819
+
820
+ let manifest_id = ManifestId::random();
821
+ let manifest_path = storage.get_manifest_path(&manifest_id).unwrap();
822
+ assert_eq!(manifest_path, format!("prefix/manifests/{manifest_id}"));
823
+
824
+ let chunk_id = ChunkId::random();
825
+ let chunk_path = storage.get_chunk_path(&chunk_id).unwrap();
826
+ assert_eq!(chunk_path, format!("prefix/chunks/{chunk_id}"));
827
+
828
+ let transaction_id = SnapshotId::random();
829
+ let transaction_path = storage.get_transaction_path(&transaction_id).unwrap();
830
+ assert_eq!(transaction_path, format!("prefix/transactions/{transaction_id}"));
831
+ }
790
832
  }
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "icechunk-python"
3
- version = "0.2.2"
3
+ version = "0.2.4"
4
4
  description = "Transactional storage engine for Zarr designed for use on cloud object storage"
5
5
  readme = "../README.md"
6
6
  repository = "https://github.com/earth-mover/icechunk"
@@ -21,7 +21,7 @@ crate-type = ["cdylib"]
21
21
  bytes = "1.9.0"
22
22
  chrono = { version = "0.4.39" }
23
23
  futures = "0.3.31"
24
- icechunk = { path = "../icechunk", version = "0.2.2", features = ["logs"] }
24
+ icechunk = { path = "../icechunk", version = "0.2.4", features = ["logs"] }
25
25
  itertools = "0.14.0"
26
26
  pyo3 = { version = "0.23", features = [
27
27
  "chrono",
@@ -0,0 +1,48 @@
1
+ # An example of using multiprocessing to write to an Icechunk dataset
2
+
3
+ import tempfile
4
+ from concurrent.futures import ProcessPoolExecutor
5
+
6
+ import xarray as xr
7
+ from icechunk import Repository, Session, local_filesystem_storage
8
+ from icechunk.distributed import merge_sessions
9
+
10
+
11
+ def write_timestamp(*, itime: int, session: Session) -> Session:
12
+ # pass a list to isel to preserve the time dimension
13
+ ds = xr.tutorial.open_dataset("rasm").isel(time=[itime])
14
+ # region="auto" tells Xarray to infer which "region" of the output arrays to write to.
15
+ ds.to_zarr(session.store, region="auto", consolidated=False)
16
+ return session
17
+
18
+
19
+ if __name__ == "__main__":
20
+ ds = xr.tutorial.open_dataset("rasm").isel(time=slice(24))
21
+ repo = Repository.create(local_filesystem_storage(tempfile.mkdtemp()))
22
+ session = repo.writable_session("main")
23
+
24
+ chunks = {1 if dim == "time" else ds.sizes[dim] for dim in ds.Tair.dims}
25
+ ds.to_zarr(
26
+ session.store, compute=False, encoding={"Tair": {"chunks": chunks}}, mode="w"
27
+ )
28
+ # this commit is optional, but may be useful in your workflow
29
+ session.commit("initialize store")
30
+
31
+ session = repo.writable_session("main")
32
+ with ProcessPoolExecutor() as executor:
33
+ # opt-in to successful pickling of a writable session
34
+ with session.allow_pickling():
35
+ # submit the writes
36
+ futures = [
37
+ executor.submit(write_timestamp, itime=i, session=session)
38
+ for i in range(ds.sizes["time"])
39
+ ]
40
+ # grab the Session objects from each individual write task
41
+ sessions = [f.result() for f in futures]
42
+
43
+ # manually merge the remote sessions in to the local session
44
+ session = merge_sessions(session, *sessions)
45
+ session.commit("finished writes")
46
+
47
+ ondisk = xr.open_zarr(repo.readonly_session("main").store, consolidated=False)
48
+ xr.testing.assert_identical(ds, ondisk)
@@ -52,6 +52,7 @@ from icechunk.credentials import (
52
52
  containers_credentials,
53
53
  gcs_credentials,
54
54
  gcs_from_env_credentials,
55
+ gcs_refreshable_credentials,
55
56
  gcs_static_credentials,
56
57
  s3_anonymous_credentials,
57
58
  s3_credentials,
@@ -128,11 +129,13 @@ __all__ = [
128
129
  "containers_credentials",
129
130
  "gcs_credentials",
130
131
  "gcs_from_env_credentials",
132
+ "gcs_refreshable_credentials",
131
133
  "gcs_static_credentials",
132
134
  "gcs_storage",
133
135
  "in_memory_storage",
134
136
  "initialize_logs",
135
137
  "local_filesystem_storage",
138
+ "print_debug_info",
136
139
  "s3_anonymous_credentials",
137
140
  "s3_credentials",
138
141
  "s3_from_env_credentials",
@@ -144,4 +147,19 @@ __all__ = [
144
147
  "tigris_storage",
145
148
  ]
146
149
 
150
+
151
+ def print_debug_info() -> None:
152
+ import platform
153
+ from importlib import import_module
154
+
155
+ print(f"platform: {platform.platform()}")
156
+ print(f"python: {platform.python_version()}")
157
+ print(f"icechunk: {__version__}")
158
+ for package in ["zarr", "numcodecs", "xarray", "virtualizarr"]:
159
+ try:
160
+ print(f"{package}: {import_module(package).__version__}")
161
+ except ModuleNotFoundError:
162
+ continue
163
+
164
+
147
165
  initialize_logs()
@@ -947,6 +947,9 @@ class PyRepository:
947
947
  ) -> PyRepository: ...
948
948
  @staticmethod
949
949
  def exists(storage: Storage) -> bool: ...
950
+ @classmethod
951
+ def from_bytes(cls, data: bytes) -> PyRepository: ...
952
+ def as_bytes(self) -> bytes: ...
950
953
  @staticmethod
951
954
  def fetch_config(storage: Storage) -> RepositoryConfig | None: ...
952
955
  def save_config(self) -> None: ...
@@ -1418,6 +1421,7 @@ class Storage:
1418
1421
  *,
1419
1422
  config: dict[str, str] | None = None,
1420
1423
  ) -> Storage: ...
1424
+ def __repr__(self) -> str: ...
1421
1425
  def default_settings(self) -> StorageSettings: ...
1422
1426
 
1423
1427
  class VersionSelection(Enum):
@@ -140,6 +140,16 @@ class Repository:
140
140
  """
141
141
  return PyRepository.exists(storage)
142
142
 
143
+ def __getstate__(self) -> object:
144
+ return {
145
+ "_repository": self._repository.as_bytes(),
146
+ }
147
+
148
+ def __setstate__(self, state: object) -> None:
149
+ if not isinstance(state, dict):
150
+ raise ValueError("Invalid repository state")
151
+ self._repository = PyRepository.from_bytes(state["_repository"])
152
+
143
153
  @staticmethod
144
154
  def fetch_config(storage: Storage) -> RepositoryConfig | None:
145
155
  """
@@ -1312,6 +1312,10 @@ impl PyStorage {
1312
1312
  })
1313
1313
  }
1314
1314
 
1315
+ pub fn __repr__(&self) -> String {
1316
+ format!("{}", self.0)
1317
+ }
1318
+
1315
1319
  pub fn default_settings(&self) -> PyStorageSettings {
1316
1320
  self.0.default_settings().into()
1317
1321
  }
@@ -1,4 +1,5 @@
1
1
  use std::{
2
+ borrow::Cow,
2
3
  collections::{BTreeMap, BTreeSet, HashMap, HashSet},
3
4
  sync::Arc,
4
5
  };
@@ -463,6 +464,29 @@ impl PyRepository {
463
464
  })
464
465
  }
465
466
 
467
+ #[classmethod]
468
+ fn from_bytes(
469
+ _cls: Bound<'_, PyType>,
470
+ py: Python<'_>,
471
+ bytes: Vec<u8>,
472
+ ) -> PyResult<Self> {
473
+ // This is a compute intensive task, we need to release the Gil
474
+ py.allow_threads(move || {
475
+ let repository = Repository::from_bytes(bytes)
476
+ .map_err(PyIcechunkStoreError::RepositoryError)?;
477
+ Ok(Self(Arc::new(repository)))
478
+ })
479
+ }
480
+
481
+ fn as_bytes(&self, py: Python<'_>) -> PyResult<Cow<[u8]>> {
482
+ // This is a compute intensive task, we need to release the Gil
483
+ py.allow_threads(move || {
484
+ let bytes =
485
+ self.0.as_bytes().map_err(PyIcechunkStoreError::RepositoryError)?;
486
+ Ok(Cow::Owned(bytes))
487
+ })
488
+ }
489
+
466
490
  #[staticmethod]
467
491
  fn fetch_config(
468
492
  py: Python<'_>,
@@ -0,0 +1,6 @@
1
+ from icechunk import print_debug_info
2
+
3
+
4
+ def test_debug_info() -> None:
5
+ # simple test that this does not cause an error.
6
+ print_debug_info()
@@ -25,6 +25,18 @@ def tmp_repo(tmpdir: Path) -> Repository:
25
25
  return repo
26
26
 
27
27
 
28
+ def test_pickle_repository(tmpdir: Path, tmp_repo: Repository) -> None:
29
+ pickled = pickle.dumps(tmp_repo)
30
+ roundtripped = pickle.loads(pickled)
31
+ assert tmp_repo.list_branches() == roundtripped.list_branches()
32
+
33
+ storage = tmp_repo.storage
34
+ assert (
35
+ repr(storage)
36
+ == f"ObjectStorage(backend=LocalFileSystemObjectStoreBackend(path={tmpdir}))"
37
+ )
38
+
39
+
28
40
  def test_pickle_read_only(tmp_repo: Repository) -> None:
29
41
  tmp_session = tmp_repo.writable_session(branch="main")
30
42
  tmp_store = tmp_session.store
@@ -19,7 +19,7 @@ license = "Apache-2.0"
19
19
  dynamic = ["version"]
20
20
  authors = [{ name = "Earthmover", email = "info@earthmover.io" }]
21
21
 
22
- dependencies = ["zarr>=3"]
22
+ dependencies = ["zarr>=3,!=3.0.3"]
23
23
 
24
24
  [project.optional-dependencies]
25
25
  test = [
@@ -52,6 +52,7 @@ from icechunk.credentials import (
52
52
  containers_credentials,
53
53
  gcs_credentials,
54
54
  gcs_from_env_credentials,
55
+ gcs_refreshable_credentials,
55
56
  gcs_static_credentials,
56
57
  s3_anonymous_credentials,
57
58
  s3_credentials,
@@ -128,11 +129,13 @@ __all__ = [
128
129
  "containers_credentials",
129
130
  "gcs_credentials",
130
131
  "gcs_from_env_credentials",
132
+ "gcs_refreshable_credentials",
131
133
  "gcs_static_credentials",
132
134
  "gcs_storage",
133
135
  "in_memory_storage",
134
136
  "initialize_logs",
135
137
  "local_filesystem_storage",
138
+ "print_debug_info",
136
139
  "s3_anonymous_credentials",
137
140
  "s3_credentials",
138
141
  "s3_from_env_credentials",
@@ -144,4 +147,19 @@ __all__ = [
144
147
  "tigris_storage",
145
148
  ]
146
149
 
150
+
151
+ def print_debug_info() -> None:
152
+ import platform
153
+ from importlib import import_module
154
+
155
+ print(f"platform: {platform.platform()}")
156
+ print(f"python: {platform.python_version()}")
157
+ print(f"icechunk: {__version__}")
158
+ for package in ["zarr", "numcodecs", "xarray", "virtualizarr"]:
159
+ try:
160
+ print(f"{package}: {import_module(package).__version__}")
161
+ except ModuleNotFoundError:
162
+ continue
163
+
164
+
147
165
  initialize_logs()