From c5533de24610aeb3e3799002adf9a28836f7178f Mon Sep 17 00:00:00 2001 From: Joseph Hamman Date: Fri, 13 Sep 2024 14:07:37 -0700 Subject: [PATCH] remove outdated v2 source code and tests from v3 branch --- src/zarr/v2/__init__.py | 54 - src/zarr/v2/_storage/__init__.py | 0 src/zarr/v2/_storage/absstore.py | 224 -- src/zarr/v2/_storage/store.py | 226 -- src/zarr/v2/attrs.py | 158 - src/zarr/v2/codecs.py | 4 - src/zarr/v2/context.py | 19 - src/zarr/v2/convenience.py | 1284 -------- src/zarr/v2/core.py | 2855 ----------------- src/zarr/v2/creation.py | 707 ---- src/zarr/v2/errors.py | 80 - src/zarr/v2/hierarchy.py | 1401 -------- src/zarr/v2/indexing.py | 1074 ------- src/zarr/v2/meta.py | 302 -- src/zarr/v2/meta_v1.py | 64 - src/zarr/v2/n5.py | 897 ------ src/zarr/v2/storage.py | 2822 ---------------- src/zarr/v2/sync.py | 48 - src/zarr/v2/util.py | 788 ----- tests/v2/__init__.py | 0 tests/v2/conftest.py | 7 - tests/v2/fixture/.zgroup | 3 - .../fixture/dimension_separator/flat/.zarray | 23 - tests/v2/fixture/dimension_separator/flat/0.0 | Bin 48 -> 0 bytes .../dimension_separator/flat_legacy/.zarray | 22 - .../dimension_separator/flat_legacy/0.0 | Bin 48 -> 0 bytes .../dimension_separator/nested/.zarray | 23 - .../v2/fixture/dimension_separator/nested/0/0 | Bin 48 -> 0 bytes .../dimension_separator/nested_legacy/.zarray | 22 - .../dimension_separator/nested_legacy/0/0 | Bin 48 -> 0 bytes .../fixture/test_format_compatibility/.zgroup | 3 - .../test_format_compatibility/array_0/.zgroup | 3 - .../array_0/compressor_0/.zarray | 14 - .../array_0/compressor_0/0 | Bin 600 -> 0 bytes .../array_0/compressor_0/1 | Bin 600 -> 0 bytes .../array_0/compressor_1/.zarray | 17 - .../array_0/compressor_1/0 | Bin 284 -> 0 bytes .../array_0/compressor_1/1 | Bin 285 -> 0 bytes .../array_0/compressor_2/.zarray | 17 - .../array_0/compressor_2/0 | Bin 525 -> 0 bytes .../array_0/compressor_2/1 | Bin 500 -> 0 bytes .../array_0/compressor_3/.zarray | 20 - .../array_0/compressor_3/0 | Bin 300 -> 0 bytes .../array_0/compressor_3/1 | Bin 303 -> 0 bytes .../array_0/compressor_4/.zarray | 20 - .../array_0/compressor_4/0 | Bin 300 -> 0 bytes .../array_0/compressor_4/1 | Bin 303 -> 0 bytes .../array_0/compressor_5/.zarray | 20 - .../array_0/compressor_5/0 | Bin 121 -> 0 bytes .../array_0/compressor_5/1 | Bin 135 -> 0 bytes .../array_0/compressor_6/.zarray | 20 - .../array_0/compressor_6/0 | Bin 616 -> 0 bytes .../array_0/compressor_6/1 | Bin 556 -> 0 bytes .../test_format_compatibility/array_1/.zgroup | 3 - .../array_1/compressor_0/.zarray | 14 - .../array_1/compressor_0/0 | Bin 1200 -> 0 bytes .../array_1/compressor_0/1 | Bin 1200 -> 0 bytes .../array_1/compressor_1/.zarray | 17 - .../array_1/compressor_1/0 | Bin 916 -> 0 bytes .../array_1/compressor_1/1 | Bin 770 -> 0 bytes .../array_1/compressor_2/.zarray | 17 - .../array_1/compressor_2/0 | Bin 714 -> 0 bytes .../array_1/compressor_2/1 | Bin 667 -> 0 bytes .../array_1/compressor_3/.zarray | 20 - .../array_1/compressor_3/0 | Bin 945 -> 0 bytes .../array_1/compressor_3/1 | Bin 798 -> 0 bytes .../array_1/compressor_4/.zarray | 20 - .../array_1/compressor_4/0 | Bin 311 -> 0 bytes .../array_1/compressor_4/1 | Bin 318 -> 0 bytes .../array_1/compressor_5/.zarray | 20 - .../array_1/compressor_5/0 | Bin 138 -> 0 bytes .../array_1/compressor_5/1 | Bin 150 -> 0 bytes .../array_1/compressor_6/.zarray | 20 - .../array_1/compressor_6/0 | Bin 1216 -> 0 bytes .../array_1/compressor_6/1 | Bin 1071 -> 0 bytes .../array_10/.zgroup | 3 - .../array_10/compressor_0/.zarray | 14 - .../array_10/compressor_0/0 | Bin 16000 -> 0 bytes .../array_10/compressor_0/1 | Bin 16000 -> 0 bytes .../array_10/compressor_1/.zarray | 17 - .../array_10/compressor_1/0 | Bin 8287 -> 0 bytes .../array_10/compressor_1/1 | Bin 6176 -> 0 bytes .../array_10/compressor_2/.zarray | 17 - .../array_10/compressor_2/0 | Bin 7521 -> 0 bytes .../array_10/compressor_2/1 | Bin 4670 -> 0 bytes .../array_10/compressor_3/.zarray | 20 - .../array_10/compressor_3/0 | Bin 8172 -> 0 bytes .../array_10/compressor_3/1 | Bin 5436 -> 0 bytes .../array_10/compressor_4/.zarray | 20 - .../array_10/compressor_4/0 | Bin 13861 -> 0 bytes .../array_10/compressor_4/1 | Bin 4740 -> 0 bytes .../array_10/compressor_5/.zarray | 20 - .../array_10/compressor_5/0 | Bin 4127 -> 0 bytes .../array_10/compressor_5/1 | Bin 2492 -> 0 bytes .../array_10/compressor_6/.zarray | 20 - .../array_10/compressor_6/0 | Bin 15987 -> 0 bytes .../array_10/compressor_6/1 | Bin 10775 -> 0 bytes .../array_11/.zgroup | 3 - .../array_11/compressor_0/.zarray | 14 - .../array_11/compressor_0/0 | Bin 6000 -> 0 bytes .../array_11/compressor_0/1 | Bin 6000 -> 0 bytes .../array_11/compressor_1/.zarray | 17 - .../array_11/compressor_1/0 | Bin 5584 -> 0 bytes .../array_11/compressor_1/1 | Bin 2758 -> 0 bytes .../array_11/compressor_2/.zarray | 17 - .../array_11/compressor_2/0 | Bin 5888 -> 0 bytes .../array_11/compressor_2/1 | Bin 3083 -> 0 bytes .../array_11/compressor_3/.zarray | 20 - .../array_11/compressor_3/0 | Bin 5609 -> 0 bytes .../array_11/compressor_3/1 | Bin 2768 -> 0 bytes .../array_11/compressor_4/.zarray | 20 - .../array_11/compressor_4/0 | Bin 5609 -> 0 bytes .../array_11/compressor_4/1 | Bin 2778 -> 0 bytes .../array_11/compressor_5/.zarray | 20 - .../array_11/compressor_5/0 | Bin 5726 -> 0 bytes .../array_11/compressor_5/1 | Bin 2934 -> 0 bytes .../array_11/compressor_6/.zarray | 20 - .../array_11/compressor_6/0 | Bin 6016 -> 0 bytes .../array_11/compressor_6/1 | Bin 2973 -> 0 bytes .../array_12/.zgroup | 3 - .../array_12/compressor_0/.zarray | 14 - .../array_12/compressor_0/0 | Bin 12000 -> 0 bytes .../array_12/compressor_0/1 | Bin 12000 -> 0 bytes .../array_12/compressor_1/.zarray | 17 - .../array_12/compressor_1/0 | Bin 11144 -> 0 bytes .../array_12/compressor_1/1 | Bin 5453 -> 0 bytes .../array_12/compressor_2/.zarray | 17 - .../array_12/compressor_2/0 | Bin 12115 -> 0 bytes .../array_12/compressor_2/1 | Bin 6120 -> 0 bytes .../array_12/compressor_3/.zarray | 20 - .../array_12/compressor_3/0 | Bin 11146 -> 0 bytes .../array_12/compressor_3/1 | Bin 5435 -> 0 bytes .../array_12/compressor_4/.zarray | 20 - .../array_12/compressor_4/0 | Bin 11138 -> 0 bytes .../array_12/compressor_4/1 | Bin 5380 -> 0 bytes .../array_12/compressor_5/.zarray | 20 - .../array_12/compressor_5/0 | Bin 10803 -> 0 bytes .../array_12/compressor_5/1 | Bin 5295 -> 0 bytes .../array_12/compressor_6/.zarray | 20 - .../array_12/compressor_6/0 | Bin 12016 -> 0 bytes .../array_12/compressor_6/1 | Bin 5890 -> 0 bytes .../array_13/.zgroup | 3 - .../array_13/compressor_0/.zarray | 14 - .../array_13/compressor_0/0 | Bin 24000 -> 0 bytes .../array_13/compressor_0/1 | Bin 24000 -> 0 bytes .../array_13/compressor_1/.zarray | 17 - .../array_13/compressor_1/0 | Bin 23139 -> 0 bytes .../array_13/compressor_1/1 | Bin 11246 -> 0 bytes .../array_13/compressor_2/.zarray | 17 - .../array_13/compressor_2/0 | Bin 23896 -> 0 bytes .../array_13/compressor_2/1 | Bin 11779 -> 0 bytes .../array_13/compressor_3/.zarray | 20 - .../array_13/compressor_3/0 | Bin 23050 -> 0 bytes .../array_13/compressor_3/1 | Bin 11156 -> 0 bytes .../array_13/compressor_4/.zarray | 20 - .../array_13/compressor_4/0 | Bin 22820 -> 0 bytes .../array_13/compressor_4/1 | Bin 10725 -> 0 bytes .../array_13/compressor_5/.zarray | 20 - .../array_13/compressor_5/0 | Bin 21824 -> 0 bytes .../array_13/compressor_5/1 | Bin 10647 -> 0 bytes .../array_13/compressor_6/.zarray | 20 - .../array_13/compressor_6/0 | Bin 24016 -> 0 bytes .../array_13/compressor_6/1 | Bin 11725 -> 0 bytes .../array_14/.zgroup | 3 - .../array_14/compressor_0/.zarray | 14 - .../array_14/compressor_0/0 | 1 - .../array_14/compressor_0/1 | Bin 3000 -> 0 bytes .../array_14/compressor_1/.zarray | 17 - .../array_14/compressor_1/0 | Bin 1058 -> 0 bytes .../array_14/compressor_1/1 | Bin 918 -> 0 bytes .../array_14/compressor_2/.zarray | 17 - .../array_14/compressor_2/0 | Bin 888 -> 0 bytes .../array_14/compressor_2/1 | Bin 797 -> 0 bytes .../array_14/compressor_3/.zarray | 20 - .../array_14/compressor_3/0 | Bin 1016 -> 0 bytes .../array_14/compressor_3/1 | Bin 895 -> 0 bytes .../array_14/compressor_4/.zarray | 20 - .../array_14/compressor_4/0 | Bin 1016 -> 0 bytes .../array_14/compressor_4/1 | Bin 895 -> 0 bytes .../array_14/compressor_5/.zarray | 20 - .../array_14/compressor_5/0 | Bin 1559 -> 0 bytes .../array_14/compressor_5/1 | Bin 1349 -> 0 bytes .../array_14/compressor_6/.zarray | 20 - .../array_14/compressor_6/0 | Bin 2311 -> 0 bytes .../array_14/compressor_6/1 | Bin 2037 -> 0 bytes .../array_15/.zgroup | 3 - .../array_15/compressor_0/.zarray | 14 - .../array_15/compressor_0/0 | Bin 48000 -> 0 bytes .../array_15/compressor_0/1 | Bin 48000 -> 0 bytes .../array_15/compressor_1/.zarray | 17 - .../array_15/compressor_1/0 | 1 - .../array_15/compressor_1/1 | Bin 3139 -> 0 bytes .../array_15/compressor_2/.zarray | 17 - .../array_15/compressor_2/0 | Bin 1028 -> 0 bytes .../array_15/compressor_2/1 | Bin 906 -> 0 bytes .../array_15/compressor_3/.zarray | 20 - .../array_15/compressor_3/0 | Bin 3116 -> 0 bytes .../array_15/compressor_3/1 | Bin 2684 -> 0 bytes .../array_15/compressor_4/.zarray | 20 - .../array_15/compressor_4/0 | Bin 3623 -> 0 bytes .../array_15/compressor_4/1 | Bin 3130 -> 0 bytes .../array_15/compressor_5/.zarray | 20 - .../array_15/compressor_5/0 | Bin 2464 -> 0 bytes .../array_15/compressor_5/1 | Bin 2152 -> 0 bytes .../array_15/compressor_6/.zarray | 20 - .../array_15/compressor_6/0 | Bin 10106 -> 0 bytes .../array_15/compressor_6/1 | Bin 8644 -> 0 bytes .../array_16/.zgroup | 3 - .../array_16/compressor_0/.zarray | 14 - .../array_16/compressor_0/0 | Bin 24000 -> 0 bytes .../array_16/compressor_0/1 | Bin 24000 -> 0 bytes .../array_16/compressor_1/.zarray | 17 - .../array_16/compressor_1/0 | 1 - .../array_16/compressor_1/1 | Bin 2498 -> 0 bytes .../array_16/compressor_2/.zarray | 17 - .../array_16/compressor_2/0 | Bin 1252 -> 0 bytes .../array_16/compressor_2/1 | Bin 1108 -> 0 bytes .../array_16/compressor_3/.zarray | 20 - .../array_16/compressor_3/0 | Bin 3029 -> 0 bytes .../array_16/compressor_3/1 | Bin 2548 -> 0 bytes .../array_16/compressor_4/.zarray | 20 - .../array_16/compressor_4/0 | Bin 5423 -> 0 bytes .../array_16/compressor_4/1 | Bin 4698 -> 0 bytes .../array_16/compressor_5/.zarray | 20 - .../array_16/compressor_5/0 | Bin 3624 -> 0 bytes .../array_16/compressor_5/1 | Bin 3139 -> 0 bytes .../array_16/compressor_6/.zarray | 20 - .../array_16/compressor_6/0 | Bin 8229 -> 0 bytes .../array_16/compressor_6/1 | Bin 7158 -> 0 bytes .../array_17/.zgroup | 3 - .../array_17/compressor_0/.zarray | 14 - .../array_17/compressor_0/0 | Bin 3000 -> 0 bytes .../array_17/compressor_0/1 | Bin 3000 -> 0 bytes .../array_17/compressor_1/.zarray | 17 - .../array_17/compressor_1/0 | Bin 709 -> 0 bytes .../array_17/compressor_1/1 | Bin 615 -> 0 bytes .../array_17/compressor_2/.zarray | 17 - .../array_17/compressor_2/0 | Bin 537 -> 0 bytes .../array_17/compressor_2/1 | Bin 473 -> 0 bytes .../array_17/compressor_3/.zarray | 20 - .../array_17/compressor_3/0 | Bin 703 -> 0 bytes .../array_17/compressor_3/1 | Bin 608 -> 0 bytes .../array_17/compressor_4/.zarray | 20 - .../array_17/compressor_4/0 | Bin 703 -> 0 bytes .../array_17/compressor_4/1 | Bin 608 -> 0 bytes .../array_17/compressor_5/.zarray | 20 - .../array_17/compressor_5/0 | Bin 419 -> 0 bytes .../array_17/compressor_5/1 | Bin 366 -> 0 bytes .../array_17/compressor_6/.zarray | 20 - .../array_17/compressor_6/0 | Bin 1870 -> 0 bytes .../array_17/compressor_6/1 | Bin 1613 -> 0 bytes .../array_18/.zgroup | 3 - .../array_18/compressor_0/.zarray | 16 - .../array_18/compressor_0/0.0 | Bin 40000 -> 0 bytes .../array_18/compressor_0/0.1 | Bin 40000 -> 0 bytes .../array_18/compressor_1/.zarray | 19 - .../array_18/compressor_1/0.0 | Bin 13951 -> 0 bytes .../array_18/compressor_1/0.1 | Bin 13936 -> 0 bytes .../array_18/compressor_2/.zarray | 19 - .../array_18/compressor_2/0.0 | Bin 9114 -> 0 bytes .../array_18/compressor_2/0.1 | Bin 8672 -> 0 bytes .../array_18/compressor_3/.zarray | 22 - .../array_18/compressor_3/0.0 | Bin 23597 -> 0 bytes .../array_18/compressor_3/0.1 | Bin 23613 -> 0 bytes .../array_18/compressor_4/.zarray | 22 - .../array_18/compressor_4/0.0 | Bin 1607 -> 0 bytes .../array_18/compressor_4/0.1 | Bin 1607 -> 0 bytes .../array_18/compressor_5/.zarray | 22 - .../array_18/compressor_5/0.0 | Bin 682 -> 0 bytes .../array_18/compressor_5/0.1 | Bin 700 -> 0 bytes .../array_18/compressor_6/.zarray | 22 - .../array_18/compressor_6/0.0 | Bin 40016 -> 0 bytes .../array_18/compressor_6/0.1 | Bin 40016 -> 0 bytes .../array_19/.zgroup | 3 - .../array_19/compressor_0/.zarray | 16 - .../array_19/compressor_0/0.0 | Bin 40000 -> 0 bytes .../array_19/compressor_0/0.1 | Bin 40000 -> 0 bytes .../array_19/compressor_1/.zarray | 19 - .../array_19/compressor_1/0.0 | Bin 13895 -> 0 bytes .../array_19/compressor_1/0.1 | Bin 13864 -> 0 bytes .../array_19/compressor_2/.zarray | 19 - .../array_19/compressor_2/0.0 | Bin 3330 -> 0 bytes .../array_19/compressor_2/0.1 | Bin 3372 -> 0 bytes .../array_19/compressor_3/.zarray | 22 - .../array_19/compressor_3/0.0 | Bin 22581 -> 0 bytes .../array_19/compressor_3/0.1 | Bin 22801 -> 0 bytes .../array_19/compressor_4/.zarray | 22 - .../array_19/compressor_4/0.0 | Bin 743 -> 0 bytes .../array_19/compressor_4/0.1 | Bin 747 -> 0 bytes .../array_19/compressor_5/.zarray | 22 - .../array_19/compressor_5/0.0 | Bin 507 -> 0 bytes .../array_19/compressor_5/0.1 | Bin 508 -> 0 bytes .../array_19/compressor_6/.zarray | 22 - .../array_19/compressor_6/0.0 | Bin 40016 -> 0 bytes .../array_19/compressor_6/0.1 | Bin 40016 -> 0 bytes .../test_format_compatibility/array_2/.zgroup | 3 - .../array_2/compressor_0/.zarray | 14 - .../array_2/compressor_0/0 | Bin 2400 -> 0 bytes .../array_2/compressor_0/1 | Bin 2400 -> 0 bytes .../array_2/compressor_1/.zarray | 17 - .../array_2/compressor_1/0 | Bin 874 -> 0 bytes .../array_2/compressor_1/1 | Bin 762 -> 0 bytes .../array_2/compressor_2/.zarray | 17 - .../array_2/compressor_2/0 | Bin 760 -> 0 bytes .../array_2/compressor_2/1 | Bin 686 -> 0 bytes .../array_2/compressor_3/.zarray | 20 - .../array_2/compressor_3/0 | Bin 1133 -> 0 bytes .../array_2/compressor_3/1 | Bin 1055 -> 0 bytes .../array_2/compressor_4/.zarray | 20 - .../array_2/compressor_4/0 | Bin 315 -> 0 bytes .../array_2/compressor_4/1 | Bin 318 -> 0 bytes .../array_2/compressor_5/.zarray | 20 - .../array_2/compressor_5/0 | Bin 138 -> 0 bytes .../array_2/compressor_5/1 | Bin 151 -> 0 bytes .../array_2/compressor_6/.zarray | 20 - .../array_2/compressor_6/0 | Bin 2416 -> 0 bytes .../array_2/compressor_6/1 | Bin 2099 -> 0 bytes .../array_20/.zgroup | 3 - .../array_20/compressor_0/.zarray | 18 - .../array_20/compressor_0/0.0.0 | Bin 40000 -> 0 bytes .../array_20/compressor_0/0.0.1 | Bin 40000 -> 0 bytes .../array_20/compressor_1/.zarray | 21 - .../array_20/compressor_1/0.0.0 | Bin 13951 -> 0 bytes .../array_20/compressor_1/0.0.1 | Bin 13936 -> 0 bytes .../array_20/compressor_2/.zarray | 21 - .../array_20/compressor_2/0.0.0 | Bin 9114 -> 0 bytes .../array_20/compressor_2/0.0.1 | Bin 8672 -> 0 bytes .../array_20/compressor_3/.zarray | 24 - .../array_20/compressor_3/0.0.0 | Bin 23597 -> 0 bytes .../array_20/compressor_3/0.0.1 | Bin 23613 -> 0 bytes .../array_20/compressor_4/.zarray | 24 - .../array_20/compressor_4/0.0.0 | Bin 1607 -> 0 bytes .../array_20/compressor_4/0.0.1 | Bin 1607 -> 0 bytes .../array_20/compressor_5/.zarray | 24 - .../array_20/compressor_5/0.0.0 | Bin 682 -> 0 bytes .../array_20/compressor_5/0.0.1 | Bin 700 -> 0 bytes .../array_20/compressor_6/.zarray | 24 - .../array_20/compressor_6/0.0.0 | Bin 40016 -> 0 bytes .../array_20/compressor_6/0.0.1 | Bin 40016 -> 0 bytes .../array_21/.zgroup | 3 - .../array_21/compressor_0/.zarray | 18 - .../array_21/compressor_0/0.0.0 | Bin 80000 -> 0 bytes .../array_21/compressor_1/.zarray | 21 - .../array_21/compressor_1/0.0.0 | Bin 27715 -> 0 bytes .../array_21/compressor_2/.zarray | 21 - .../array_21/compressor_2/0.0.0 | Bin 6038 -> 0 bytes .../array_21/compressor_3/.zarray | 24 - .../array_21/compressor_3/0.0.0 | Bin 45710 -> 0 bytes .../array_21/compressor_4/.zarray | 24 - .../array_21/compressor_4/0.0.0 | Bin 1180 -> 0 bytes .../array_21/compressor_5/.zarray | 24 - .../array_21/compressor_5/0.0.0 | Bin 834 -> 0 bytes .../array_21/compressor_6/.zarray | 24 - .../array_21/compressor_6/0.0.0 | Bin 80016 -> 0 bytes .../array_22/.zgroup | 3 - .../array_22/compressor_0/.zarray | 20 - .../array_22/compressor_0/0.0.0.0 | Bin 40000 -> 0 bytes .../array_22/compressor_0/0.0.0.1 | Bin 40000 -> 0 bytes .../array_22/compressor_1/.zarray | 23 - .../array_22/compressor_1/0.0.0.0 | Bin 13951 -> 0 bytes .../array_22/compressor_1/0.0.0.1 | Bin 13936 -> 0 bytes .../array_22/compressor_2/.zarray | 23 - .../array_22/compressor_2/0.0.0.0 | Bin 9114 -> 0 bytes .../array_22/compressor_2/0.0.0.1 | Bin 8672 -> 0 bytes .../array_22/compressor_3/.zarray | 26 - .../array_22/compressor_3/0.0.0.0 | Bin 23597 -> 0 bytes .../array_22/compressor_3/0.0.0.1 | Bin 23613 -> 0 bytes .../array_22/compressor_4/.zarray | 26 - .../array_22/compressor_4/0.0.0.0 | Bin 1607 -> 0 bytes .../array_22/compressor_4/0.0.0.1 | Bin 1607 -> 0 bytes .../array_22/compressor_5/.zarray | 26 - .../array_22/compressor_5/0.0.0.0 | Bin 682 -> 0 bytes .../array_22/compressor_5/0.0.0.1 | Bin 700 -> 0 bytes .../array_22/compressor_6/.zarray | 26 - .../array_22/compressor_6/0.0.0.0 | Bin 40016 -> 0 bytes .../array_22/compressor_6/0.0.0.1 | Bin 40016 -> 0 bytes .../array_23/.zgroup | 3 - .../array_23/compressor_0/.zarray | 20 - .../array_23/compressor_0/0.0.0.0 | Bin 40000 -> 0 bytes .../array_23/compressor_0/0.0.0.1 | Bin 40000 -> 0 bytes .../array_23/compressor_1/.zarray | 23 - .../array_23/compressor_1/0.0.0.0 | Bin 13895 -> 0 bytes .../array_23/compressor_1/0.0.0.1 | Bin 13864 -> 0 bytes .../array_23/compressor_2/.zarray | 23 - .../array_23/compressor_2/0.0.0.0 | Bin 3330 -> 0 bytes .../array_23/compressor_2/0.0.0.1 | Bin 3372 -> 0 bytes .../array_23/compressor_3/.zarray | 26 - .../array_23/compressor_3/0.0.0.0 | Bin 22581 -> 0 bytes .../array_23/compressor_3/0.0.0.1 | Bin 22801 -> 0 bytes .../array_23/compressor_4/.zarray | 26 - .../array_23/compressor_4/0.0.0.0 | Bin 743 -> 0 bytes .../array_23/compressor_4/0.0.0.1 | Bin 747 -> 0 bytes .../array_23/compressor_5/.zarray | 26 - .../array_23/compressor_5/0.0.0.0 | Bin 507 -> 0 bytes .../array_23/compressor_5/0.0.0.1 | Bin 508 -> 0 bytes .../array_23/compressor_6/.zarray | 26 - .../array_23/compressor_6/0.0.0.0 | Bin 40016 -> 0 bytes .../array_23/compressor_6/0.0.0.1 | Bin 40016 -> 0 bytes .../test_format_compatibility/array_3/.zgroup | 3 - .../array_3/compressor_0/.zarray | 14 - .../array_3/compressor_0/0 | Bin 8000 -> 0 bytes .../array_3/compressor_0/1 | Bin 8000 -> 0 bytes .../array_3/compressor_1/.zarray | 17 - .../array_3/compressor_1/0 | Bin 1548 -> 0 bytes .../array_3/compressor_1/1 | Bin 252 -> 0 bytes .../array_3/compressor_2/.zarray | 17 - .../array_3/compressor_2/0 | Bin 1000 -> 0 bytes .../array_3/compressor_2/1 | Bin 220 -> 0 bytes .../array_3/compressor_3/.zarray | 20 - .../array_3/compressor_3/0 | Bin 1441 -> 0 bytes .../array_3/compressor_3/1 | Bin 178 -> 0 bytes .../array_3/compressor_4/.zarray | 20 - .../array_3/compressor_4/0 | Bin 320 -> 0 bytes .../array_3/compressor_4/1 | Bin 164 -> 0 bytes .../array_3/compressor_5/.zarray | 20 - .../array_3/compressor_5/0 | Bin 151 -> 0 bytes .../array_3/compressor_5/1 | Bin 117 -> 0 bytes .../array_3/compressor_6/.zarray | 20 - .../array_3/compressor_6/0 | Bin 4131 -> 0 bytes .../array_3/compressor_6/1 | Bin 612 -> 0 bytes .../test_format_compatibility/array_4/.zgroup | 3 - .../array_4/compressor_0/.zarray | 14 - .../array_4/compressor_0/0 | Bin 1200 -> 0 bytes .../array_4/compressor_0/1 | Bin 1200 -> 0 bytes .../array_4/compressor_1/.zarray | 17 - .../array_4/compressor_1/0 | Bin 1196 -> 0 bytes .../array_4/compressor_1/1 | Bin 1033 -> 0 bytes .../array_4/compressor_2/.zarray | 17 - .../array_4/compressor_2/0 | Bin 1417 -> 0 bytes .../array_4/compressor_2/1 | Bin 1195 -> 0 bytes .../array_4/compressor_3/.zarray | 20 - .../array_4/compressor_3/0 | Bin 1216 -> 0 bytes .../array_4/compressor_3/1 | Bin 1066 -> 0 bytes .../array_4/compressor_4/.zarray | 20 - .../array_4/compressor_4/0 | Bin 1216 -> 0 bytes .../array_4/compressor_4/1 | Bin 1066 -> 0 bytes .../array_4/compressor_5/.zarray | 20 - .../array_4/compressor_5/0 | Bin 1216 -> 0 bytes .../array_4/compressor_5/1 | Bin 1081 -> 0 bytes .../array_4/compressor_6/.zarray | 20 - .../array_4/compressor_6/0 | Bin 1216 -> 0 bytes .../array_4/compressor_6/1 | Bin 1070 -> 0 bytes .../test_format_compatibility/array_5/.zgroup | 3 - .../array_5/compressor_0/.zarray | 14 - .../array_5/compressor_0/0 | Bin 2400 -> 0 bytes .../array_5/compressor_0/1 | Bin 2400 -> 0 bytes .../array_5/compressor_1/.zarray | 17 - .../array_5/compressor_1/0 | Bin 1998 -> 0 bytes .../array_5/compressor_1/1 | Bin 1724 -> 0 bytes .../array_5/compressor_2/.zarray | 17 - .../array_5/compressor_2/0 | Bin 2030 -> 0 bytes .../array_5/compressor_2/1 | Bin 1809 -> 0 bytes .../array_5/compressor_3/.zarray | 20 - .../array_5/compressor_3/0 | Bin 2010 -> 0 bytes .../array_5/compressor_3/1 | Bin 1740 -> 0 bytes .../array_5/compressor_4/.zarray | 20 - .../array_5/compressor_4/0 | Bin 2010 -> 0 bytes .../array_5/compressor_4/1 | Bin 1743 -> 0 bytes .../array_5/compressor_5/.zarray | 20 - .../array_5/compressor_5/0 | Bin 1700 -> 0 bytes .../array_5/compressor_5/1 | Bin 1478 -> 0 bytes .../array_5/compressor_6/.zarray | 20 - .../array_5/compressor_6/0 | Bin 2416 -> 0 bytes .../array_5/compressor_6/1 | Bin 2097 -> 0 bytes .../test_format_compatibility/array_6/.zgroup | 3 - .../array_6/compressor_0/.zarray | 14 - .../array_6/compressor_0/0 | Bin 4800 -> 0 bytes .../array_6/compressor_0/1 | Bin 4800 -> 0 bytes .../array_6/compressor_1/.zarray | 17 - .../array_6/compressor_1/0 | Bin 2588 -> 0 bytes .../array_6/compressor_1/1 | Bin 2231 -> 0 bytes .../array_6/compressor_2/.zarray | 17 - .../array_6/compressor_2/0 | Bin 2094 -> 0 bytes .../array_6/compressor_2/1 | Bin 1824 -> 0 bytes .../array_6/compressor_3/.zarray | 20 - .../array_6/compressor_3/0 | Bin 2471 -> 0 bytes .../array_6/compressor_3/1 | Bin 2139 -> 0 bytes .../array_6/compressor_4/.zarray | 20 - .../array_6/compressor_4/0 | Bin 2014 -> 0 bytes .../array_6/compressor_4/1 | Bin 1742 -> 0 bytes .../array_6/compressor_5/.zarray | 20 - .../array_6/compressor_5/0 | Bin 1700 -> 0 bytes .../array_6/compressor_5/1 | Bin 1479 -> 0 bytes .../array_6/compressor_6/.zarray | 20 - .../array_6/compressor_6/0 | Bin 4816 -> 0 bytes .../array_6/compressor_6/1 | Bin 4145 -> 0 bytes .../test_format_compatibility/array_7/.zgroup | 3 - .../array_7/compressor_0/.zarray | 14 - .../array_7/compressor_0/0 | Bin 9600 -> 0 bytes .../array_7/compressor_0/1 | Bin 9600 -> 0 bytes .../array_7/compressor_1/.zarray | 17 - .../array_7/compressor_1/0 | 7 - .../array_7/compressor_1/1 | Bin 2422 -> 0 bytes .../array_7/compressor_2/.zarray | 17 - .../array_7/compressor_2/0 | Bin 2267 -> 0 bytes .../array_7/compressor_2/1 | Bin 1955 -> 0 bytes .../array_7/compressor_3/.zarray | 20 - .../array_7/compressor_3/0 | Bin 2144 -> 0 bytes .../array_7/compressor_3/1 | Bin 1833 -> 0 bytes .../array_7/compressor_4/.zarray | 20 - .../array_7/compressor_4/0 | Bin 2021 -> 0 bytes .../array_7/compressor_4/1 | Bin 1745 -> 0 bytes .../array_7/compressor_5/.zarray | 20 - .../array_7/compressor_5/0 | Bin 1700 -> 0 bytes .../array_7/compressor_5/1 | Bin 1479 -> 0 bytes .../array_7/compressor_6/.zarray | 20 - .../array_7/compressor_6/0 | Bin 5006 -> 0 bytes .../array_7/compressor_6/1 | Bin 4276 -> 0 bytes .../test_format_compatibility/array_8/.zgroup | 3 - .../array_8/compressor_0/.zarray | 14 - .../array_8/compressor_0/0 | Bin 4000 -> 0 bytes .../array_8/compressor_0/1 | Bin 4000 -> 0 bytes .../array_8/compressor_1/.zarray | 17 - .../array_8/compressor_1/0 | Bin 3291 -> 0 bytes .../array_8/compressor_1/1 | Bin 1336 -> 0 bytes .../array_8/compressor_2/.zarray | 17 - .../array_8/compressor_2/0 | Bin 2993 -> 0 bytes .../array_8/compressor_2/1 | Bin 1128 -> 0 bytes .../array_8/compressor_3/.zarray | 20 - .../array_8/compressor_3/0 | Bin 3477 -> 0 bytes .../array_8/compressor_3/1 | Bin 2055 -> 0 bytes .../array_8/compressor_4/.zarray | 20 - .../array_8/compressor_4/0 | Bin 1915 -> 0 bytes .../array_8/compressor_4/1 | Bin 900 -> 0 bytes .../array_8/compressor_5/.zarray | 20 - .../array_8/compressor_5/0 | Bin 735 -> 0 bytes .../array_8/compressor_5/1 | Bin 404 -> 0 bytes .../array_8/compressor_6/.zarray | 20 - .../array_8/compressor_6/0 | Bin 4016 -> 0 bytes .../array_8/compressor_6/1 | Bin 2722 -> 0 bytes .../test_format_compatibility/array_9/.zgroup | 3 - .../array_9/compressor_0/.zarray | 14 - .../array_9/compressor_0/0 | Bin 8000 -> 0 bytes .../array_9/compressor_0/1 | Bin 8000 -> 0 bytes .../array_9/compressor_1/.zarray | 17 - .../array_9/compressor_1/0 | Bin 6736 -> 0 bytes .../array_9/compressor_1/1 | Bin 4349 -> 0 bytes .../array_9/compressor_2/.zarray | 17 - .../array_9/compressor_2/0 | Bin 5805 -> 0 bytes .../array_9/compressor_2/1 | Bin 3480 -> 0 bytes .../array_9/compressor_3/.zarray | 20 - .../array_9/compressor_3/0 | Bin 7248 -> 0 bytes .../array_9/compressor_3/1 | Bin 4581 -> 0 bytes .../array_9/compressor_4/.zarray | 20 - .../array_9/compressor_4/0 | Bin 5900 -> 0 bytes .../array_9/compressor_4/1 | Bin 1810 -> 0 bytes .../array_9/compressor_5/.zarray | 20 - .../array_9/compressor_5/0 | Bin 1813 -> 0 bytes .../array_9/compressor_5/1 | Bin 1062 -> 0 bytes .../array_9/compressor_6/.zarray | 20 - .../array_9/compressor_6/0 | Bin 8016 -> 0 bytes .../array_9/compressor_6/1 | Bin 5415 -> 0 bytes tests/v2/test_attrs.py | 265 -- tests/v2/test_convenience.py | 804 ----- tests/v2/test_core.py | 2510 --------------- tests/v2/test_creation.py | 691 ---- tests/v2/test_dim_separator.py | 136 - tests/v2/test_filters.py | 209 -- tests/v2/test_hierarchy.py | 1452 --------- tests/v2/test_indexing.py | 1758 ---------- tests/v2/test_info.py | 66 - tests/v2/test_meta.py | 527 --- tests/v2/test_meta_array.py | 251 -- tests/v2/test_n5.py | 53 - tests/v2/test_storage.py | 2505 --------------- tests/v2/test_storage_v3.py | 692 ---- tests/v2/test_sync.py | 319 -- tests/v2/test_util.py | 292 -- tests/v2/util.py | 91 - tests/v3/test_codecs/test_codecs.py | 101 - tests/v3/test_codecs/test_endian.py | 29 - tests/v3/test_codecs/test_transpose.py | 17 - 572 files changed, 29200 deletions(-) delete mode 100644 src/zarr/v2/__init__.py delete mode 100644 src/zarr/v2/_storage/__init__.py delete mode 100644 src/zarr/v2/_storage/absstore.py delete mode 100644 src/zarr/v2/_storage/store.py delete mode 100644 src/zarr/v2/attrs.py delete mode 100644 src/zarr/v2/codecs.py delete mode 100644 src/zarr/v2/context.py delete mode 100644 src/zarr/v2/convenience.py delete mode 100644 src/zarr/v2/core.py delete mode 100644 src/zarr/v2/creation.py delete mode 100644 src/zarr/v2/errors.py delete mode 100644 src/zarr/v2/hierarchy.py delete mode 100644 src/zarr/v2/indexing.py delete mode 100644 src/zarr/v2/meta.py delete mode 100644 src/zarr/v2/meta_v1.py delete mode 100644 src/zarr/v2/n5.py delete mode 100644 src/zarr/v2/storage.py delete mode 100644 src/zarr/v2/sync.py delete mode 100644 src/zarr/v2/util.py delete mode 100644 tests/v2/__init__.py delete mode 100644 tests/v2/conftest.py delete mode 100644 tests/v2/fixture/.zgroup delete mode 100644 tests/v2/fixture/dimension_separator/flat/.zarray delete mode 100644 tests/v2/fixture/dimension_separator/flat/0.0 delete mode 100644 tests/v2/fixture/dimension_separator/flat_legacy/.zarray delete mode 100644 tests/v2/fixture/dimension_separator/flat_legacy/0.0 delete mode 100644 tests/v2/fixture/dimension_separator/nested/.zarray delete mode 100644 tests/v2/fixture/dimension_separator/nested/0/0 delete mode 100644 tests/v2/fixture/dimension_separator/nested_legacy/.zarray delete mode 100644 tests/v2/fixture/dimension_separator/nested_legacy/0/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_0/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_0/compressor_0/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_0/compressor_0/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_0/compressor_0/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_0/compressor_1/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_0/compressor_1/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_0/compressor_1/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_0/compressor_2/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_0/compressor_2/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_0/compressor_2/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_0/compressor_3/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_0/compressor_3/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_0/compressor_3/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_0/compressor_4/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_0/compressor_4/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_0/compressor_4/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_0/compressor_5/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_0/compressor_5/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_0/compressor_5/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_0/compressor_6/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_0/compressor_6/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_0/compressor_6/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_1/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_1/compressor_0/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_1/compressor_0/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_1/compressor_0/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_1/compressor_1/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_1/compressor_1/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_1/compressor_1/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_1/compressor_2/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_1/compressor_2/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_1/compressor_2/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_1/compressor_3/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_1/compressor_3/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_1/compressor_3/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_1/compressor_4/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_1/compressor_4/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_1/compressor_4/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_1/compressor_5/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_1/compressor_5/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_1/compressor_5/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_1/compressor_6/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_1/compressor_6/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_1/compressor_6/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_10/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_10/compressor_0/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_10/compressor_0/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_10/compressor_0/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_10/compressor_1/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_10/compressor_1/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_10/compressor_1/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_10/compressor_2/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_10/compressor_2/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_10/compressor_2/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_10/compressor_3/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_10/compressor_3/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_10/compressor_3/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_10/compressor_4/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_10/compressor_4/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_10/compressor_4/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_10/compressor_5/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_10/compressor_5/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_10/compressor_5/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_10/compressor_6/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_10/compressor_6/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_10/compressor_6/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_11/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_11/compressor_0/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_11/compressor_0/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_11/compressor_0/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_11/compressor_1/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_11/compressor_1/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_11/compressor_1/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_11/compressor_2/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_11/compressor_2/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_11/compressor_2/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_11/compressor_3/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_11/compressor_3/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_11/compressor_3/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_11/compressor_4/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_11/compressor_4/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_11/compressor_4/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_11/compressor_5/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_11/compressor_5/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_11/compressor_5/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_11/compressor_6/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_11/compressor_6/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_11/compressor_6/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_12/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_12/compressor_0/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_12/compressor_0/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_12/compressor_0/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_12/compressor_1/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_12/compressor_1/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_12/compressor_1/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_12/compressor_2/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_12/compressor_2/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_12/compressor_2/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_12/compressor_3/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_12/compressor_3/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_12/compressor_3/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_12/compressor_4/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_12/compressor_4/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_12/compressor_4/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_12/compressor_5/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_12/compressor_5/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_12/compressor_5/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_12/compressor_6/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_12/compressor_6/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_12/compressor_6/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_13/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_13/compressor_0/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_13/compressor_0/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_13/compressor_0/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_13/compressor_1/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_13/compressor_1/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_13/compressor_1/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_13/compressor_2/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_13/compressor_2/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_13/compressor_2/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_13/compressor_3/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_13/compressor_3/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_13/compressor_3/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_13/compressor_4/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_13/compressor_4/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_13/compressor_4/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_13/compressor_5/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_13/compressor_5/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_13/compressor_5/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_13/compressor_6/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_13/compressor_6/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_13/compressor_6/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_14/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_14/compressor_0/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_14/compressor_0/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_14/compressor_0/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_14/compressor_1/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_14/compressor_1/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_14/compressor_1/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_14/compressor_2/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_14/compressor_2/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_14/compressor_2/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_14/compressor_3/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_14/compressor_3/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_14/compressor_3/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_14/compressor_4/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_14/compressor_4/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_14/compressor_4/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_14/compressor_5/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_14/compressor_5/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_14/compressor_5/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_14/compressor_6/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_14/compressor_6/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_14/compressor_6/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_15/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_15/compressor_0/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_15/compressor_0/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_15/compressor_0/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_15/compressor_1/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_15/compressor_1/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_15/compressor_1/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_15/compressor_2/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_15/compressor_2/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_15/compressor_2/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_15/compressor_3/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_15/compressor_3/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_15/compressor_3/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_15/compressor_4/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_15/compressor_4/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_15/compressor_4/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_15/compressor_5/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_15/compressor_5/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_15/compressor_5/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_15/compressor_6/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_15/compressor_6/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_15/compressor_6/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_16/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_16/compressor_0/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_16/compressor_0/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_16/compressor_0/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_16/compressor_1/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_16/compressor_1/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_16/compressor_1/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_16/compressor_2/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_16/compressor_2/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_16/compressor_2/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_16/compressor_3/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_16/compressor_3/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_16/compressor_3/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_16/compressor_4/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_16/compressor_4/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_16/compressor_4/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_16/compressor_5/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_16/compressor_5/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_16/compressor_5/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_16/compressor_6/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_16/compressor_6/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_16/compressor_6/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_17/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_17/compressor_0/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_17/compressor_0/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_17/compressor_0/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_17/compressor_1/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_17/compressor_1/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_17/compressor_1/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_17/compressor_2/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_17/compressor_2/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_17/compressor_2/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_17/compressor_3/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_17/compressor_3/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_17/compressor_3/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_17/compressor_4/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_17/compressor_4/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_17/compressor_4/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_17/compressor_5/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_17/compressor_5/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_17/compressor_5/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_17/compressor_6/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_17/compressor_6/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_17/compressor_6/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_18/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_18/compressor_0/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_18/compressor_0/0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_18/compressor_0/0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_18/compressor_1/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_18/compressor_1/0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_18/compressor_1/0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_18/compressor_2/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_18/compressor_2/0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_18/compressor_2/0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_18/compressor_3/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_18/compressor_3/0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_18/compressor_3/0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_18/compressor_4/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_18/compressor_4/0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_18/compressor_4/0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_18/compressor_5/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_18/compressor_5/0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_18/compressor_5/0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_18/compressor_6/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_18/compressor_6/0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_18/compressor_6/0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_19/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_19/compressor_0/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_19/compressor_0/0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_19/compressor_0/0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_19/compressor_1/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_19/compressor_1/0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_19/compressor_1/0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_19/compressor_2/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_19/compressor_2/0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_19/compressor_2/0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_19/compressor_3/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_19/compressor_3/0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_19/compressor_3/0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_19/compressor_4/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_19/compressor_4/0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_19/compressor_4/0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_19/compressor_5/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_19/compressor_5/0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_19/compressor_5/0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_19/compressor_6/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_19/compressor_6/0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_19/compressor_6/0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_2/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_2/compressor_0/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_2/compressor_0/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_2/compressor_0/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_2/compressor_1/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_2/compressor_1/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_2/compressor_1/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_2/compressor_2/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_2/compressor_2/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_2/compressor_2/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_2/compressor_3/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_2/compressor_3/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_2/compressor_3/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_2/compressor_4/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_2/compressor_4/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_2/compressor_4/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_2/compressor_5/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_2/compressor_5/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_2/compressor_5/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_2/compressor_6/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_2/compressor_6/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_2/compressor_6/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_20/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_20/compressor_0/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_20/compressor_0/0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_20/compressor_0/0.0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_20/compressor_1/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_20/compressor_1/0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_20/compressor_1/0.0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_20/compressor_2/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_20/compressor_2/0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_20/compressor_2/0.0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_20/compressor_3/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_20/compressor_3/0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_20/compressor_3/0.0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_20/compressor_4/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_20/compressor_4/0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_20/compressor_4/0.0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_20/compressor_5/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_20/compressor_5/0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_20/compressor_5/0.0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_20/compressor_6/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_20/compressor_6/0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_20/compressor_6/0.0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_21/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_21/compressor_0/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_21/compressor_0/0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_21/compressor_1/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_21/compressor_1/0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_21/compressor_2/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_21/compressor_2/0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_21/compressor_3/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_21/compressor_3/0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_21/compressor_4/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_21/compressor_4/0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_21/compressor_5/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_21/compressor_5/0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_21/compressor_6/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_21/compressor_6/0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_22/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_22/compressor_0/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_22/compressor_0/0.0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_22/compressor_0/0.0.0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_22/compressor_1/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_22/compressor_1/0.0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_22/compressor_1/0.0.0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_22/compressor_2/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_22/compressor_2/0.0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_22/compressor_2/0.0.0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_22/compressor_3/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_22/compressor_3/0.0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_22/compressor_3/0.0.0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_22/compressor_4/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_22/compressor_4/0.0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_22/compressor_4/0.0.0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_22/compressor_5/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_22/compressor_5/0.0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_22/compressor_5/0.0.0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_22/compressor_6/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_22/compressor_6/0.0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_22/compressor_6/0.0.0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_23/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_23/compressor_0/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_23/compressor_0/0.0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_23/compressor_0/0.0.0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_23/compressor_1/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_23/compressor_1/0.0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_23/compressor_1/0.0.0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_23/compressor_2/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_23/compressor_2/0.0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_23/compressor_2/0.0.0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_23/compressor_3/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_23/compressor_3/0.0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_23/compressor_3/0.0.0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_23/compressor_4/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_23/compressor_4/0.0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_23/compressor_4/0.0.0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_23/compressor_5/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_23/compressor_5/0.0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_23/compressor_5/0.0.0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_23/compressor_6/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_23/compressor_6/0.0.0.0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_23/compressor_6/0.0.0.1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_3/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_3/compressor_0/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_3/compressor_0/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_3/compressor_0/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_3/compressor_1/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_3/compressor_1/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_3/compressor_1/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_3/compressor_2/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_3/compressor_2/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_3/compressor_2/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_3/compressor_3/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_3/compressor_3/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_3/compressor_3/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_3/compressor_4/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_3/compressor_4/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_3/compressor_4/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_3/compressor_5/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_3/compressor_5/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_3/compressor_5/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_3/compressor_6/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_3/compressor_6/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_3/compressor_6/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_4/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_4/compressor_0/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_4/compressor_0/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_4/compressor_0/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_4/compressor_1/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_4/compressor_1/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_4/compressor_1/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_4/compressor_2/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_4/compressor_2/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_4/compressor_2/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_4/compressor_3/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_4/compressor_3/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_4/compressor_3/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_4/compressor_4/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_4/compressor_4/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_4/compressor_4/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_4/compressor_5/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_4/compressor_5/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_4/compressor_5/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_4/compressor_6/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_4/compressor_6/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_4/compressor_6/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_5/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_5/compressor_0/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_5/compressor_0/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_5/compressor_0/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_5/compressor_1/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_5/compressor_1/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_5/compressor_1/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_5/compressor_2/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_5/compressor_2/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_5/compressor_2/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_5/compressor_3/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_5/compressor_3/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_5/compressor_3/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_5/compressor_4/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_5/compressor_4/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_5/compressor_4/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_5/compressor_5/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_5/compressor_5/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_5/compressor_5/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_5/compressor_6/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_5/compressor_6/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_5/compressor_6/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_6/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_6/compressor_0/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_6/compressor_0/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_6/compressor_0/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_6/compressor_1/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_6/compressor_1/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_6/compressor_1/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_6/compressor_2/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_6/compressor_2/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_6/compressor_2/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_6/compressor_3/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_6/compressor_3/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_6/compressor_3/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_6/compressor_4/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_6/compressor_4/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_6/compressor_4/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_6/compressor_5/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_6/compressor_5/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_6/compressor_5/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_6/compressor_6/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_6/compressor_6/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_6/compressor_6/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_7/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_7/compressor_0/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_7/compressor_0/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_7/compressor_0/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_7/compressor_1/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_7/compressor_1/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_7/compressor_1/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_7/compressor_2/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_7/compressor_2/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_7/compressor_2/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_7/compressor_3/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_7/compressor_3/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_7/compressor_3/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_7/compressor_4/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_7/compressor_4/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_7/compressor_4/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_7/compressor_5/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_7/compressor_5/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_7/compressor_5/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_7/compressor_6/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_7/compressor_6/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_7/compressor_6/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_8/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_8/compressor_0/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_8/compressor_0/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_8/compressor_0/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_8/compressor_1/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_8/compressor_1/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_8/compressor_1/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_8/compressor_2/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_8/compressor_2/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_8/compressor_2/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_8/compressor_3/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_8/compressor_3/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_8/compressor_3/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_8/compressor_4/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_8/compressor_4/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_8/compressor_4/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_8/compressor_5/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_8/compressor_5/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_8/compressor_5/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_8/compressor_6/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_8/compressor_6/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_8/compressor_6/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_9/.zgroup delete mode 100644 tests/v2/fixture/test_format_compatibility/array_9/compressor_0/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_9/compressor_0/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_9/compressor_0/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_9/compressor_1/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_9/compressor_1/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_9/compressor_1/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_9/compressor_2/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_9/compressor_2/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_9/compressor_2/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_9/compressor_3/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_9/compressor_3/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_9/compressor_3/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_9/compressor_4/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_9/compressor_4/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_9/compressor_4/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_9/compressor_5/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_9/compressor_5/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_9/compressor_5/1 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_9/compressor_6/.zarray delete mode 100644 tests/v2/fixture/test_format_compatibility/array_9/compressor_6/0 delete mode 100644 tests/v2/fixture/test_format_compatibility/array_9/compressor_6/1 delete mode 100644 tests/v2/test_attrs.py delete mode 100644 tests/v2/test_convenience.py delete mode 100644 tests/v2/test_core.py delete mode 100644 tests/v2/test_creation.py delete mode 100644 tests/v2/test_dim_separator.py delete mode 100644 tests/v2/test_filters.py delete mode 100644 tests/v2/test_hierarchy.py delete mode 100644 tests/v2/test_indexing.py delete mode 100644 tests/v2/test_info.py delete mode 100644 tests/v2/test_meta.py delete mode 100644 tests/v2/test_meta_array.py delete mode 100644 tests/v2/test_n5.py delete mode 100644 tests/v2/test_storage.py delete mode 100644 tests/v2/test_storage_v3.py delete mode 100644 tests/v2/test_sync.py delete mode 100644 tests/v2/test_util.py delete mode 100644 tests/v2/util.py diff --git a/src/zarr/v2/__init__.py b/src/zarr/v2/__init__.py deleted file mode 100644 index 27c7595580..0000000000 --- a/src/zarr/v2/__init__.py +++ /dev/null @@ -1,54 +0,0 @@ -# flake8: noqa -from zarr.v2.codecs import * -from zarr.v2.convenience import ( - consolidate_metadata, - copy, - copy_all, - copy_store, - load, - open, - open_consolidated, - save, - save_array, - save_group, - tree, -) -from zarr.v2.core import Array -from zarr.v2.creation import ( - array, - create, - empty, - empty_like, - full, - full_like, - ones, - ones_like, - open_array, - open_like, - zeros, - zeros_like, -) -from zarr.v2.errors import CopyError, MetadataError -from zarr.v2.hierarchy import Group, group, open_group -from zarr.v2.n5 import N5Store, N5FSStore -from zarr.v2.storage import ( - ABSStore, - DBMStore, - DictStore, - DirectoryStore, - KVStore, - LMDBStore, - LRUStoreCache, - MemoryStore, - MongoDBStore, - NestedDirectoryStore, - RedisStore, - SQLiteStore, - TempStore, - ZipStore, -) -from zarr.v2.sync import ProcessSynchronizer, ThreadSynchronizer -from zarr._version import version as __version__ - -# in case setuptools scm screw up and find version to be 0.0.0 -assert not __version__.startswith("0.0.0") diff --git a/src/zarr/v2/_storage/__init__.py b/src/zarr/v2/_storage/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/zarr/v2/_storage/absstore.py b/src/zarr/v2/_storage/absstore.py deleted file mode 100644 index c04ad240da..0000000000 --- a/src/zarr/v2/_storage/absstore.py +++ /dev/null @@ -1,224 +0,0 @@ -"""This module contains storage classes related to Azure Blob Storage (ABS)""" - -import warnings -from numcodecs.compat import ensure_bytes -from zarr.v2.util import normalize_storage_path -from zarr.v2._storage.store import Store - -__doctest_requires__ = { - ("ABSStore", "ABSStore.*"): ["azure.storage.blob"], -} - - -class ABSStore(Store): - """Storage class using Azure Blob Storage (ABS). - - Parameters - ---------- - container : string - The name of the ABS container to use. - - .. deprecated:: - Use ``client`` instead. - - prefix : string - Location of the "directory" to use as the root of the storage hierarchy - within the container. - - account_name : string - The Azure blob storage account name. - - .. deprecated:: 2.8.3 - Use ``client`` instead. - - account_key : string - The Azure blob storage account access key. - - .. deprecated:: 2.8.3 - Use ``client`` instead. - - blob_service_kwargs : dictionary - Extra arguments to be passed into the azure blob client, for e.g. when - using the emulator, pass in blob_service_kwargs={'is_emulated': True}. - - .. deprecated:: 2.8.3 - Use ``client`` instead. - - dimension_separator : {'.', '/'}, optional - Separator placed between the dimensions of a chunk. - - client : azure.storage.blob.ContainerClient, optional - And ``azure.storage.blob.ContainerClient`` to connect with. See - `here `_ # noqa - for more. - - .. versionadded:: 2.8.3 - - Notes - ----- - In order to use this store, you must install the Microsoft Azure Storage SDK for Python, - ``azure-storage-blob>=12.5.0``. - """ - - def __init__( - self, - container=None, - prefix="", - account_name=None, - account_key=None, - blob_service_kwargs=None, - dimension_separator=None, - client=None, - ): - self._dimension_separator = dimension_separator - self.prefix = normalize_storage_path(prefix) - if client is None: - # deprecated option, try to construct the client for them - msg = ( - "Providing 'container', 'account_name', 'account_key', and 'blob_service_kwargs'" - "is deprecated. Provide and instance of 'azure.storage.blob.ContainerClient' " - "'client' instead." - ) - warnings.warn(msg, FutureWarning, stacklevel=2) - from azure.storage.blob import ContainerClient - - blob_service_kwargs = blob_service_kwargs or {} - client = ContainerClient( - "https://{}.blob.core.windows.net/".format(account_name), - container, - credential=account_key, - **blob_service_kwargs, - ) - - self.client = client - self._container = container - self._account_name = account_name - self._account_key = account_key - - @staticmethod - def _warn_deprecated(property_): - msg = ( - "The {} property is deprecated and will be removed in a future " - "version. Get the property from 'ABSStore.client' instead." - ) - warnings.warn(msg.format(property_), FutureWarning, stacklevel=3) - - @property - def container(self): - self._warn_deprecated("container") - return self._container - - @property - def account_name(self): - self._warn_deprecated("account_name") - return self._account_name - - @property - def account_key(self): - self._warn_deprecated("account_key") - return self._account_key - - def _append_path_to_prefix(self, path): - if self.prefix == "": - return normalize_storage_path(path) - else: - return "/".join([self.prefix, normalize_storage_path(path)]) - - @staticmethod - def _strip_prefix_from_path(path, prefix): - # normalized things will not have any leading or trailing slashes - path_norm = normalize_storage_path(path) - prefix_norm = normalize_storage_path(prefix) - if prefix: - return path_norm[(len(prefix_norm) + 1) :] - else: - return path_norm - - def __getitem__(self, key): - from azure.core.exceptions import ResourceNotFoundError - - blob_name = self._append_path_to_prefix(key) - try: - return self.client.download_blob(blob_name).readall() - except ResourceNotFoundError: - raise KeyError("Blob %s not found" % blob_name) - - def __setitem__(self, key, value): - value = ensure_bytes(value) - blob_name = self._append_path_to_prefix(key) - self.client.upload_blob(blob_name, value, overwrite=True) - - def __delitem__(self, key): - from azure.core.exceptions import ResourceNotFoundError - - try: - self.client.delete_blob(self._append_path_to_prefix(key)) - except ResourceNotFoundError: - raise KeyError("Blob %s not found" % key) - - def __eq__(self, other): - return ( - isinstance(other, ABSStore) - and self.client == other.client - and self.prefix == other.prefix - ) - - def keys(self): - return list(self.__iter__()) - - def __iter__(self): - if self.prefix: - list_blobs_prefix = self.prefix + "/" - else: - list_blobs_prefix = None - for blob in self.client.list_blobs(list_blobs_prefix): - yield self._strip_prefix_from_path(blob.name, self.prefix) - - def __len__(self): - return len(self.keys()) - - def __contains__(self, key): - blob_name = self._append_path_to_prefix(key) - return self.client.get_blob_client(blob_name).exists() - - def listdir(self, path=None): - dir_path = normalize_storage_path(self._append_path_to_prefix(path)) - if dir_path: - dir_path += "/" - items = [ - self._strip_prefix_from_path(blob.name, dir_path) - for blob in self.client.walk_blobs(name_starts_with=dir_path, delimiter="/") - ] - return items - - def rmdir(self, path=None): - dir_path = normalize_storage_path(self._append_path_to_prefix(path)) - if dir_path: - dir_path += "/" - for blob in self.client.list_blobs(name_starts_with=dir_path): - self.client.delete_blob(blob) - - def getsize(self, path=None): - store_path = normalize_storage_path(path) - fs_path = self._append_path_to_prefix(store_path) - if fs_path: - blob_client = self.client.get_blob_client(fs_path) - else: - blob_client = None - - if blob_client and blob_client.exists(): - return blob_client.get_blob_properties().size - else: - size = 0 - if fs_path == "": - fs_path = None - elif not fs_path.endswith("/"): - fs_path += "/" - for blob in self.client.walk_blobs(name_starts_with=fs_path, delimiter="/"): - blob_client = self.client.get_blob_client(blob) - if blob_client.exists(): - size += blob_client.get_blob_properties().size - return size - - def clear(self): - self.rmdir() diff --git a/src/zarr/v2/_storage/store.py b/src/zarr/v2/_storage/store.py deleted file mode 100644 index ec1dbf0565..0000000000 --- a/src/zarr/v2/_storage/store.py +++ /dev/null @@ -1,226 +0,0 @@ -from collections.abc import MutableMapping -from typing import Any, List, Mapping, Optional, Sequence, Union - -from zarr.v2.meta import Metadata2 -from zarr.v2.util import normalize_storage_path -from zarr.v2.context import Context - - -# v2 store keys -array_meta_key = ".zarray" -group_meta_key = ".zgroup" -attrs_key = ".zattrs" - -DEFAULT_ZARR_VERSION = 2 - - -class BaseStore(MutableMapping[str, Any]): - """Abstract base class for store implementations. - - This is a thin wrapper over MutableMapping that provides methods to check - whether a store is readable, writeable, eraseable and or listable. - - Stores cannot be mutable mapping as they do have a couple of other - requirements that would break Liskov substitution principle (stores only - allow strings as keys, mutable mapping are more generic). - - Having no-op base method also helps simplifying store usage and do not need - to check the presence of attributes and methods, like `close()`. - - Stores can be used as context manager to make sure they close on exit. - - .. added: 2.11.0 - - """ - - _readable = True - _writeable = True - _erasable = True - _listable = True - _store_version = 2 - _metadata_class = Metadata2 - - def is_readable(self): - return self._readable - - def is_writeable(self): - return self._writeable - - def is_listable(self): - return self._listable - - def is_erasable(self): - return self._erasable - - def __enter__(self): - if not hasattr(self, "_open_count"): - self._open_count = 0 - self._open_count += 1 - return self - - def __exit__(self, exc_type, exc_value, traceback): - self._open_count -= 1 - if self._open_count == 0: - self.close() - - def close(self) -> None: - """Do nothing by default""" - pass - - def rename(self, src_path: str, dst_path: str) -> None: - if not self.is_erasable(): - raise NotImplementedError( - f'{type(self)} is not erasable, cannot call "rename"' - ) # pragma: no cover - _rename_from_keys(self, src_path, dst_path) - - @staticmethod - def _ensure_store(store: Any): - """ - We want to make sure internally that zarr stores are always a class - with a specific interface derived from ``BaseStore``, which is slightly - different than ``MutableMapping``. - - We'll do this conversion in a few places automatically - """ - from zarr.v2.storage import KVStore # avoid circular import - - if isinstance(store, BaseStore): - if not store._store_version == 2: - raise ValueError( - f"cannot initialize a v2 store with a v{store._store_version} store" - ) - return store - elif isinstance(store, MutableMapping): - return KVStore(store) - else: - for attr in [ - "keys", - "values", - "get", - "__setitem__", - "__getitem__", - "__delitem__", - "__contains__", - ]: - if not hasattr(store, attr): - break - else: - return KVStore(store) - - raise ValueError( - "Starting with Zarr 2.11.0, stores must be subclasses of " - "BaseStore, if your store exposes the MutableMapping interface " - f"wrap it in zarr.v2.storage.KVStore. Got {store}" - ) - - def getitems( - self, keys: Sequence[str], *, contexts: Mapping[str, Context] - ) -> Mapping[str, Any]: - """Retrieve data from multiple keys. - - Parameters - ---------- - keys : Iterable[str] - The keys to retrieve - contexts: Mapping[str, Context] - A mapping of keys to their context. Each context is a mapping of store - specific information. E.g. a context could be a dict telling the store - the preferred output array type: `{"meta_array": cupy.empty(())}` - - Returns - ------- - Mapping - A collection mapping the input keys to their results. - - Notes - ----- - This default implementation uses __getitem__() to read each key sequentially and - ignores contexts. Overwrite this method to implement concurrent reads of multiple - keys and/or to utilize the contexts. - """ - return {k: self[k] for k in keys if k in self} - - -class Store(BaseStore): - """Abstract store class used by implementations following the Zarr v2 spec. - - Adds public `listdir`, `rename`, and `rmdir` methods on top of BaseStore. - - .. added: 2.11.0 - - """ - - def listdir(self, path: str = "") -> List[str]: - path = normalize_storage_path(path) - return _listdir_from_keys(self, path) - - def rmdir(self, path: str = "") -> None: - if not self.is_erasable(): - raise NotImplementedError( - f'{type(self)} is not erasable, cannot call "rmdir"' - ) # pragma: no cover - path = normalize_storage_path(path) - _rmdir_from_keys(self, path) - - -# allow MutableMapping for backwards compatibility -StoreLike = Union[BaseStore, MutableMapping[str, Any]] - - -def _path_to_prefix(path: Optional[str]) -> str: - # assume path already normalized - if path: - prefix = path + "/" - else: - prefix = "" - return prefix - - -def _rename_from_keys(store: BaseStore, src_path: str, dst_path: str) -> None: - # assume path already normalized - src_prefix = _path_to_prefix(src_path) - dst_prefix = _path_to_prefix(dst_path) - version = getattr(store, "_store_version", 2) - if version == 2: - for key in list(store.keys()): - if key.startswith(src_prefix): - new_key = dst_prefix + key.lstrip(src_prefix) - store[new_key] = store.pop(key) - else: - raise NotImplementedError("This function only supports Zarr version 2.") - - -def _rmdir_from_keys(store: StoreLike, path: Optional[str] = None) -> None: - # assume path already normalized - prefix = _path_to_prefix(path) - for key in list(store.keys()): - if key.startswith(prefix): - del store[key] - - -def _listdir_from_keys(store: BaseStore, path: Optional[str] = None) -> List[str]: - # assume path already normalized - prefix = _path_to_prefix(path) - children = set() - for key in list(store.keys()): - if key.startswith(prefix) and len(key) > len(prefix): - suffix = key[len(prefix) :] - child = suffix.split("/")[0] - children.add(child) - return sorted(children) - - -def _prefix_to_array_key(store: StoreLike, prefix: str) -> str: - key = prefix + array_meta_key - return key - - -def _prefix_to_group_key(store: StoreLike, prefix: str) -> str: - key = prefix + group_meta_key - return key - - -def _prefix_to_attrs_key(store: StoreLike, prefix: str) -> str: - key = prefix + attrs_key - return key diff --git a/src/zarr/v2/attrs.py b/src/zarr/v2/attrs.py deleted file mode 100644 index af23d43b9e..0000000000 --- a/src/zarr/v2/attrs.py +++ /dev/null @@ -1,158 +0,0 @@ -from typing import Any -import warnings -from collections.abc import MutableMapping - -from zarr.v2._storage.store import Store -from zarr.v2.util import json_dumps - - -class Attributes(MutableMapping[str, Any]): - """Class providing access to user attributes on an array or group. Should not be - instantiated directly, will be available via the `.attrs` property of an array or - group. - - Parameters - ---------- - store : MutableMapping - The store in which to store the attributes. - key : str, optional - The key under which the attributes will be stored. - read_only : bool, optional - If True, attributes cannot be modified. - cache : bool, optional - If True (default), attributes will be cached locally. - synchronizer : Synchronizer - Only necessary if attributes may be modified from multiple threads or processes. - - """ - - def __init__(self, store, key=".zattrs", read_only=False, cache=True, synchronizer=None): - _Store = Store - self.store = _Store._ensure_store(store) - self.key = key - self.read_only = read_only - self.cache = cache - self._cached_asdict = None - self.synchronizer = synchronizer - - def _get_nosync(self): - try: - data = self.store[self.key] - except KeyError: - d: dict[str, Any] = dict() - else: - d = self.store._metadata_class.parse_metadata(data) - return d - - def asdict(self): - """Retrieve all attributes as a dictionary.""" - if self.cache and self._cached_asdict is not None: - return self._cached_asdict - d = self._get_nosync() - if self.cache: - self._cached_asdict = d - return d - - def refresh(self): - """Refresh cached attributes from the store.""" - if self.cache: - self._cached_asdict = self._get_nosync() - - def __contains__(self, x): - return x in self.asdict() - - def __getitem__(self, item): - return self.asdict()[item] - - def _write_op(self, f, *args, **kwargs): - # guard condition - if self.read_only: - raise PermissionError("attributes are read-only") - - # synchronization - if self.synchronizer is None: - return f(*args, **kwargs) - else: - with self.synchronizer[self.key]: - return f(*args, **kwargs) - - def __setitem__(self, item, value): - self._write_op(self._setitem_nosync, item, value) - - def _setitem_nosync(self, item, value): - # load existing data - d = self._get_nosync() - - # set key value - - d[item] = value - - # _put modified data - self._put_nosync(d) - - def __delitem__(self, item): - self._write_op(self._delitem_nosync, item) - - def _delitem_nosync(self, key): - # load existing data - d = self._get_nosync() - - # delete key value - del d[key] - - # _put modified data - self._put_nosync(d) - - def put(self, d): - """Overwrite all attributes with the key/value pairs in the provided dictionary - `d` in a single operation.""" - self._write_op(self._put_nosync, d) - - def _put_nosync(self, d): - d_to_check = d - if not all(isinstance(item, str) for item in d_to_check): - # TODO: Raise an error for non-string keys - # raise TypeError("attribute keys must be strings") - warnings.warn( - "only attribute keys of type 'string' will be allowed in the future", - DeprecationWarning, - stacklevel=2, - ) - - try: - d_to_check = {str(k): v for k, v in d_to_check.items()} - except TypeError as ex: # pragma: no cover - raise TypeError("attribute keys can not be stringified") from ex - - d = d_to_check - - self.store[self.key] = json_dumps(d) - if self.cache: - self._cached_asdict = d - - # noinspection PyMethodOverriding - def update(self, *args, **kwargs): - """Update the values of several attributes in a single operation.""" - self._write_op(self._update_nosync, *args, **kwargs) - - def _update_nosync(self, *args, **kwargs): - # load existing data - d = self._get_nosync() - - # update - d.update(*args, **kwargs) - - # _put modified data - self._put_nosync(d) - - def keys(self): - return self.asdict().keys() - - def __iter__(self): - return iter(self.asdict()) - - def __len__(self): - return len(self.asdict()) - - def _ipython_key_completions_(self): - return sorted(self) diff --git a/src/zarr/v2/codecs.py b/src/zarr/v2/codecs.py deleted file mode 100644 index 4ad68b8627..0000000000 --- a/src/zarr/v2/codecs.py +++ /dev/null @@ -1,4 +0,0 @@ -# flake8: noqa -from numcodecs import * -from numcodecs import get_codec, Blosc, Pickle, Zlib, Delta, AsType, BZ2 -from numcodecs.registry import codec_registry diff --git a/src/zarr/v2/context.py b/src/zarr/v2/context.py deleted file mode 100644 index 4eb1db7491..0000000000 --- a/src/zarr/v2/context.py +++ /dev/null @@ -1,19 +0,0 @@ -from typing import TypedDict - -from numcodecs.compat import NDArrayLike - - -class Context(TypedDict, total=False): - """A context for component specific information - - All keys are optional. Any component reading the context must provide - a default implementation in the case a key cannot be found. - - Attributes - ---------- - meta_array : array-like, optional - An array-like instance to use for determining the preferred output - array type. - """ - - meta_array: NDArrayLike diff --git a/src/zarr/v2/convenience.py b/src/zarr/v2/convenience.py deleted file mode 100644 index c066ee59e0..0000000000 --- a/src/zarr/v2/convenience.py +++ /dev/null @@ -1,1284 +0,0 @@ -"""Convenience functions for storing and loading data.""" - -import itertools -import os -import re -from collections.abc import Mapping, MutableMapping -from zarr.v2.core import Array -from zarr.v2.creation import array as _create_array -from zarr.v2.creation import open_array -from zarr.v2.errors import CopyError, PathNotFoundError -from zarr.v2.hierarchy import Group -from zarr.v2.hierarchy import group as _create_group -from zarr.v2.hierarchy import open_group -from zarr.v2.meta import json_dumps, json_loads -from zarr.v2.storage import ( - contains_array, - contains_group, - normalize_store_arg, - BaseStore, - ConsolidatedMetadataStore, -) -from zarr.v2.util import TreeViewer, buffer_size, normalize_storage_path - -from typing import Any, Union - -StoreLike = Union[BaseStore, MutableMapping[str, Any], str, None] - -_builtin_open = open # builtin open is later shadowed by a local open function - - -def _check_and_update_path(store: BaseStore, path): - if getattr(store, "_store_version", 2) > 2 and not path: - raise ValueError("path must be provided for v3 stores") - return normalize_storage_path(path) - - -# noinspection PyShadowingBuiltins -def open(store: StoreLike = None, mode: str = "a", *, path=None, **kwargs): - """Convenience function to open a group or array using file-mode-like semantics. - - Parameters - ---------- - store : Store or string, optional - Store or path to directory in file system or name of zip file. - mode : {'r', 'r+', 'a', 'w', 'w-'}, optional - Persistence mode: 'r' means read only (must exist); 'r+' means - read/write (must exist); 'a' means read/write (create if doesn't - exist); 'w' means create (overwrite if exists); 'w-' means create - (fail if exists). - path : str or None, optional - The path within the store to open. - **kwargs - Additional parameters are passed through to :func:`zarr.v2.creation.open_array` or - :func:`zarr.v2.hierarchy.open_group`. - - Returns - ------- - z : :class:`zarr.v2.core.Array` or :class:`zarr.v2.hierarchy.Group` - Array or group, depending on what exists in the given store. - - See Also - -------- - zarr.v2.creation.open_array, zarr.v2.hierarchy.open_group - - Examples - -------- - - Storing data in a directory 'data/example.zarr' on the local file system:: - - >>> import zarr - >>> store = 'data/example.zarr' - >>> zw = zarr.v2.open(store, mode='w', shape=100, dtype='i4') # open new array - >>> zw - - >>> za = zarr.v2.open(store, mode='a') # open existing array for reading and writing - >>> za - - >>> zr = zarr.v2.open(store, mode='r') # open existing array read-only - >>> zr - - >>> gw = zarr.v2.open(store, mode='w') # open new group, overwriting previous data - >>> gw - - >>> ga = zarr.v2.open(store, mode='a') # open existing group for reading and writing - >>> ga - - >>> gr = zarr.v2.open(store, mode='r') # open existing group read-only - >>> gr - - - """ - - # handle polymorphic store arg - # we pass storage options explicitly, since normalize_store_arg might construct - # a store if the input is a fsspec-compatible URL - _store: BaseStore = normalize_store_arg( - store, storage_options=kwargs.pop("storage_options", {}), mode=mode - ) - # path = _check_and_update_path(_store, path) - path = normalize_storage_path(path) - kwargs["path"] = path - - if mode in {"w", "w-", "x"}: - if "shape" in kwargs: - return open_array(_store, mode=mode, **kwargs) - else: - return open_group(_store, mode=mode, **kwargs) - - elif mode == "a": - if "shape" in kwargs or contains_array(_store, path): - return open_array(_store, mode=mode, **kwargs) - else: - return open_group(_store, mode=mode, **kwargs) - - else: - if contains_array(_store, path): - return open_array(_store, mode=mode, **kwargs) - elif contains_group(_store, path): - return open_group(_store, mode=mode, **kwargs) - else: - raise PathNotFoundError(path) - - -def _might_close(path): - return isinstance(path, (str, os.PathLike)) - - -def save_array(store: StoreLike, arr, *, path=None, **kwargs): - """Convenience function to save a NumPy array to the local file system, following a - similar API to the NumPy save() function. - - Parameters - ---------- - store : MutableMapping or string - Store or path to directory in file system or name of zip file. - arr : ndarray - NumPy array with data to save. - path : str or None, optional - The path within the store where the array will be saved. - kwargs - Passed through to :func:`create`, e.g., compressor. - - Examples - -------- - Save an array to a directory on the file system (uses a :class:`DirectoryStore`):: - - >>> import zarr - >>> import numpy as np - >>> arr = np.arange(10000) - >>> zarr.v2.save_array('data/example.zarr', arr) - >>> zarr.v2.load('data/example.zarr') - array([ 0, 1, 2, ..., 9997, 9998, 9999]) - - Save an array to a single file (uses a :class:`ZipStore`):: - - >>> zarr.v2.save_array('data/example.zip', arr) - >>> zarr.v2.load('data/example.zip') - array([ 0, 1, 2, ..., 9997, 9998, 9999]) - - """ - may_need_closing = _might_close(store) - _store: BaseStore = normalize_store_arg(store, mode="w") - path = _check_and_update_path(_store, path) - try: - _create_array(arr, store=_store, overwrite=True, path=path, **kwargs) - finally: - if may_need_closing: - # needed to ensure zip file records are written - _store.close() - - -def save_group(store: StoreLike, *args, path=None, **kwargs): - """Convenience function to save several NumPy arrays to the local file system, following a - similar API to the NumPy savez()/savez_compressed() functions. - - Parameters - ---------- - store : MutableMapping or string - Store or path to directory in file system or name of zip file. - args : ndarray - NumPy arrays with data to save. - path : str or None, optional - Path within the store where the group will be saved. - kwargs - NumPy arrays with data to save. - - Examples - -------- - Save several arrays to a directory on the file system (uses a - :class:`DirectoryStore`): - - >>> import zarr - >>> import numpy as np - >>> a1 = np.arange(10000) - >>> a2 = np.arange(10000, 0, -1) - >>> zarr.v2.save_group('data/example.zarr', a1, a2) - >>> loader = zarr.v2.load('data/example.zarr') - >>> loader - - >>> loader['arr_0'] - array([ 0, 1, 2, ..., 9997, 9998, 9999]) - >>> loader['arr_1'] - array([10000, 9999, 9998, ..., 3, 2, 1]) - - Save several arrays using named keyword arguments:: - - >>> zarr.v2.save_group('data/example.zarr', foo=a1, bar=a2) - >>> loader = zarr.v2.load('data/example.zarr') - >>> loader - - >>> loader['foo'] - array([ 0, 1, 2, ..., 9997, 9998, 9999]) - >>> loader['bar'] - array([10000, 9999, 9998, ..., 3, 2, 1]) - - Store several arrays in a single zip file (uses a :class:`ZipStore`):: - - >>> zarr.v2.save_group('data/example.zip', foo=a1, bar=a2) - >>> loader = zarr.v2.load('data/example.zip') - >>> loader - - >>> loader['foo'] - array([ 0, 1, 2, ..., 9997, 9998, 9999]) - >>> loader['bar'] - array([10000, 9999, 9998, ..., 3, 2, 1]) - - Notes - ----- - Default compression options will be used. - - """ - if len(args) == 0 and len(kwargs) == 0: - raise ValueError("at least one array must be provided") - # handle polymorphic store arg - may_need_closing = _might_close(store) - _store: BaseStore = normalize_store_arg(store, mode="w") - path = _check_and_update_path(_store, path) - try: - grp = _create_group(_store, path=path, overwrite=True) - for i, arr in enumerate(args): - k = "arr_{}".format(i) - grp.create_dataset(k, data=arr, overwrite=True) - for k, arr in kwargs.items(): - grp.create_dataset(k, data=arr, overwrite=True) - finally: - if may_need_closing: - # needed to ensure zip file records are written - _store.close() - - -def save(store: StoreLike, *args, path=None, **kwargs): - """Convenience function to save an array or group of arrays to the local file system. - - Parameters - ---------- - store : MutableMapping or string - Store or path to directory in file system or name of zip file. - args : ndarray - NumPy arrays with data to save. - path : str or None, optional - The path within the group where the arrays will be saved. - kwargs - NumPy arrays with data to save. - - Examples - -------- - Save an array to a directory on the file system (uses a :class:`DirectoryStore`):: - - >>> import zarr - >>> import numpy as np - >>> arr = np.arange(10000) - >>> zarr.v2.save('data/example.zarr', arr) - >>> zarr.v2.load('data/example.zarr') - array([ 0, 1, 2, ..., 9997, 9998, 9999]) - - Save an array to a Zip file (uses a :class:`ZipStore`):: - - >>> zarr.v2.save('data/example.zip', arr) - >>> zarr.v2.load('data/example.zip') - array([ 0, 1, 2, ..., 9997, 9998, 9999]) - - Save several arrays to a directory on the file system (uses a - :class:`DirectoryStore` and stores arrays in a group):: - - >>> import zarr - >>> import numpy as np - >>> a1 = np.arange(10000) - >>> a2 = np.arange(10000, 0, -1) - >>> zarr.v2.save('data/example.zarr', a1, a2) - >>> loader = zarr.v2.load('data/example.zarr') - >>> loader - - >>> loader['arr_0'] - array([ 0, 1, 2, ..., 9997, 9998, 9999]) - >>> loader['arr_1'] - array([10000, 9999, 9998, ..., 3, 2, 1]) - - Save several arrays using named keyword arguments:: - - >>> zarr.v2.save('data/example.zarr', foo=a1, bar=a2) - >>> loader = zarr.v2.load('data/example.zarr') - >>> loader - - >>> loader['foo'] - array([ 0, 1, 2, ..., 9997, 9998, 9999]) - >>> loader['bar'] - array([10000, 9999, 9998, ..., 3, 2, 1]) - - Store several arrays in a single zip file (uses a :class:`ZipStore`):: - - >>> zarr.v2.save('data/example.zip', foo=a1, bar=a2) - >>> loader = zarr.v2.load('data/example.zip') - >>> loader - - >>> loader['foo'] - array([ 0, 1, 2, ..., 9997, 9998, 9999]) - >>> loader['bar'] - array([10000, 9999, 9998, ..., 3, 2, 1]) - - See Also - -------- - save_array, save_group - - """ - if len(args) == 0 and len(kwargs) == 0: - raise ValueError("at least one array must be provided") - if len(args) == 1 and len(kwargs) == 0: - save_array(store, args[0], path=path) - else: - save_group(store, *args, path=path, **kwargs) - - -class LazyLoader(Mapping): - def __init__(self, grp): - self.grp = grp - self.cache = dict() - - def __getitem__(self, item): - try: - return self.cache[item] - except KeyError: - arr = self.grp[item][...] - self.cache[item] = arr - return arr - - def __len__(self): - return len(self.grp) - - def __iter__(self): - return iter(self.grp) - - def __contains__(self, item): - return item in self.grp - - def __repr__(self): - r = ">> import zarr - >>> g1 = zarr.v2.group() - >>> g2 = g1.create_group('foo') - >>> g3 = g1.create_group('bar') - >>> g4 = g3.create_group('baz') - >>> g5 = g3.create_group('qux') - >>> d1 = g5.create_dataset('baz', shape=100, chunks=10) - >>> g1.tree() - / - ├── bar - │ ├── baz - │ └── qux - │ └── baz (100,) float64 - └── foo - >>> import h5py - >>> h5f = h5py.File('data/example.h5', mode='w') - >>> zarr.v2.copy_all(g1, h5f) - (5, 0, 800) - >>> zarr.v2.tree(h5f) - / - ├── bar - │ ├── baz - │ └── qux - │ └── baz (100,) float64 - └── foo - - See Also - -------- - zarr.v2.hierarchy.Group.tree - - Notes - ----- - Please note that this is an experimental feature. The behaviour of this - function is still evolving and the default output and/or parameters may change - in future versions. - - """ - - return TreeViewer(grp, expand=expand, level=level) - - -class _LogWriter: - def __init__(self, log): - self.log_func = None - self.log_file = None - self.needs_closing = False - if log is None: - # don't do any logging - pass - elif callable(log): - self.log_func = log - elif isinstance(log, str): - self.log_file = _builtin_open(log, mode="w") - self.needs_closing = True - elif hasattr(log, "write"): - self.log_file = log - else: - raise TypeError( - "log must be a callable function, file path or file-like object, found %r" % log - ) - - def __enter__(self): - return self - - def __exit__(self, *args): - if self.log_file is not None and self.needs_closing: - self.log_file.close() - - def __call__(self, *args, **kwargs): - if self.log_file is not None: - kwargs["file"] = self.log_file - print(*args, **kwargs) - if hasattr(self.log_file, "flush"): - # get immediate feedback - self.log_file.flush() - elif self.log_func is not None: - self.log_func(*args, **kwargs) - - -def _log_copy_summary(log, dry_run, n_copied, n_skipped, n_bytes_copied): - # log a final message with a summary of what happened - if dry_run: - message = "dry run: " - else: - message = "all done: " - message += "{:,} copied, {:,} skipped".format(n_copied, n_skipped) - if not dry_run: - message += ", {:,} bytes copied".format(n_bytes_copied) - log(message) - - -def copy_store( - source, - dest, - source_path="", - dest_path="", - excludes=None, - includes=None, - flags=0, - if_exists="raise", - dry_run=False, - log=None, -): - """Copy data directly from the `source` store to the `dest` store. Use this - function when you want to copy a group or array in the most efficient way, - preserving all configuration and attributes. This function is more efficient - than the copy() or copy_all() functions because it avoids de-compressing and - re-compressing data, rather the compressed chunk data for each array are - copied directly between stores. - - Parameters - ---------- - source : Mapping - Store to copy data from. - dest : MutableMapping - Store to copy data into. - source_path : str, optional - Only copy data from under this path in the source store. - dest_path : str, optional - Copy data into this path in the destination store. - excludes : sequence of str, optional - One or more regular expressions which will be matched against keys in - the source store. Any matching key will not be copied. - includes : sequence of str, optional - One or more regular expressions which will be matched against keys in - the source store and will override any excludes also matching. - flags : int, optional - Regular expression flags used for matching excludes and includes. - if_exists : {'raise', 'replace', 'skip'}, optional - How to handle keys that already exist in the destination store. If - 'raise' then a CopyError is raised on the first key already present - in the destination store. If 'replace' then any data will be replaced in - the destination. If 'skip' then any existing keys will not be copied. - dry_run : bool, optional - If True, don't actually copy anything, just log what would have - happened. - log : callable, file path or file-like object, optional - If provided, will be used to log progress information. - - Returns - ------- - n_copied : int - Number of items copied. - n_skipped : int - Number of items skipped. - n_bytes_copied : int - Number of bytes of data that were actually copied. - - Examples - -------- - - >>> import zarr - >>> store1 = zarr.v2.DirectoryStore('data/example.zarr') - >>> root = zarr.v2.group(store1, overwrite=True) - >>> foo = root.create_group('foo') - >>> bar = foo.create_group('bar') - >>> baz = bar.create_dataset('baz', shape=100, chunks=50, dtype='i8') - >>> import numpy as np - >>> baz[:] = np.arange(100) - >>> root.tree() - / - └── foo - └── bar - └── baz (100,) int64 - >>> from sys import stdout - >>> store2 = zarr.v2.ZipStore('data/example.zip', mode='w') - >>> zarr.v2.copy_store(store1, store2, log=stdout) - copy .zgroup - copy foo/.zgroup - copy foo/bar/.zgroup - copy foo/bar/baz/.zarray - copy foo/bar/baz/0 - copy foo/bar/baz/1 - all done: 6 copied, 0 skipped, 566 bytes copied - (6, 0, 566) - >>> new_root = zarr.v2.group(store2) - >>> new_root.tree() - / - └── foo - └── bar - └── baz (100,) int64 - >>> new_root['foo/bar/baz'][:] - array([ 0, 1, 2, ..., 97, 98, 99]) - >>> store2.close() # zip stores need to be closed - - Notes - ----- - Please note that this is an experimental feature. The behaviour of this - function is still evolving and the default behaviour and/or parameters may change - in future versions. - - """ - - # normalize paths - source_path = normalize_storage_path(source_path) - dest_path = normalize_storage_path(dest_path) - if source_path: - source_path = source_path + "/" - if dest_path: - dest_path = dest_path + "/" - - # normalize excludes and includes - if excludes is None: - excludes = [] - elif isinstance(excludes, str): - excludes = [excludes] - if includes is None: - includes = [] - elif isinstance(includes, str): - includes = [includes] - excludes = [re.compile(e, flags) for e in excludes] - includes = [re.compile(i, flags) for i in includes] - - # check if_exists parameter - valid_if_exists = ["raise", "replace", "skip"] - if if_exists not in valid_if_exists: - raise ValueError( - "if_exists must be one of {!r}; found {!r}".format(valid_if_exists, if_exists) - ) - - # setup counting variables - n_copied = n_skipped = n_bytes_copied = 0 - - source_store_version = getattr(source, "_store_version", 2) - dest_store_version = getattr(dest, "_store_version", 2) - if source_store_version != dest_store_version: - raise ValueError("zarr stores must share the same protocol version") - - if source_store_version > 2: - raise NotImplementedError("This function only supports Zarr version 2.") - - # setup logging - with _LogWriter(log) as log: - # iterate over source keys - for source_key in sorted(source.keys()): - # filter to keys under source path - if source_store_version == 2: - if not source_key.startswith(source_path): - continue - elif source_store_version == 3: - raise NotImplementedError("This function only supports Zarr version 2.") - # process excludes and includes - exclude = False - for prog in excludes: - if prog.search(source_key): - exclude = True - break - if exclude: - for prog in includes: - if prog.search(source_key): - exclude = False - break - if exclude: - continue - - # map key to destination path - if source_store_version == 2: - key_suffix = source_key[len(source_path) :] - dest_key = dest_path + key_suffix - elif source_store_version == 3: - raise NotImplementedError("This function only supports Zarr version 2.") - # create a descriptive label for this operation - descr = source_key - if dest_key != source_key: - descr = descr + " -> " + dest_key - - # decide what to do - do_copy = True - if if_exists != "replace" and dest_key in dest: - if if_exists == "raise": - raise CopyError("key {!r} exists in destination".format(dest_key)) - elif if_exists == "skip": - do_copy = False - - # take action - if do_copy: - log("copy {}".format(descr)) - if not dry_run: - data = source[source_key] - n_bytes_copied += buffer_size(data) - dest[dest_key] = data - n_copied += 1 - else: - log("skip {}".format(descr)) - n_skipped += 1 - - # log a final message with a summary of what happened - _log_copy_summary(log, dry_run, n_copied, n_skipped, n_bytes_copied) - - return n_copied, n_skipped, n_bytes_copied - - -def _check_dest_is_group(dest): - if not hasattr(dest, "create_dataset"): - raise ValueError("dest must be a group, got {!r}".format(dest)) - - -def copy( - source, - dest, - name=None, - shallow=False, - without_attrs=False, - log=None, - if_exists="raise", - dry_run=False, - **create_kws, -): - """Copy the `source` array or group into the `dest` group. - - Parameters - ---------- - source : group or array/dataset - A zarr group or array, or an h5py group or dataset. - dest : group - A zarr or h5py group. - name : str, optional - Name to copy the object to. - shallow : bool, optional - If True, only copy immediate children of `source`. - without_attrs : bool, optional - Do not copy user attributes. - log : callable, file path or file-like object, optional - If provided, will be used to log progress information. - if_exists : {'raise', 'replace', 'skip', 'skip_initialized'}, optional - How to handle arrays that already exist in the destination group. If - 'raise' then a CopyError is raised on the first array already present - in the destination group. If 'replace' then any array will be - replaced in the destination. If 'skip' then any existing arrays will - not be copied. If 'skip_initialized' then any existing arrays with - all chunks initialized will not be copied (not available when copying to - h5py). - dry_run : bool, optional - If True, don't actually copy anything, just log what would have - happened. - **create_kws - Passed through to the create_dataset method when copying an array/dataset. - - Returns - ------- - n_copied : int - Number of items copied. - n_skipped : int - Number of items skipped. - n_bytes_copied : int - Number of bytes of data that were actually copied. - - Examples - -------- - Here's an example of copying a group named 'foo' from an HDF5 file to a - Zarr group:: - - >>> import h5py - >>> import zarr - >>> import numpy as np - >>> source = h5py.File('data/example.h5', mode='w') - >>> foo = source.create_group('foo') - >>> baz = foo.create_dataset('bar/baz', data=np.arange(100), chunks=(50,)) - >>> spam = source.create_dataset('spam', data=np.arange(100, 200), chunks=(30,)) - >>> zarr.v2.tree(source) - / - ├── foo - │ └── bar - │ └── baz (100,) int64 - └── spam (100,) int64 - >>> dest = zarr.v2.group() - >>> from sys import stdout - >>> zarr.v2.copy(source['foo'], dest, log=stdout) - copy /foo - copy /foo/bar - copy /foo/bar/baz (100,) int64 - all done: 3 copied, 0 skipped, 800 bytes copied - (3, 0, 800) - >>> dest.tree() # N.B., no spam - / - └── foo - └── bar - └── baz (100,) int64 - >>> source.close() - - The ``if_exists`` parameter provides options for how to handle pre-existing data in - the destination. Here are some examples of these options, also using - ``dry_run=True`` to find out what would happen without actually copying anything:: - - >>> source = zarr.v2.group() - >>> dest = zarr.v2.group() - >>> baz = source.create_dataset('foo/bar/baz', data=np.arange(100)) - >>> spam = source.create_dataset('foo/spam', data=np.arange(1000)) - >>> existing_spam = dest.create_dataset('foo/spam', data=np.arange(1000)) - >>> from sys import stdout - >>> try: - ... zarr.v2.copy(source['foo'], dest, log=stdout, dry_run=True) - ... except zarr.v2.CopyError as e: - ... print(e) - ... - copy /foo - copy /foo/bar - copy /foo/bar/baz (100,) int64 - an object 'spam' already exists in destination '/foo' - >>> zarr.v2.copy(source['foo'], dest, log=stdout, if_exists='replace', dry_run=True) - copy /foo - copy /foo/bar - copy /foo/bar/baz (100,) int64 - copy /foo/spam (1000,) int64 - dry run: 4 copied, 0 skipped - (4, 0, 0) - >>> zarr.v2.copy(source['foo'], dest, log=stdout, if_exists='skip', dry_run=True) - copy /foo - copy /foo/bar - copy /foo/bar/baz (100,) int64 - skip /foo/spam (1000,) int64 - dry run: 3 copied, 1 skipped - (3, 1, 0) - - Notes - ----- - Please note that this is an experimental feature. The behaviour of this - function is still evolving and the default behaviour and/or parameters may change - in future versions. - - """ - - # value checks - _check_dest_is_group(dest) - - # setup logging - with _LogWriter(log) as log: - # do the copying - n_copied, n_skipped, n_bytes_copied = _copy( - log, - source, - dest, - name=name, - root=True, - shallow=shallow, - without_attrs=without_attrs, - if_exists=if_exists, - dry_run=dry_run, - **create_kws, - ) - - # log a final message with a summary of what happened - _log_copy_summary(log, dry_run, n_copied, n_skipped, n_bytes_copied) - - return n_copied, n_skipped, n_bytes_copied - - -def _copy(log, source, dest, name, root, shallow, without_attrs, if_exists, dry_run, **create_kws): - # N.B., if this is a dry run, dest may be None - - # setup counting variables - n_copied = n_skipped = n_bytes_copied = 0 - - # are we copying to/from h5py? - source_h5py = source.__module__.startswith("h5py.") - dest_h5py = dest is not None and dest.__module__.startswith("h5py.") - - # check if_exists parameter - valid_if_exists = ["raise", "replace", "skip", "skip_initialized"] - if if_exists not in valid_if_exists: - raise ValueError( - "if_exists must be one of {!r}; found {!r}".format(valid_if_exists, if_exists) - ) - if dest_h5py and if_exists == "skip_initialized": - raise ValueError("{!r} can only be used when copying to zarr".format(if_exists)) - - # determine name to copy to - if name is None: - name = source.name.split("/")[-1] - if not name: - # this can happen if source is the root group - raise TypeError( - "source has no name, please provide the `name` " - "parameter to indicate a name to copy to" - ) - - if hasattr(source, "shape"): - # copy a dataset/array - - # check if already exists, decide what to do - do_copy = True - exists = dest is not None and name in dest - if exists: - if if_exists == "raise": - raise CopyError( - "an object {!r} already exists in destination {!r}".format(name, dest.name) - ) - elif if_exists == "skip": - do_copy = False - elif if_exists == "skip_initialized": - ds = dest[name] - if ds.nchunks_initialized == ds.nchunks: - do_copy = False - - # take action - if do_copy: - # log a message about what we're going to do - log("copy {} {} {}".format(source.name, source.shape, source.dtype)) - - if not dry_run: - # clear the way - if exists: - del dest[name] - - # setup creation keyword arguments - kws = create_kws.copy() - - # setup chunks option, preserve by default - kws.setdefault("chunks", source.chunks) - - # setup compression options - if source_h5py: - if dest_h5py: - # h5py -> h5py; preserve compression options by default - kws.setdefault("compression", source.compression) - kws.setdefault("compression_opts", source.compression_opts) - kws.setdefault("shuffle", source.shuffle) - kws.setdefault("fletcher32", source.fletcher32) - kws.setdefault("fillvalue", source.fillvalue) - else: - # h5py -> zarr; use zarr default compression options - kws.setdefault("fill_value", source.fillvalue) - else: - if dest_h5py: - # zarr -> h5py; use some vaguely sensible defaults - kws.setdefault("chunks", True) - kws.setdefault("compression", "gzip") - kws.setdefault("compression_opts", 1) - kws.setdefault("shuffle", False) - kws.setdefault("fillvalue", source.fill_value) - else: - # zarr -> zarr; preserve compression options by default - kws.setdefault("compressor", source.compressor) - kws.setdefault("filters", source.filters) - kws.setdefault("order", source.order) - kws.setdefault("fill_value", source.fill_value) - - # create new dataset in destination - ds = dest.create_dataset(name, shape=source.shape, dtype=source.dtype, **kws) - - # copy data - N.B., go chunk by chunk to avoid loading - # everything into memory - shape = ds.shape - chunks = ds.chunks - chunk_offsets = [range(0, s, c) for s, c in zip(shape, chunks)] - for offset in itertools.product(*chunk_offsets): - sel = tuple(slice(o, min(s, o + c)) for o, s, c in zip(offset, shape, chunks)) - ds[sel] = source[sel] - n_bytes_copied += ds.size * ds.dtype.itemsize - - # copy attributes - if not without_attrs: - if dest_h5py and "filters" in source.attrs: - # No filters key in v3 metadata so it was stored in the - # attributes instead. We cannot copy this key to - # HDF5 attrs, though! - source_attrs = source.attrs.asdict().copy() - source_attrs.pop("filters", None) - else: - source_attrs = source.attrs - ds.attrs.update(source_attrs) - - n_copied += 1 - - else: - log("skip {} {} {}".format(source.name, source.shape, source.dtype)) - n_skipped += 1 - - elif root or not shallow: - # copy a group - - # check if an array is in the way - do_copy = True - exists_array = dest is not None and name in dest and hasattr(dest[name], "shape") - if exists_array: - if if_exists == "raise": - raise CopyError( - "an array {!r} already exists in destination {!r}".format(name, dest.name) - ) - elif if_exists == "skip": - do_copy = False - - # take action - if do_copy: - # log action - log("copy {}".format(source.name)) - - if not dry_run: - # clear the way - if exists_array: - del dest[name] - - # require group in destination - grp = dest.require_group(name) - - # copy attributes - if not without_attrs: - grp.attrs.update(source.attrs) - - else: - # setup for dry run without creating any groups in the - # destination - if dest is not None: - grp = dest.get(name, None) - else: - grp = None - - # recurse - for k in source.keys(): - c, s, b = _copy( - log, - source[k], - grp, - name=k, - root=False, - shallow=shallow, - without_attrs=without_attrs, - if_exists=if_exists, - dry_run=dry_run, - **create_kws, - ) - n_copied += c - n_skipped += s - n_bytes_copied += b - - n_copied += 1 - - else: - log("skip {}".format(source.name)) - n_skipped += 1 - - return n_copied, n_skipped, n_bytes_copied - - -def copy_all( - source, - dest, - shallow=False, - without_attrs=False, - log=None, - if_exists="raise", - dry_run=False, - **create_kws, -): - """Copy all children of the `source` group into the `dest` group. - - Parameters - ---------- - source : group or array/dataset - A zarr group or array, or an h5py group or dataset. - dest : group - A zarr or h5py group. - shallow : bool, optional - If True, only copy immediate children of `source`. - without_attrs : bool, optional - Do not copy user attributes. - log : callable, file path or file-like object, optional - If provided, will be used to log progress information. - if_exists : {'raise', 'replace', 'skip', 'skip_initialized'}, optional - How to handle arrays that already exist in the destination group. If - 'raise' then a CopyError is raised on the first array already present - in the destination group. If 'replace' then any array will be - replaced in the destination. If 'skip' then any existing arrays will - not be copied. If 'skip_initialized' then any existing arrays with - all chunks initialized will not be copied (not available when copying to - h5py). - dry_run : bool, optional - If True, don't actually copy anything, just log what would have - happened. - **create_kws - Passed through to the create_dataset method when copying an - array/dataset. - - Returns - ------- - n_copied : int - Number of items copied. - n_skipped : int - Number of items skipped. - n_bytes_copied : int - Number of bytes of data that were actually copied. - - Examples - -------- - >>> import h5py - >>> import zarr - >>> import numpy as np - >>> source = h5py.File('data/example.h5', mode='w') - >>> foo = source.create_group('foo') - >>> baz = foo.create_dataset('bar/baz', data=np.arange(100), chunks=(50,)) - >>> spam = source.create_dataset('spam', data=np.arange(100, 200), chunks=(30,)) - >>> zarr.v2.tree(source) - / - ├── foo - │ └── bar - │ └── baz (100,) int64 - └── spam (100,) int64 - >>> dest = zarr.v2.group() - >>> import sys - >>> zarr.v2.copy_all(source, dest, log=sys.stdout) - copy /foo - copy /foo/bar - copy /foo/bar/baz (100,) int64 - copy /spam (100,) int64 - all done: 4 copied, 0 skipped, 1,600 bytes copied - (4, 0, 1600) - >>> dest.tree() - / - ├── foo - │ └── bar - │ └── baz (100,) int64 - └── spam (100,) int64 - >>> source.close() - - Notes - ----- - Please note that this is an experimental feature. The behaviour of this - function is still evolving and the default behaviour and/or parameters may change - in future versions. - - """ - - # value checks - _check_dest_is_group(dest) - - # setup counting variables - n_copied = n_skipped = n_bytes_copied = 0 - - # setup logging - with _LogWriter(log) as log: - for k in source.keys(): - c, s, b = _copy( - log, - source[k], - dest, - name=k, - root=False, - shallow=shallow, - without_attrs=without_attrs, - if_exists=if_exists, - dry_run=dry_run, - **create_kws, - ) - n_copied += c - n_skipped += s - n_bytes_copied += b - - dest.attrs.update(**source.attrs) - - # log a final message with a summary of what happened - _log_copy_summary(log, dry_run, n_copied, n_skipped, n_bytes_copied) - - return n_copied, n_skipped, n_bytes_copied - - -def consolidate_metadata(store: BaseStore, metadata_key=".zmetadata", *, path=""): - """ - Consolidate all metadata for groups and arrays within the given store - into a single resource and put it under the given key. - - This produces a single object in the backend store, containing all the - metadata read from all the zarr-related keys that can be found. After - metadata have been consolidated, use :func:`open_consolidated` to open - the root group in optimised, read-only mode, using the consolidated - metadata to reduce the number of read operations on the backend store. - - Note, that if the metadata in the store is changed after this - consolidation, then the metadata read by :func:`open_consolidated` - would be incorrect unless this function is called again. - - .. note:: This is an experimental feature. - - Parameters - ---------- - store : MutableMapping or string - Store or path to directory in file system or name of zip file. - metadata_key : str - Key to put the consolidated metadata under. - path : str or None - Path corresponding to the group that is being consolidated. Not required - for zarr v2 stores. - - Returns - ------- - g : :class:`zarr.v2.hierarchy.Group` - Group instance, opened with the new consolidated metadata. - - See Also - -------- - open_consolidated - - """ - store = normalize_store_arg(store, mode="w") - - version = store._store_version - - if version == 2: - - def is_zarr_key(key): - return key.endswith(".zarray") or key.endswith(".zgroup") or key.endswith(".zattrs") - - else: - raise NotImplementedError("This function only supports Zarr version 2.") - out = { - "zarr_consolidated_format": 1, - "metadata": {key: json_loads(store[key]) for key in store if is_zarr_key(key)}, - } - store[metadata_key] = json_dumps(out) - return open_consolidated(store, metadata_key=metadata_key, path=path) - - -def open_consolidated(store: StoreLike, metadata_key=".zmetadata", mode="r+", **kwargs): - """Open group using metadata previously consolidated into a single key. - - This is an optimised method for opening a Zarr group, where instead of - traversing the group/array hierarchy by accessing the metadata keys at - each level, a single key contains all of the metadata for everything. - For remote data sources where the overhead of accessing a key is large - compared to the time to read data. - - The group accessed must have already had its metadata consolidated into a - single key using the function :func:`consolidate_metadata`. - - This optimised method only works in modes which do not change the - metadata, although the data may still be written/updated. - - Parameters - ---------- - store : MutableMapping or string - Store or path to directory in file system or name of zip file. - metadata_key : str - Key to read the consolidated metadata from. The default (.zmetadata) - corresponds to the default used by :func:`consolidate_metadata`. - mode : {'r', 'r+'}, optional - Persistence mode: 'r' means read only (must exist); 'r+' means - read/write (must exist) although only writes to data are allowed, - changes to metadata including creation of new arrays or group - are not allowed. - **kwargs - Additional parameters are passed through to :func:`zarr.v2.creation.open_array` or - :func:`zarr.v2.hierarchy.open_group`. - - Returns - ------- - g : :class:`zarr.v2.hierarchy.Group` - Group instance, opened with the consolidated metadata. - - See Also - -------- - consolidate_metadata - - """ - - # normalize parameters - store = normalize_store_arg(store, storage_options=kwargs.get("storage_options"), mode=mode) - if mode not in {"r", "r+"}: - raise ValueError("invalid mode, expected either 'r' or 'r+'; found {!r}".format(mode)) - - path = kwargs.pop("path", None) - if store._store_version == 2: - ConsolidatedStoreClass = ConsolidatedMetadataStore - else: - raise NotImplementedError("This function only supports Zarr version 2.") - - # setup metadata store - meta_store = ConsolidatedStoreClass(store, metadata_key=metadata_key) - - # pass through - chunk_store = kwargs.pop("chunk_store", None) or store - return open(store=meta_store, chunk_store=chunk_store, mode=mode, path=path, **kwargs) diff --git a/src/zarr/v2/core.py b/src/zarr/v2/core.py deleted file mode 100644 index 9eeb467d68..0000000000 --- a/src/zarr/v2/core.py +++ /dev/null @@ -1,2855 +0,0 @@ -import binascii -import hashlib -import itertools -import math -import operator -import re -from functools import reduce -from typing import Any - -import numpy as np -from numcodecs import AsType, get_codec -from numcodecs.compat import ensure_bytes, ensure_ndarray_like - -from zarr.v2._storage.store import _prefix_to_attrs_key -from zarr.v2.attrs import Attributes -from zarr.v2.context import Context -from zarr.v2.errors import ArrayNotFoundError, ReadOnlyError, ArrayIndexError -from zarr.v2.indexing import ( - BasicIndexer, - CoordinateIndexer, - MaskIndexer, - OIndex, - OrthogonalIndexer, - VIndex, - BlockIndex, - BlockIndexer, - PartialChunkIterator, - check_fields, - check_no_multi_fields, - ensure_tuple, - err_too_many_indices, - is_contiguous_selection, - is_pure_fancy_indexing, - is_pure_orthogonal_indexing, - is_scalar, - pop_fields, -) -from zarr.v2.storage import ( - _prefix_to_array_key, - KVStore, - getsize, - listdir, - normalize_store_arg, -) -from zarr.v2.util import ( - ConstantMap, - UncompressedPartialReadBufferV3, - all_equal, - InfoReporter, - check_array_shape, - human_readable_size, - is_total_slice, - nolock, - normalize_chunks, - normalize_resize_args, - normalize_shape, - normalize_storage_path, - PartialReadBuffer, -) - -__all__ = ["Array"] - - -# noinspection PyUnresolvedReferences -class Array: - """Instantiate an array from an initialized store. - - Parameters - ---------- - store : MutableMapping - Array store, already initialized. - path : string, optional - Storage path. - read_only : bool, optional - True if array should be protected against modification. - chunk_store : MutableMapping, optional - Separate storage for chunks. If not provided, `store` will be used - for storage of both chunks and metadata. - synchronizer : object, optional - Array synchronizer. - cache_metadata : bool, optional - If True (default), array configuration metadata will be cached for the - lifetime of the object. If False, array metadata will be reloaded - prior to all data access and modification operations (may incur - overhead depending on storage and data access pattern). - cache_attrs : bool, optional - If True (default), user attributes will be cached for attribute read - operations. If False, user attributes are reloaded from the store prior - to all attribute read operations. - partial_decompress : bool, optional - If True and while the chunk_store is a FSStore and the compression used - is Blosc, when getting data from the array chunks will be partially - read and decompressed when possible. - - .. versionadded:: 2.7 - - write_empty_chunks : bool, optional - If True, all chunks will be stored regardless of their contents. If - False (default), each chunk is compared to the array's fill value prior - to storing. If a chunk is uniformly equal to the fill value, then that - chunk is not be stored, and the store entry for that chunk's key is - deleted. This setting enables sparser storage, as only chunks with - non-fill-value data are stored, at the expense of overhead associated - with checking the data of each chunk. - - .. versionadded:: 2.11 - - meta_array : array-like, optional - An array instance to use for determining arrays to create and return - to users. Use `numpy.empty(())` by default. - - .. versionadded:: 2.13 - """ - - def __init__( - self, - store: Any, # BaseStore not strictly required due to normalize_store_arg - path=None, - read_only=False, - chunk_store=None, - synchronizer=None, - cache_metadata=True, - cache_attrs=True, - partial_decompress=False, - write_empty_chunks=True, - meta_array=None, - ): - # N.B., expect at this point store is fully initialized with all - # configuration metadata fully specified and normalized - store = normalize_store_arg(store) - - if chunk_store is not None: - chunk_store = normalize_store_arg(chunk_store) - - self._store = store - self._chunk_store = chunk_store - self._transformed_chunk_store = None - self._path = normalize_storage_path(path) - if self._path: - self._key_prefix = self._path + "/" - else: - self._key_prefix = "" - self._read_only = bool(read_only) - self._synchronizer = synchronizer - self._cache_metadata = cache_metadata - self._is_view = False - self._partial_decompress = partial_decompress - self._write_empty_chunks = write_empty_chunks - if meta_array is not None: - self._meta_array = np.empty_like(meta_array, shape=()) - else: - self._meta_array = np.empty(()) - - # initialize metadata - self._load_metadata() - - # initialize attributes - akey = _prefix_to_attrs_key(self._store, self._key_prefix) - self._attrs = Attributes( - store, key=akey, read_only=read_only, synchronizer=synchronizer, cache=cache_attrs - ) - - # initialize info reporter - self._info_reporter = InfoReporter(self) - - # initialize indexing helpers - self._oindex = OIndex(self) - self._vindex = VIndex(self) - self._blocks = BlockIndex(self) - - def _load_metadata(self): - """(Re)load metadata from store.""" - if self._synchronizer is None: - self._load_metadata_nosync() - else: - mkey = _prefix_to_array_key(self._store, self._key_prefix) - with self._synchronizer[mkey]: - self._load_metadata_nosync() - - def _load_metadata_nosync(self): - try: - mkey = _prefix_to_array_key(self._store, self._key_prefix) - meta_bytes = self._store[mkey] - except KeyError: - raise ArrayNotFoundError(self._path) - else: - # decode and store metadata as instance members - meta = self._store._metadata_class.decode_array_metadata(meta_bytes) - self._meta = meta - self._shape = meta["shape"] - self._fill_value = meta["fill_value"] - dimension_separator = meta.get("dimension_separator", None) - - self._chunks = meta["chunks"] - self._dtype = meta["dtype"] - self._order = meta["order"] - if dimension_separator is None: - try: - dimension_separator = self._store._dimension_separator - except (AttributeError, KeyError): - pass - - # Fallback for any stores which do not choose a default - if dimension_separator is None: - dimension_separator = "." - - self._dimension_separator = dimension_separator - - # setup compressor - compressor = meta.get("compressor", None) - if compressor is None: - self._compressor = None - else: - self._compressor = get_codec(compressor) - - # setup filters - - filters = meta.get("filters", []) - - if filters: - filters = [get_codec(config) for config in filters] - self._filters = filters - - def _refresh_metadata(self): - if not self._cache_metadata: - self._load_metadata() - - def _refresh_metadata_nosync(self): - if not self._cache_metadata and not self._is_view: - self._load_metadata_nosync() - - def _flush_metadata_nosync(self): - if self._is_view: - raise PermissionError("operation not permitted for views") - - if self._compressor: - compressor_config = self._compressor.get_config() - else: - compressor_config = None - if self._filters: - filters_config = [f.get_config() for f in self._filters] - else: - filters_config = None - _compressor = compressor_config - meta = dict( - shape=self._shape, - compressor=_compressor, - fill_value=self._fill_value, - filters=filters_config, - ) - - meta.update( - dict( - chunks=self._chunks, - dtype=self._dtype, - order=self._order, - dimension_separator=self._dimension_separator, - ) - ) - mkey = _prefix_to_array_key(self._store, self._key_prefix) - self._store[mkey] = self._store._metadata_class.encode_array_metadata(meta) - - @property - def store(self): - """A MutableMapping providing the underlying storage for the array.""" - return self._store - - @property - def path(self): - """Storage path.""" - return self._path - - @property - def name(self): - """Array name following h5py convention.""" - if self.path: - # follow h5py convention: add leading slash - name = self.path - if name[0] != "/": - name = "/" + name - return name - return None - - @property - def basename(self): - """Final component of name.""" - if self.name is not None: - return self.name.split("/")[-1] - return None - - @property - def read_only(self): - """A boolean, True if modification operations are not permitted.""" - return self._read_only - - @read_only.setter - def read_only(self, value): - self._read_only = bool(value) - - @property - def chunk_store(self): - """A MutableMapping providing the underlying storage for array chunks.""" - if self._transformed_chunk_store is not None: - return self._transformed_chunk_store - elif self._chunk_store is not None: - return self._chunk_store - else: - return self._store - - @property - def shape(self): - """A tuple of integers describing the length of each dimension of - the array.""" - # N.B., shape may change if array is resized, hence need to refresh - # metadata - self._refresh_metadata() - return self._shape - - @shape.setter - def shape(self, value): - self.resize(value) - - @property - def chunks(self): - """A tuple of integers describing the length of each dimension of a - chunk of the array.""" - return self._chunks - - @property - def dtype(self): - """The NumPy data type.""" - return self._dtype - - @property - def compressor(self): - """Primary compression codec.""" - return self._compressor - - @property - def fill_value(self): - """A value used for uninitialized portions of the array.""" - return self._fill_value - - @fill_value.setter - def fill_value(self, new): - self._fill_value = new - self._flush_metadata_nosync() - - @property - def order(self): - """A string indicating the order in which bytes are arranged within - chunks of the array.""" - return self._order - - @property - def filters(self): - """One or more codecs used to transform data prior to compression.""" - return self._filters - - @property - def synchronizer(self): - """Object used to synchronize write access to the array.""" - return self._synchronizer - - @property - def attrs(self): - """A MutableMapping containing user-defined attributes. Note that - attribute values must be JSON serializable.""" - return self._attrs - - @property - def ndim(self): - """Number of dimensions.""" - return len(self._shape) - - @property - def _size(self): - return reduce(operator.mul, self._shape, 1) - - @property - def size(self): - """The total number of elements in the array.""" - # N.B., this property depends on shape, and shape may change if array - # is resized, hence need to refresh metadata - self._refresh_metadata() - return self._size - - @property - def itemsize(self): - """The size in bytes of each item in the array.""" - return self.dtype.itemsize - - @property - def _nbytes(self): - return self._size * self.itemsize - - @property - def nbytes(self): - """The total number of bytes that would be required to store the - array without compression.""" - # N.B., this property depends on shape, and shape may change if array - # is resized, hence need to refresh metadata - self._refresh_metadata() - return self._nbytes - - @property - def nbytes_stored(self): - """The total number of stored bytes of data for the array. This - includes storage required for configuration metadata and user - attributes.""" - m = getsize(self._store, self._path) - if self._chunk_store is None: - return m - else: - n = getsize(self._chunk_store, self._path) - if m < 0 or n < 0: - return -1 - else: - return m + n - - @property - def _cdata_shape(self): - if self._shape == (): - return (1,) - else: - return tuple(math.ceil(s / c) for s, c in zip(self._shape, self._chunks)) - - @property - def cdata_shape(self): - """A tuple of integers describing the number of chunks along each - dimension of the array.""" - self._refresh_metadata() - return self._cdata_shape - - @property - def _nchunks(self): - return reduce(operator.mul, self._cdata_shape, 1) - - @property - def nchunks(self): - """Total number of chunks.""" - self._refresh_metadata() - return self._nchunks - - @property - def nchunks_initialized(self): - """The number of chunks that have been initialized with some data.""" - - # key pattern for chunk keys - prog = re.compile(r"\.".join([r"\d+"] * min(1, self.ndim))) - - # count chunk keys - return sum(1 for k in listdir(self.chunk_store, self._path) if prog.match(k)) - - # backwards compatibility - initialized = nchunks_initialized - - @property - def is_view(self): - """A boolean, True if this array is a view on another array.""" - return self._is_view - - @property - def oindex(self): - """Shortcut for orthogonal (outer) indexing, see :func:`get_orthogonal_selection` and - :func:`set_orthogonal_selection` for documentation and examples.""" - return self._oindex - - @property - def vindex(self): - """Shortcut for vectorized (inner) indexing, see :func:`get_coordinate_selection`, - :func:`set_coordinate_selection`, :func:`get_mask_selection` and - :func:`set_mask_selection` for documentation and examples.""" - return self._vindex - - @property - def blocks(self): - """Shortcut for blocked chunked indexing, see :func:`get_block_selection` and - :func:`set_block_selection` for documentation and examples.""" - return self._blocks - - @property - def write_empty_chunks(self) -> bool: - """A Boolean, True if chunks composed of the array's fill value - will be stored. If False, such chunks will not be stored. - """ - return self._write_empty_chunks - - @property - def meta_array(self): - """An array-like instance to use for determining arrays to create and return - to users. - """ - return self._meta_array - - def __eq__(self, other): - return ( - isinstance(other, Array) - and self.store == other.store - and self.read_only == other.read_only - and self.path == other.path - and not self._is_view - # N.B., no need to compare other properties, should be covered by - # store comparison - ) - - def __array__(self, *args): - a = self[...] - if args: - a = a.astype(args[0]) - return a - - def islice(self, start=None, end=None): - """ - Yield a generator for iterating over the entire or parts of the - array. Uses a cache so chunks only have to be decompressed once. - - Parameters - ---------- - start : int, optional - Start index for the generator to start at. Defaults to 0. - end : int, optional - End index for the generator to stop at. Defaults to self.shape[0]. - - Yields - ------ - out : generator - A generator that can be used to iterate over the requested region - the array. - - Examples - -------- - Setup a 1-dimensional array:: - - >>> import zarr - >>> import numpy as np - >>> z = zarr.v2.array(np.arange(100)) - - Iterate over part of the array: - >>> for value in z.islice(25, 30): value; - 25 - 26 - 27 - 28 - 29 - """ - - if len(self.shape) == 0: - # Same error as numpy - raise TypeError("iteration over a 0-d array") - if start is None: - start = 0 - if end is None or end > self.shape[0]: - end = self.shape[0] - - if not isinstance(start, int) or start < 0: - raise ValueError("start must be a nonnegative integer") - - if not isinstance(end, int) or end < 0: - raise ValueError("end must be a nonnegative integer") - - # Avoid repeatedly decompressing chunks by iterating over the chunks - # in the first dimension. - chunk_size = self.chunks[0] - chunk = None - for j in range(start, end): - if j % chunk_size == 0: - chunk = self[j : j + chunk_size] - # init chunk if we start offset of chunk borders - elif chunk is None: - chunk_start = j - j % chunk_size - chunk_end = chunk_start + chunk_size - chunk = self[chunk_start:chunk_end] - yield chunk[j % chunk_size] - - def __iter__(self): - return self.islice() - - def __len__(self): - if self.shape: - return self.shape[0] - else: - # 0-dimensional array, same error message as numpy - raise TypeError("len() of unsized object") - - def __getitem__(self, selection): - """Retrieve data for an item or region of the array. - - Parameters - ---------- - selection : tuple - An integer index or slice or tuple of int/slice objects specifying the - requested item or region for each dimension of the array. - - Returns - ------- - out : ndarray - A NumPy array containing the data for the requested region. - - Examples - -------- - Setup a 1-dimensional array:: - - >>> import zarr - >>> import numpy as np - >>> z = zarr.v2.array(np.arange(100)) - - Retrieve a single item:: - - >>> z[5] - 5 - - Retrieve a region via slicing:: - - >>> z[:5] - array([0, 1, 2, 3, 4]) - >>> z[-5:] - array([95, 96, 97, 98, 99]) - >>> z[5:10] - array([5, 6, 7, 8, 9]) - >>> z[5:10:2] - array([5, 7, 9]) - >>> z[::2] - array([ 0, 2, 4, ..., 94, 96, 98]) - - Load the entire array into memory:: - - >>> z[...] - array([ 0, 1, 2, ..., 97, 98, 99]) - - Setup a 2-dimensional array:: - - >>> z = zarr.v2.array(np.arange(100).reshape(10, 10)) - - Retrieve an item:: - - >>> z[2, 2] - 22 - - Retrieve a region via slicing:: - - >>> z[1:3, 1:3] - array([[11, 12], - [21, 22]]) - >>> z[1:3, :] - array([[10, 11, 12, 13, 14, 15, 16, 17, 18, 19], - [20, 21, 22, 23, 24, 25, 26, 27, 28, 29]]) - >>> z[:, 1:3] - array([[ 1, 2], - [11, 12], - [21, 22], - [31, 32], - [41, 42], - [51, 52], - [61, 62], - [71, 72], - [81, 82], - [91, 92]]) - >>> z[0:5:2, 0:5:2] - array([[ 0, 2, 4], - [20, 22, 24], - [40, 42, 44]]) - >>> z[::2, ::2] - array([[ 0, 2, 4, 6, 8], - [20, 22, 24, 26, 28], - [40, 42, 44, 46, 48], - [60, 62, 64, 66, 68], - [80, 82, 84, 86, 88]]) - - Load the entire array into memory:: - - >>> z[...] - array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - [10, 11, 12, 13, 14, 15, 16, 17, 18, 19], - [20, 21, 22, 23, 24, 25, 26, 27, 28, 29], - [30, 31, 32, 33, 34, 35, 36, 37, 38, 39], - [40, 41, 42, 43, 44, 45, 46, 47, 48, 49], - [50, 51, 52, 53, 54, 55, 56, 57, 58, 59], - [60, 61, 62, 63, 64, 65, 66, 67, 68, 69], - [70, 71, 72, 73, 74, 75, 76, 77, 78, 79], - [80, 81, 82, 83, 84, 85, 86, 87, 88, 89], - [90, 91, 92, 93, 94, 95, 96, 97, 98, 99]]) - - For arrays with a structured dtype, specific fields can be retrieved, e.g.:: - - >>> a = np.array([(b'aaa', 1, 4.2), - ... (b'bbb', 2, 8.4), - ... (b'ccc', 3, 12.6)], - ... dtype=[('foo', 'S3'), ('bar', 'i4'), ('baz', 'f8')]) - >>> z = zarr.v2.array(a) - >>> z['foo'] - array([b'aaa', b'bbb', b'ccc'], - dtype='|S3') - - Notes - ----- - Slices with step > 1 are supported, but slices with negative step are not. - - Currently the implementation for __getitem__ is provided by - :func:`vindex` if the indexing is pure fancy indexing (ie a - broadcast-compatible tuple of integer array indices), or by - :func:`set_basic_selection` otherwise. - - Effectively, this means that the following indexing modes are supported: - - - integer indexing - - slice indexing - - mixed slice and integer indexing - - boolean indexing - - fancy indexing (vectorized list of integers) - - For specific indexing options including outer indexing, see the - methods listed under See Also. - - See Also - -------- - get_basic_selection, set_basic_selection, get_mask_selection, set_mask_selection, - get_coordinate_selection, set_coordinate_selection, get_orthogonal_selection, - set_orthogonal_selection, get_block_selection, set_block_selection, - vindex, oindex, blocks, __setitem__ - - """ - fields, pure_selection = pop_fields(selection) - if is_pure_fancy_indexing(pure_selection, self.ndim): - result = self.vindex[selection] - elif is_pure_orthogonal_indexing(pure_selection, self.ndim): - result = self.get_orthogonal_selection(pure_selection, fields=fields) - else: - result = self.get_basic_selection(pure_selection, fields=fields) - return result - - def get_basic_selection(self, selection=Ellipsis, out=None, fields=None): - """Retrieve data for an item or region of the array. - - Parameters - ---------- - selection : tuple - A tuple specifying the requested item or region for each dimension of the - array. May be any combination of int and/or slice for multidimensional arrays. - out : ndarray, optional - If given, load the selected data directly into this array. - fields : str or sequence of str, optional - For arrays with a structured dtype, one or more fields can be specified to - extract data for. - - Returns - ------- - out : ndarray - A NumPy array containing the data for the requested region. - - Examples - -------- - Setup a 1-dimensional array:: - - >>> import zarr - >>> import numpy as np - >>> z = zarr.v2.array(np.arange(100)) - - Retrieve a single item:: - - >>> z.get_basic_selection(5) - 5 - - Retrieve a region via slicing:: - - >>> z.get_basic_selection(slice(5)) - array([0, 1, 2, 3, 4]) - >>> z.get_basic_selection(slice(-5, None)) - array([95, 96, 97, 98, 99]) - >>> z.get_basic_selection(slice(5, 10)) - array([5, 6, 7, 8, 9]) - >>> z.get_basic_selection(slice(5, 10, 2)) - array([5, 7, 9]) - >>> z.get_basic_selection(slice(None, None, 2)) - array([ 0, 2, 4, ..., 94, 96, 98]) - - Setup a 2-dimensional array:: - - >>> z = zarr.v2.array(np.arange(100).reshape(10, 10)) - - Retrieve an item:: - - >>> z.get_basic_selection((2, 2)) - 22 - - Retrieve a region via slicing:: - - >>> z.get_basic_selection((slice(1, 3), slice(1, 3))) - array([[11, 12], - [21, 22]]) - >>> z.get_basic_selection((slice(1, 3), slice(None))) - array([[10, 11, 12, 13, 14, 15, 16, 17, 18, 19], - [20, 21, 22, 23, 24, 25, 26, 27, 28, 29]]) - >>> z.get_basic_selection((slice(None), slice(1, 3))) - array([[ 1, 2], - [11, 12], - [21, 22], - [31, 32], - [41, 42], - [51, 52], - [61, 62], - [71, 72], - [81, 82], - [91, 92]]) - >>> z.get_basic_selection((slice(0, 5, 2), slice(0, 5, 2))) - array([[ 0, 2, 4], - [20, 22, 24], - [40, 42, 44]]) - >>> z.get_basic_selection((slice(None, None, 2), slice(None, None, 2))) - array([[ 0, 2, 4, 6, 8], - [20, 22, 24, 26, 28], - [40, 42, 44, 46, 48], - [60, 62, 64, 66, 68], - [80, 82, 84, 86, 88]]) - - For arrays with a structured dtype, specific fields can be retrieved, e.g.:: - - >>> a = np.array([(b'aaa', 1, 4.2), - ... (b'bbb', 2, 8.4), - ... (b'ccc', 3, 12.6)], - ... dtype=[('foo', 'S3'), ('bar', 'i4'), ('baz', 'f8')]) - >>> z = zarr.v2.array(a) - >>> z.get_basic_selection(slice(2), fields='foo') - array([b'aaa', b'bbb'], - dtype='|S3') - - Notes - ----- - Slices with step > 1 are supported, but slices with negative step are not. - - Currently this method provides the implementation for accessing data via the - square bracket notation (__getitem__). See :func:`__getitem__` for examples - using the alternative notation. - - See Also - -------- - set_basic_selection, get_mask_selection, set_mask_selection, - get_coordinate_selection, set_coordinate_selection, get_orthogonal_selection, - set_orthogonal_selection, get_block_selection, set_block_selection, - vindex, oindex, blocks, __getitem__, __setitem__ - - """ - - # refresh metadata - if not self._cache_metadata: - self._load_metadata() - - # check args - check_fields(fields, self._dtype) - - # handle zero-dimensional arrays - if self._shape == (): - return self._get_basic_selection_zd(selection=selection, out=out, fields=fields) - else: - return self._get_basic_selection_nd(selection=selection, out=out, fields=fields) - - def _get_basic_selection_zd(self, selection, out=None, fields=None): - # special case basic selection for zero-dimensional array - - # check selection is valid - selection = ensure_tuple(selection) - if selection not in ((), (Ellipsis,)): - err_too_many_indices(selection, ()) - - try: - # obtain encoded data for chunk - ckey = self._chunk_key((0,)) - cdata = self.chunk_store[ckey] - - except KeyError: - # chunk not initialized - chunk = np.zeros_like(self._meta_array, shape=(), dtype=self._dtype) - if self._fill_value is not None: - chunk.fill(self._fill_value) - - else: - chunk = self._decode_chunk(cdata) - - # handle fields - if fields: - chunk = chunk[fields] - - # handle selection of the scalar value via empty tuple - if out is None: - out = chunk[selection] - else: - out[selection] = chunk[selection] - - return out - - def _get_basic_selection_nd(self, selection, out=None, fields=None): - # implementation of basic selection for array with at least one dimension - - # setup indexer - indexer = BasicIndexer(selection, self) - - return self._get_selection(indexer=indexer, out=out, fields=fields) - - def get_orthogonal_selection(self, selection, out=None, fields=None): - """Retrieve data by making a selection for each dimension of the array. For - example, if an array has 2 dimensions, allows selecting specific rows and/or - columns. The selection for each dimension can be either an integer (indexing a - single item), a slice, an array of integers, or a Boolean array where True - values indicate a selection. - - Parameters - ---------- - selection : tuple - A selection for each dimension of the array. May be any combination of int, - slice, integer array or Boolean array. - out : ndarray, optional - If given, load the selected data directly into this array. - fields : str or sequence of str, optional - For arrays with a structured dtype, one or more fields can be specified to - extract data for. - - Returns - ------- - out : ndarray - A NumPy array containing the data for the requested selection. - - Examples - -------- - Setup a 2-dimensional array:: - - >>> import zarr - >>> import numpy as np - >>> z = zarr.v2.array(np.arange(100).reshape(10, 10)) - - Retrieve rows and columns via any combination of int, slice, integer array and/or - Boolean array:: - - >>> z.get_orthogonal_selection(([1, 4], slice(None))) - array([[10, 11, 12, 13, 14, 15, 16, 17, 18, 19], - [40, 41, 42, 43, 44, 45, 46, 47, 48, 49]]) - >>> z.get_orthogonal_selection((slice(None), [1, 4])) - array([[ 1, 4], - [11, 14], - [21, 24], - [31, 34], - [41, 44], - [51, 54], - [61, 64], - [71, 74], - [81, 84], - [91, 94]]) - >>> z.get_orthogonal_selection(([1, 4], [1, 4])) - array([[11, 14], - [41, 44]]) - >>> sel = np.zeros(z.shape[0], dtype=bool) - >>> sel[1] = True - >>> sel[4] = True - >>> z.get_orthogonal_selection((sel, sel)) - array([[11, 14], - [41, 44]]) - - For convenience, the orthogonal selection functionality is also available via the - `oindex` property, e.g.:: - - >>> z.oindex[[1, 4], :] - array([[10, 11, 12, 13, 14, 15, 16, 17, 18, 19], - [40, 41, 42, 43, 44, 45, 46, 47, 48, 49]]) - >>> z.oindex[:, [1, 4]] - array([[ 1, 4], - [11, 14], - [21, 24], - [31, 34], - [41, 44], - [51, 54], - [61, 64], - [71, 74], - [81, 84], - [91, 94]]) - >>> z.oindex[[1, 4], [1, 4]] - array([[11, 14], - [41, 44]]) - >>> sel = np.zeros(z.shape[0], dtype=bool) - >>> sel[1] = True - >>> sel[4] = True - >>> z.oindex[sel, sel] - array([[11, 14], - [41, 44]]) - - Notes - ----- - Orthogonal indexing is also known as outer indexing. - - Slices with step > 1 are supported, but slices with negative step are not. - - See Also - -------- - get_basic_selection, set_basic_selection, get_mask_selection, set_mask_selection, - get_coordinate_selection, set_coordinate_selection, set_orthogonal_selection, - get_block_selection, set_block_selection, - vindex, oindex, blocks, __getitem__, __setitem__ - - """ - - # refresh metadata - if not self._cache_metadata: - self._load_metadata() - - # check args - check_fields(fields, self._dtype) - - # setup indexer - indexer = OrthogonalIndexer(selection, self) - - return self._get_selection(indexer=indexer, out=out, fields=fields) - - def get_coordinate_selection(self, selection, out=None, fields=None): - """Retrieve a selection of individual items, by providing the indices - (coordinates) for each selected item. - - Parameters - ---------- - selection : tuple - An integer (coordinate) array for each dimension of the array. - out : ndarray, optional - If given, load the selected data directly into this array. - fields : str or sequence of str, optional - For arrays with a structured dtype, one or more fields can be specified to - extract data for. - - Returns - ------- - out : ndarray - A NumPy array containing the data for the requested selection. - - Examples - -------- - Setup a 2-dimensional array:: - - >>> import zarr - >>> import numpy as np - >>> z = zarr.v2.array(np.arange(100).reshape(10, 10)) - - Retrieve items by specifying their coordinates:: - - >>> z.get_coordinate_selection(([1, 4], [1, 4])) - array([11, 44]) - - For convenience, the coordinate selection functionality is also available via the - `vindex` property, e.g.:: - - >>> z.vindex[[1, 4], [1, 4]] - array([11, 44]) - - Notes - ----- - Coordinate indexing is also known as point selection, and is a form of vectorized - or inner indexing. - - Slices are not supported. Coordinate arrays must be provided for all dimensions - of the array. - - Coordinate arrays may be multidimensional, in which case the output array will - also be multidimensional. Coordinate arrays are broadcast against each other - before being applied. The shape of the output will be the same as the shape of - each coordinate array after broadcasting. - - See Also - -------- - get_basic_selection, set_basic_selection, get_mask_selection, set_mask_selection, - get_orthogonal_selection, set_orthogonal_selection, set_coordinate_selection, - get_block_selection, set_block_selection, - vindex, oindex, blocks, __getitem__, __setitem__ - - """ - - # refresh metadata - if not self._cache_metadata: - self._load_metadata() - - # check args - check_fields(fields, self._dtype) - - # setup indexer - indexer = CoordinateIndexer(selection, self) - - # handle output - need to flatten - if out is not None: - out = out.reshape(-1) - - out = self._get_selection(indexer=indexer, out=out, fields=fields) - - # restore shape - out = out.reshape(indexer.sel_shape) - - return out - - def get_block_selection(self, selection, out=None, fields=None): - """Retrieve a selection of individual chunk blocks, by providing the indices - (coordinates) for each chunk block. - - Parameters - ---------- - selection : tuple - An integer (coordinate) or slice for each dimension of the array. - out : ndarray, optional - If given, load the selected data directly into this array. - fields : str or sequence of str, optional - For arrays with a structured dtype, one or more fields can be specified to - extract data for. - - Returns - ------- - out : ndarray - A NumPy array containing the data for the requested selection. - - Examples - -------- - Setup a 2-dimensional array:: - - >>> import zarr - >>> import numpy as np - >>> z = zarr.v2.array(np.arange(100).reshape(10, 10), chunks=(3, 3)) - - Retrieve items by specifying their block coordinates:: - - >>> z.get_block_selection((1, slice(None))) - array([[30, 31, 32, 33, 34, 35, 36, 37, 38, 39], - [40, 41, 42, 43, 44, 45, 46, 47, 48, 49], - [50, 51, 52, 53, 54, 55, 56, 57, 58, 59]]) - - Which is equivalent to:: - - >>> z[3:6, :] - array([[30, 31, 32, 33, 34, 35, 36, 37, 38, 39], - [40, 41, 42, 43, 44, 45, 46, 47, 48, 49], - [50, 51, 52, 53, 54, 55, 56, 57, 58, 59]]) - - For convenience, the block selection functionality is also available via the - `blocks` property, e.g.:: - - >>> z.blocks[1] - array([[30, 31, 32, 33, 34, 35, 36, 37, 38, 39], - [40, 41, 42, 43, 44, 45, 46, 47, 48, 49], - [50, 51, 52, 53, 54, 55, 56, 57, 58, 59]]) - - Notes - ----- - Block indexing is a convenience indexing method to work on individual chunks - with chunk index slicing. It has the same concept as Dask's `Array.blocks` - indexing. - - Slices are supported. However, only with a step size of one. - - Block index arrays may be multidimensional to index multidimensional arrays. - For example:: - - >>> z.blocks[0, 1:3] - array([[ 3, 4, 5, 6, 7, 8], - [13, 14, 15, 16, 17, 18], - [23, 24, 25, 26, 27, 28]]) - - See Also - -------- - get_basic_selection, set_basic_selection, get_mask_selection, set_mask_selection, - get_orthogonal_selection, set_orthogonal_selection, get_coordinate_selection, - set_coordinate_selection, set_block_selection, - vindex, oindex, blocks, __getitem__, __setitem__ - - """ - if not self._cache_metadata: - self._load_metadata() - - # check args - check_fields(fields, self._dtype) - - # setup indexer - indexer = BlockIndexer(selection, self) - - return self._get_selection(indexer=indexer, out=out, fields=fields) - - def get_mask_selection(self, selection, out=None, fields=None): - """Retrieve a selection of individual items, by providing a Boolean array of the - same shape as the array against which the selection is being made, where True - values indicate a selected item. - - Parameters - ---------- - selection : ndarray, bool - A Boolean array of the same shape as the array against which the selection is - being made. - out : ndarray, optional - If given, load the selected data directly into this array. - fields : str or sequence of str, optional - For arrays with a structured dtype, one or more fields can be specified to - extract data for. - - Returns - ------- - out : ndarray - A NumPy array containing the data for the requested selection. - - Examples - -------- - Setup a 2-dimensional array:: - - >>> import zarr - >>> import numpy as np - >>> z = zarr.v2.array(np.arange(100).reshape(10, 10)) - - Retrieve items by specifying a mask:: - - >>> sel = np.zeros_like(z, dtype=bool) - >>> sel[1, 1] = True - >>> sel[4, 4] = True - >>> z.get_mask_selection(sel) - array([11, 44]) - - For convenience, the mask selection functionality is also available via the - `vindex` property, e.g.:: - - >>> z.vindex[sel] - array([11, 44]) - - Notes - ----- - Mask indexing is a form of vectorized or inner indexing, and is equivalent to - coordinate indexing. Internally the mask array is converted to coordinate - arrays by calling `np.nonzero`. - - See Also - -------- - get_basic_selection, set_basic_selection, set_mask_selection, - get_orthogonal_selection, set_orthogonal_selection, get_coordinate_selection, - set_coordinate_selection, get_block_selection, set_block_selection, - vindex, oindex, blocks, __getitem__, __setitem__ - """ - - # refresh metadata - if not self._cache_metadata: - self._load_metadata() - - # check args - check_fields(fields, self._dtype) - - # setup indexer - indexer = MaskIndexer(selection, self) - - return self._get_selection(indexer=indexer, out=out, fields=fields) - - def _get_selection(self, indexer, out=None, fields=None): - # We iterate over all chunks which overlap the selection and thus contain data - # that needs to be extracted. Each chunk is processed in turn, extracting the - # necessary data and storing into the correct location in the output array. - - # N.B., it is an important optimisation that we only visit chunks which overlap - # the selection. This minimises the number of iterations in the main for loop. - - # check fields are sensible - out_dtype = check_fields(fields, self._dtype) - - # determine output shape - out_shape = indexer.shape - - # setup output array - if out is None: - out = np.empty_like( - self._meta_array, shape=out_shape, dtype=out_dtype, order=self._order - ) - else: - check_array_shape("out", out, out_shape) - - # iterate over chunks - - if math.prod(out_shape) > 0: - # allow storage to get multiple items at once - lchunk_coords, lchunk_selection, lout_selection = zip(*indexer) - self._chunk_getitems( - lchunk_coords, - lchunk_selection, - out, - lout_selection, - drop_axes=indexer.drop_axes, - fields=fields, - ) - if out.shape: - return out - else: - return out[()] - - def __setitem__(self, selection, value): - """Modify data for an item or region of the array. - - Parameters - ---------- - selection : tuple - An integer index or slice or tuple of int/slice specifying the requested - region for each dimension of the array. - value : scalar or array-like - Value to be stored into the array. - - Examples - -------- - Setup a 1-dimensional array:: - - >>> import zarr - >>> z = zarr.v2.zeros(100, dtype=int) - - Set all array elements to the same scalar value:: - - >>> z[...] = 42 - >>> z[...] - array([42, 42, 42, ..., 42, 42, 42]) - - Set a portion of the array:: - - >>> z[:10] = np.arange(10) - >>> z[-10:] = np.arange(10)[::-1] - >>> z[...] - array([ 0, 1, 2, ..., 2, 1, 0]) - - Setup a 2-dimensional array:: - - >>> z = zarr.v2.zeros((5, 5), dtype=int) - - Set all array elements to the same scalar value:: - - >>> z[...] = 42 - - Set a portion of the array:: - - >>> z[0, :] = np.arange(z.shape[1]) - >>> z[:, 0] = np.arange(z.shape[0]) - >>> z[...] - array([[ 0, 1, 2, 3, 4], - [ 1, 42, 42, 42, 42], - [ 2, 42, 42, 42, 42], - [ 3, 42, 42, 42, 42], - [ 4, 42, 42, 42, 42]]) - - For arrays with a structured dtype, specific fields can be modified, e.g.:: - - >>> a = np.array([(b'aaa', 1, 4.2), - ... (b'bbb', 2, 8.4), - ... (b'ccc', 3, 12.6)], - ... dtype=[('foo', 'S3'), ('bar', 'i4'), ('baz', 'f8')]) - >>> z = zarr.v2.array(a) - >>> z['foo'] = b'zzz' - >>> z[...] - array([(b'zzz', 1, 4.2), (b'zzz', 2, 8.4), (b'zzz', 3, 12.6)], - dtype=[('foo', 'S3'), ('bar', ' 1 are supported, but slices with negative step are not. - - Currently the implementation for __setitem__ is provided by - :func:`vindex` if the indexing is pure fancy indexing (ie a - broadcast-compatible tuple of integer array indices), or by - :func:`set_basic_selection` otherwise. - - Effectively, this means that the following indexing modes are supported: - - - integer indexing - - slice indexing - - mixed slice and integer indexing - - boolean indexing - - fancy indexing (vectorized list of integers) - - For specific indexing options including outer indexing, see the - methods listed under See Also. - - See Also - -------- - get_basic_selection, set_basic_selection, get_mask_selection, set_mask_selection, - get_coordinate_selection, set_coordinate_selection, get_orthogonal_selection, - set_orthogonal_selection, get_block_selection, set_block_selection, - vindex, oindex, blocks, __getitem__ - - """ - fields, pure_selection = pop_fields(selection) - if is_pure_fancy_indexing(pure_selection, self.ndim): - self.vindex[selection] = value - elif is_pure_orthogonal_indexing(pure_selection, self.ndim): - self.set_orthogonal_selection(pure_selection, value, fields=fields) - else: - self.set_basic_selection(pure_selection, value, fields=fields) - - def set_basic_selection(self, selection, value, fields=None): - """Modify data for an item or region of the array. - - Parameters - ---------- - selection : tuple - An integer index or slice or tuple of int/slice specifying the requested - region for each dimension of the array. - value : scalar or array-like - Value to be stored into the array. - fields : str or sequence of str, optional - For arrays with a structured dtype, one or more fields can be specified to set - data for. - - Examples - -------- - Setup a 1-dimensional array:: - - >>> import zarr - >>> import numpy as np - >>> z = zarr.v2.zeros(100, dtype=int) - - Set all array elements to the same scalar value:: - - >>> z.set_basic_selection(..., 42) - >>> z[...] - array([42, 42, 42, ..., 42, 42, 42]) - - Set a portion of the array:: - - >>> z.set_basic_selection(slice(10), np.arange(10)) - >>> z.set_basic_selection(slice(-10, None), np.arange(10)[::-1]) - >>> z[...] - array([ 0, 1, 2, ..., 2, 1, 0]) - - Setup a 2-dimensional array:: - - >>> z = zarr.v2.zeros((5, 5), dtype=int) - - Set all array elements to the same scalar value:: - - >>> z.set_basic_selection(..., 42) - - Set a portion of the array:: - - >>> z.set_basic_selection((0, slice(None)), np.arange(z.shape[1])) - >>> z.set_basic_selection((slice(None), 0), np.arange(z.shape[0])) - >>> z[...] - array([[ 0, 1, 2, 3, 4], - [ 1, 42, 42, 42, 42], - [ 2, 42, 42, 42, 42], - [ 3, 42, 42, 42, 42], - [ 4, 42, 42, 42, 42]]) - - For arrays with a structured dtype, the `fields` parameter can be used to set - data for a specific field, e.g.:: - - >>> a = np.array([(b'aaa', 1, 4.2), - ... (b'bbb', 2, 8.4), - ... (b'ccc', 3, 12.6)], - ... dtype=[('foo', 'S3'), ('bar', 'i4'), ('baz', 'f8')]) - >>> z = zarr.v2.array(a) - >>> z.set_basic_selection(slice(0, 2), b'zzz', fields='foo') - >>> z[:] - array([(b'zzz', 1, 4.2), (b'zzz', 2, 8.4), (b'ccc', 3, 12.6)], - dtype=[('foo', 'S3'), ('bar', '>> import zarr - >>> import numpy as np - >>> z = zarr.v2.zeros((5, 5), dtype=int) - - Set data for a selection of rows:: - - >>> z.set_orthogonal_selection(([1, 4], slice(None)), 1) - >>> z[...] - array([[0, 0, 0, 0, 0], - [1, 1, 1, 1, 1], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [1, 1, 1, 1, 1]]) - - Set data for a selection of columns:: - - >>> z.set_orthogonal_selection((slice(None), [1, 4]), 2) - >>> z[...] - array([[0, 2, 0, 0, 2], - [1, 2, 1, 1, 2], - [0, 2, 0, 0, 2], - [0, 2, 0, 0, 2], - [1, 2, 1, 1, 2]]) - - Set data for a selection of rows and columns:: - - >>> z.set_orthogonal_selection(([1, 4], [1, 4]), 3) - >>> z[...] - array([[0, 2, 0, 0, 2], - [1, 3, 1, 1, 3], - [0, 2, 0, 0, 2], - [0, 2, 0, 0, 2], - [1, 3, 1, 1, 3]]) - - For convenience, this functionality is also available via the `oindex` property. - E.g.:: - - >>> z.oindex[[1, 4], [1, 4]] = 4 - >>> z[...] - array([[0, 2, 0, 0, 2], - [1, 4, 1, 1, 4], - [0, 2, 0, 0, 2], - [0, 2, 0, 0, 2], - [1, 4, 1, 1, 4]]) - - Notes - ----- - Orthogonal indexing is also known as outer indexing. - - Slices with step > 1 are supported, but slices with negative step are not. - - See Also - -------- - get_basic_selection, set_basic_selection, get_mask_selection, set_mask_selection, - get_coordinate_selection, set_coordinate_selection, get_orthogonal_selection, - get_block_selection, set_block_selection, - vindex, oindex, blocks, __getitem__, __setitem__ - - """ - - # guard conditions - if self._read_only: - raise ReadOnlyError - - # refresh metadata - if not self._cache_metadata: - self._load_metadata_nosync() - - # setup indexer - indexer = OrthogonalIndexer(selection, self) - - self._set_selection(indexer, value, fields=fields) - - def set_coordinate_selection(self, selection, value, fields=None): - """Modify a selection of individual items, by providing the indices (coordinates) - for each item to be modified. - - Parameters - ---------- - selection : tuple - An integer (coordinate) array for each dimension of the array. - value : scalar or array-like - Value to be stored into the array. - fields : str or sequence of str, optional - For arrays with a structured dtype, one or more fields can be specified to set - data for. - - Examples - -------- - Setup a 2-dimensional array:: - - >>> import zarr - >>> import numpy as np - >>> z = zarr.v2.zeros((5, 5), dtype=int) - - Set data for a selection of items:: - - >>> z.set_coordinate_selection(([1, 4], [1, 4]), 1) - >>> z[...] - array([[0, 0, 0, 0, 0], - [0, 1, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 1]]) - - For convenience, this functionality is also available via the `vindex` property. - E.g.:: - - >>> z.vindex[[1, 4], [1, 4]] = 2 - >>> z[...] - array([[0, 0, 0, 0, 0], - [0, 2, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 2]]) - - Notes - ----- - Coordinate indexing is also known as point selection, and is a form of vectorized - or inner indexing. - - Slices are not supported. Coordinate arrays must be provided for all dimensions - of the array. - - See Also - -------- - get_basic_selection, set_basic_selection, get_mask_selection, set_mask_selection, - get_orthogonal_selection, set_orthogonal_selection, get_coordinate_selection, - get_block_selection, set_block_selection, - vindex, oindex, blocks, __getitem__, __setitem__ - - """ - - # guard conditions - if self._read_only: - raise ReadOnlyError - - # refresh metadata - if not self._cache_metadata: - self._load_metadata_nosync() - - # setup indexer - indexer = CoordinateIndexer(selection, self) - - # handle value - need ndarray-like flatten value - if not is_scalar(value, self._dtype): - try: - value = ensure_ndarray_like(value) - except TypeError: - # Handle types like `list` or `tuple` - value = np.array(value, like=self._meta_array) - if hasattr(value, "shape") and len(value.shape) > 1: - value = value.reshape(-1) - - self._set_selection(indexer, value, fields=fields) - - def set_block_selection(self, selection, value, fields=None): - """Modify a selection of individual blocks, by providing the chunk indices - (coordinates) for each block to be modified. - - Parameters - ---------- - selection : tuple - An integer (coordinate) or slice for each dimension of the array. - value : scalar or array-like - Value to be stored into the array. - fields : str or sequence of str, optional - For arrays with a structured dtype, one or more fields can be specified to set - data for. - - Examples - -------- - Set up a 2-dimensional array:: - - >>> import zarr - >>> import numpy as np - >>> z = zarr.v2.zeros((6, 6), dtype=int, chunks=2) - - Set data for a selection of items:: - - >>> z.set_block_selection((1, 0), 1) - >>> z[...] - array([[0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [1, 1, 0, 0, 0, 0], - [1, 1, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0]]) - - For convenience, this functionality is also available via the `blocks` property. - E.g.:: - - >>> z.blocks[2, 1] = 4 - >>> z[...] - array([[0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [1, 1, 0, 0, 0, 0], - [1, 1, 0, 0, 0, 0], - [0, 0, 4, 4, 0, 0], - [0, 0, 4, 4, 0, 0]]) - - >>> z.blocks[:, 2] = 7 - >>> z[...] - array([[0, 0, 0, 0, 7, 7], - [0, 0, 0, 0, 7, 7], - [1, 1, 0, 0, 7, 7], - [1, 1, 0, 0, 7, 7], - [0, 0, 4, 4, 7, 7], - [0, 0, 4, 4, 7, 7]]) - - Notes - ----- - Block indexing is a convenience indexing method to work on individual chunks - with chunk index slicing. It has the same concept as Dask's `Array.blocks` - indexing. - - Slices are supported. However, only with a step size of one. - - See Also - -------- - get_basic_selection, set_basic_selection, get_mask_selection, set_mask_selection, - get_orthogonal_selection, set_orthogonal_selection, get_coordinate_selection, - get_block_selection, set_block_selection, - vindex, oindex, blocks, __getitem__, __setitem__ - - """ - # guard conditions - if self._read_only: - raise ReadOnlyError - - # refresh metadata - if not self._cache_metadata: - self._load_metadata_nosync() - - # setup indexer - indexer = BlockIndexer(selection, self) - - self._set_selection(indexer, value, fields=fields) - - def set_mask_selection(self, selection, value, fields=None): - """Modify a selection of individual items, by providing a Boolean array of the - same shape as the array against which the selection is being made, where True - values indicate a selected item. - - Parameters - ---------- - selection : ndarray, bool - A Boolean array of the same shape as the array against which the selection is - being made. - value : scalar or array-like - Value to be stored into the array. - fields : str or sequence of str, optional - For arrays with a structured dtype, one or more fields can be specified to set - data for. - - Examples - -------- - Setup a 2-dimensional array:: - - >>> import zarr - >>> import numpy as np - >>> z = zarr.v2.zeros((5, 5), dtype=int) - - Set data for a selection of items:: - - >>> sel = np.zeros_like(z, dtype=bool) - >>> sel[1, 1] = True - >>> sel[4, 4] = True - >>> z.set_mask_selection(sel, 1) - >>> z[...] - array([[0, 0, 0, 0, 0], - [0, 1, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 1]]) - - For convenience, this functionality is also available via the `vindex` property. - E.g.:: - - >>> z.vindex[sel] = 2 - >>> z[...] - array([[0, 0, 0, 0, 0], - [0, 2, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 2]]) - - Notes - ----- - Mask indexing is a form of vectorized or inner indexing, and is equivalent to - coordinate indexing. Internally the mask array is converted to coordinate - arrays by calling `np.nonzero`. - - See Also - -------- - get_basic_selection, set_basic_selection, get_mask_selection, - get_orthogonal_selection, set_orthogonal_selection, get_coordinate_selection, - set_coordinate_selection, get_block_selection, set_block_selection, - vindex, oindex, blocks, __getitem__, __setitem__ - - """ - - # guard conditions - if self._read_only: - raise ReadOnlyError - - # refresh metadata - if not self._cache_metadata: - self._load_metadata_nosync() - - # setup indexer - indexer = MaskIndexer(selection, self) - - self._set_selection(indexer, value, fields=fields) - - def _set_basic_selection_zd(self, selection, value, fields=None): - # special case __setitem__ for zero-dimensional array - - # check selection is valid - selection = ensure_tuple(selection) - if selection not in ((), (Ellipsis,)): - err_too_many_indices(selection, self._shape) - - # check fields - check_fields(fields, self._dtype) - fields = check_no_multi_fields(fields) - - # obtain key for chunk - ckey = self._chunk_key((0,)) - - # setup chunk - try: - # obtain compressed data for chunk - cdata = self.chunk_store[ckey] - - except KeyError: - # chunk not initialized - chunk = np.zeros_like(self._meta_array, shape=(), dtype=self._dtype) - if self._fill_value is not None: - chunk.fill(self._fill_value) - - else: - # decode chunk - chunk = self._decode_chunk(cdata).copy() - - # set value - if fields: - chunk[fields][selection] = value - else: - chunk[selection] = value - - # remove chunk if write_empty_chunks is false and it only contains the fill value - if (not self.write_empty_chunks) and all_equal(self.fill_value, chunk): - try: - del self.chunk_store[ckey] - return - except Exception: # pragma: no cover - # deleting failed, fallback to overwriting - pass - else: - # encode and store - cdata = self._encode_chunk(chunk) - self.chunk_store[ckey] = cdata - - def _set_basic_selection_nd(self, selection, value, fields=None): - # implementation of __setitem__ for array with at least one dimension - - # setup indexer - indexer = BasicIndexer(selection, self) - - self._set_selection(indexer, value, fields=fields) - - def _set_selection(self, indexer, value, fields=None): - # We iterate over all chunks which overlap the selection and thus contain data - # that needs to be replaced. Each chunk is processed in turn, extracting the - # necessary data from the value array and storing into the chunk array. - - # N.B., it is an important optimisation that we only visit chunks which overlap - # the selection. This minimises the number of iterations in the main for loop. - - # check fields are sensible - check_fields(fields, self._dtype) - fields = check_no_multi_fields(fields) - - # determine indices of chunks overlapping the selection - sel_shape = indexer.shape - - # check value shape - if sel_shape == (): - # setting a single item - pass - elif is_scalar(value, self._dtype): - # setting a scalar value - pass - else: - if not hasattr(value, "shape"): - value = np.asanyarray(value, like=self._meta_array) - check_array_shape("value", value, sel_shape) - - # iterate over chunks in range - if ( - not hasattr(self.chunk_store, "setitems") - or self._synchronizer is not None - or any(map(lambda x: x == 0, self.shape)) - ): - # iterative approach - for chunk_coords, chunk_selection, out_selection in indexer: - # extract data to store - if sel_shape == (): - chunk_value = value - elif is_scalar(value, self._dtype): - chunk_value = value - else: - chunk_value = value[out_selection] - # handle missing singleton dimensions - if indexer.drop_axes: - item = [slice(None)] * self.ndim - for a in indexer.drop_axes: - item[a] = np.newaxis - item = tuple(item) - chunk_value = chunk_value[item] - - # put data - self._chunk_setitem(chunk_coords, chunk_selection, chunk_value, fields=fields) - else: - lchunk_coords, lchunk_selection, lout_selection = zip(*indexer) - chunk_values = [] - for out_selection in lout_selection: - if sel_shape == (): - chunk_values.append(value) - elif is_scalar(value, self._dtype): - chunk_values.append(value) - else: - cv = value[out_selection] - # handle missing singleton dimensions - if indexer.drop_axes: # pragma: no cover - item = [slice(None)] * self.ndim - for a in indexer.drop_axes: - item[a] = np.newaxis - item = tuple(item) - cv = chunk_value[item] - chunk_values.append(cv) - - self._chunk_setitems(lchunk_coords, lchunk_selection, chunk_values, fields=fields) - - def _process_chunk( - self, - out, - cdata, - chunk_selection, - drop_axes, - out_is_ndarray, - fields, - out_selection, - partial_read_decode=False, - ): - """Take binary data from storage and fill output array""" - if ( - out_is_ndarray - and not fields - and is_contiguous_selection(out_selection) - and is_total_slice(chunk_selection, self._chunks) - and not self._filters - and self._dtype != object - ): - dest = out[out_selection] - # Assume that array-like objects that doesn't have a - # `writeable` flag is writable. - dest_is_writable = getattr(dest, "writeable", True) - write_direct = dest_is_writable and ( - (self._order == "C" and dest.flags.c_contiguous) - or (self._order == "F" and dest.flags.f_contiguous) - ) - - if write_direct: - # optimization: we want the whole chunk, and the destination is - # contiguous, so we can decompress directly from the chunk - # into the destination array - if self._compressor: - if isinstance(cdata, PartialReadBuffer): - cdata = cdata.read_full() - self._compressor.decode(cdata, dest) - else: - chunk = ensure_ndarray_like(cdata).view(self._dtype) - chunk = chunk.reshape(self._chunks, order=self._order) - np.copyto(dest, chunk) - return - - # decode chunk - try: - if partial_read_decode: - cdata.prepare_chunk() - # size of chunk - tmp = np.empty_like(self._meta_array, shape=self._chunks, dtype=self.dtype) - index_selection = PartialChunkIterator(chunk_selection, self.chunks) - for start, nitems, partial_out_selection in index_selection: - expected_shape = [ - len(range(*partial_out_selection[i].indices(self.chunks[0] + 1))) - if i < len(partial_out_selection) - else dim - for i, dim in enumerate(self.chunks) - ] - cdata.read_part(start, nitems) - chunk_partial = self._decode_chunk( - cdata.buff, - start=start, - nitems=nitems, - expected_shape=expected_shape, - ) - tmp[partial_out_selection] = chunk_partial - out[out_selection] = tmp[chunk_selection] - return - except ArrayIndexError: - cdata = cdata.read_full() - chunk = self._decode_chunk(cdata) - - # select data from chunk - if fields: - chunk = chunk[fields] - tmp = chunk[chunk_selection] - if drop_axes: - tmp = np.squeeze(tmp, axis=drop_axes) - - # store selected data in output - out[out_selection] = tmp - - def _chunk_getitems( - self, lchunk_coords, lchunk_selection, out, lout_selection, drop_axes=None, fields=None - ): - """Obtain part or whole of chunks. - - Parameters - ---------- - chunk_coords : list of tuple of ints - Indices of the chunks. - chunk_selection : list of selections - Location of region within the chunks to extract. - out : ndarray - Array to store result in. - out_selection : list of selections - Location of regions within output array to store results in. - drop_axes : tuple of ints - Axes to squeeze out of the chunk. - fields - TODO - """ - - out_is_ndarray = True - try: - out = ensure_ndarray_like(out) - except TypeError: # pragma: no cover - out_is_ndarray = False - - # Keys to retrieve - ckeys = [self._chunk_key(ch) for ch in lchunk_coords] - - # Check if we can do a partial read - if ( - self._partial_decompress - and self._compressor - and self._compressor.codec_id == "blosc" - and hasattr(self._compressor, "decode_partial") - and not fields - and self.dtype != object - and hasattr(self.chunk_store, "getitems") - ): - partial_read_decode = True - cdatas = { - ckey: PartialReadBuffer(ckey, self.chunk_store) - for ckey in ckeys - if ckey in self.chunk_store - } - elif ( - self._partial_decompress - and not self._compressor - and not fields - and self.dtype != object - and hasattr(self.chunk_store, "get_partial_values") - and self.chunk_store.supports_efficient_get_partial_values - ): - partial_read_decode = True - cdatas = { - ckey: UncompressedPartialReadBufferV3( - ckey, self.chunk_store, itemsize=self.itemsize - ) - for ckey in ckeys - if ckey in self.chunk_store - } - elif hasattr(self.chunk_store, "get_partial_values"): - partial_read_decode = False - values = self.chunk_store.get_partial_values([(ckey, (0, None)) for ckey in ckeys]) - cdatas = {key: value for key, value in zip(ckeys, values) if value is not None} - else: - partial_read_decode = False - contexts = {} - if not isinstance(self._meta_array, np.ndarray): - contexts = ConstantMap(ckeys, constant=Context(meta_array=self._meta_array)) - cdatas = self.chunk_store.getitems(ckeys, contexts=contexts) - - for ckey, chunk_select, out_select in zip(ckeys, lchunk_selection, lout_selection): - if ckey in cdatas: - self._process_chunk( - out, - cdatas[ckey], - chunk_select, - drop_axes, - out_is_ndarray, - fields, - out_select, - partial_read_decode=partial_read_decode, - ) - else: - # check exception type - if self._fill_value is not None: - if fields: - fill_value = self._fill_value[fields] - else: - fill_value = self._fill_value - out[out_select] = fill_value - - def _chunk_setitems(self, lchunk_coords, lchunk_selection, values, fields=None): - ckeys = map(self._chunk_key, lchunk_coords) - cdatas = { - key: self._process_for_setitem(key, sel, val, fields=fields) - for key, sel, val in zip(ckeys, lchunk_selection, values) - } - to_store = {} - if not self.write_empty_chunks: - empty_chunks = {k: v for k, v in cdatas.items() if all_equal(self.fill_value, v)} - self._chunk_delitems(empty_chunks.keys()) - nonempty_keys = cdatas.keys() - empty_chunks.keys() - to_store = {k: self._encode_chunk(cdatas[k]) for k in nonempty_keys} - else: - to_store = {k: self._encode_chunk(v) for k, v in cdatas.items()} - self.chunk_store.setitems(to_store) - - def _chunk_delitems(self, ckeys): - if hasattr(self.store, "delitems"): - self.store.delitems(ckeys) - else: # pragma: no cover - # exempting this branch from coverage as there are no extant stores - # that will trigger this condition, but it's possible that they - # will be developed in the future. - tuple(map(self._chunk_delitem, ckeys)) - - def _chunk_delitem(self, ckey): - """ - Attempt to delete the value associated with ckey. - """ - try: - del self.chunk_store[ckey] - except KeyError: - pass - - def _chunk_setitem(self, chunk_coords, chunk_selection, value, fields=None): - """Replace part or whole of a chunk. - - Parameters - ---------- - chunk_coords : tuple of ints - Indices of the chunk. - chunk_selection : tuple of slices - Location of region within the chunk. - value : scalar or ndarray - Value to set. - - """ - - if self._synchronizer is None: - # no synchronization - lock = nolock - else: - # synchronize on the chunk - ckey = self._chunk_key(chunk_coords) - lock = self._synchronizer[ckey] - - with lock: - self._chunk_setitem_nosync(chunk_coords, chunk_selection, value, fields=fields) - - def _chunk_setitem_nosync(self, chunk_coords, chunk_selection, value, fields=None): - ckey = self._chunk_key(chunk_coords) - cdata = self._process_for_setitem(ckey, chunk_selection, value, fields=fields) - - # attempt to delete chunk if it only contains the fill value - if (not self.write_empty_chunks) and all_equal(self.fill_value, cdata): - self._chunk_delitem(ckey) - else: - self.chunk_store[ckey] = self._encode_chunk(cdata) - - def _process_for_setitem(self, ckey, chunk_selection, value, fields=None): - if is_total_slice(chunk_selection, self._chunks) and not fields: - # totally replace chunk - - # optimization: we are completely replacing the chunk, so no need - # to access the existing chunk data - - if is_scalar(value, self._dtype): - # setup array filled with value - chunk = np.empty_like( - self._meta_array, shape=self._chunks, dtype=self._dtype, order=self._order - ) - chunk.fill(value) - - else: - # ensure array is contiguous - chunk = value.astype(self._dtype, order=self._order, copy=False) - - else: - # partially replace the contents of this chunk - - try: - # obtain compressed data for chunk - cdata = self.chunk_store[ckey] - - except KeyError: - # chunk not initialized - if self._fill_value is not None: - chunk = np.empty_like( - self._meta_array, shape=self._chunks, dtype=self._dtype, order=self._order - ) - chunk.fill(self._fill_value) - elif self._dtype == object: - chunk = np.empty(self._chunks, dtype=self._dtype, order=self._order) - else: - # N.B., use zeros here so any region beyond the array has consistent - # and compressible data - chunk = np.zeros_like( - self._meta_array, shape=self._chunks, dtype=self._dtype, order=self._order - ) - - else: - # decode chunk - chunk = self._decode_chunk(cdata) - if not chunk.flags.writeable: - chunk = chunk.copy(order="K") - - # modify - if fields: - # N.B., currently multi-field assignment is not supported in numpy, so - # this only works for a single field - chunk[fields][chunk_selection] = value - else: - chunk[chunk_selection] = value - - return chunk - - def _chunk_key(self, chunk_coords): - return self._key_prefix + self._dimension_separator.join(map(str, chunk_coords)) - - def _decode_chunk(self, cdata, start=None, nitems=None, expected_shape=None): - # decompress - if self._compressor: - # only decode requested items - if ( - all(x is not None for x in [start, nitems]) and self._compressor.codec_id == "blosc" - ) and hasattr(self._compressor, "decode_partial"): - chunk = self._compressor.decode_partial(cdata, start, nitems) - else: - chunk = self._compressor.decode(cdata) - else: - chunk = cdata - - # apply filters - if self._filters: - for f in reversed(self._filters): - chunk = f.decode(chunk) - - # view as numpy array with correct dtype - chunk = ensure_ndarray_like(chunk) - # special case object dtype, because incorrect handling can lead to - # segfaults and other bad things happening - if self._dtype != object: - chunk = chunk.view(self._dtype) - elif chunk.dtype != object: - # If we end up here, someone must have hacked around with the filters. - # We cannot deal with object arrays unless there is an object - # codec in the filter chain, i.e., a filter that converts from object - # array to something else during encoding, and converts back to object - # array during decoding. - raise RuntimeError("cannot read object array without object codec") - - # ensure correct chunk shape - chunk = chunk.reshape(-1, order="A") - chunk = chunk.reshape(expected_shape or self._chunks, order=self._order) - - return chunk - - def _encode_chunk(self, chunk): - # apply filters - if self._filters: - for f in self._filters: - chunk = f.encode(chunk) - - # check object encoding - if ensure_ndarray_like(chunk).dtype == object: - raise RuntimeError("cannot write object array without object codec") - - # compress - if self._compressor: - cdata = self._compressor.encode(chunk) - else: - cdata = chunk - - # ensure in-memory data is immutable and easy to compare - if isinstance(self.chunk_store, KVStore) or isinstance(self._chunk_store, KVStore): - cdata = ensure_bytes(cdata) - - return cdata - - def __repr__(self): - t = type(self) - r = "<{}.{}".format(t.__module__, t.__name__) - if self.name: - r += " %r" % self.name - r += " %s" % str(self.shape) - r += " %s" % self.dtype - if self._read_only: - r += " read-only" - r += ">" - return r - - @property - def info(self): - """Report some diagnostic information about the array. - - Examples - -------- - >>> import zarr - >>> z = zarr.v2.zeros(1000000, chunks=100000, dtype='i4') - >>> z.info - Type : zarr.v2.core.Array - Data type : int32 - Shape : (1000000,) - Chunk shape : (100000,) - Order : C - Read-only : False - Compressor : Blosc(cname='lz4', clevel=5, shuffle=SHUFFLE, blocksize=0) - Store type : zarr.v2.storage.KVStore - No. bytes : 4000000 (3.8M) - No. bytes stored : 320 - Storage ratio : 12500.0 - Chunks initialized : 0/10 - - """ - return self._info_reporter - - def info_items(self): - return self._synchronized_op(self._info_items_nosync) - - def _info_items_nosync(self): - def typestr(o): - return "{}.{}".format(type(o).__module__, type(o).__name__) - - def bytestr(n): - if n > 2**10: - return "{} ({})".format(n, human_readable_size(n)) - else: - return str(n) - - items = [] - - # basic info - if self.name is not None: - items += [("Name", self.name)] - items += [ - ("Type", typestr(self)), - ("Data type", "%s" % self.dtype), - ("Shape", str(self.shape)), - ("Chunk shape", str(self.chunks)), - ("Order", self.order), - ("Read-only", str(self.read_only)), - ] - - # filters - if self.filters: - for i, f in enumerate(self.filters): - items += [("Filter [%s]" % i, repr(f))] - - # compressor - items += [("Compressor", repr(self.compressor))] - - # synchronizer - if self._synchronizer is not None: - items += [("Synchronizer type", typestr(self._synchronizer))] - - # storage info - items += [("Store type", typestr(self._store))] - if self._chunk_store is not None: - items += [("Chunk store type", typestr(self._chunk_store))] - items += [("No. bytes", bytestr(self.nbytes))] - if self.nbytes_stored > 0: - items += [ - ("No. bytes stored", bytestr(self.nbytes_stored)), - ("Storage ratio", "%.1f" % (self.nbytes / self.nbytes_stored)), - ] - items += [("Chunks initialized", "{}/{}".format(self.nchunks_initialized, self.nchunks))] - - return items - - def digest(self, hashname="sha1"): - """ - Compute a checksum for the data. Default uses sha1 for speed. - - Examples - -------- - >>> import binascii - >>> import zarr - >>> z = zarr.v2.empty(shape=(10000, 10000), chunks=(1000, 1000)) - >>> binascii.hexlify(z.digest()) - b'041f90bc7a571452af4f850a8ca2c6cddfa8a1ac' - >>> z = zarr.v2.zeros(shape=(10000, 10000), chunks=(1000, 1000)) - >>> binascii.hexlify(z.digest()) - b'7162d416d26a68063b66ed1f30e0a866e4abed60' - >>> z = zarr.v2.zeros(shape=(10000, 10000), dtype="u1", chunks=(1000, 1000)) - >>> binascii.hexlify(z.digest()) - b'cb387af37410ae5a3222e893cf3373e4e4f22816' - """ - - h = hashlib.new(hashname) - - for i in itertools.product(*[range(s) for s in self.cdata_shape]): - h.update(self.chunk_store.get(self._chunk_key(i), b"")) - - mkey = _prefix_to_array_key(self._store, self._key_prefix) - h.update(self.store.get(mkey, b"")) - - h.update(self.store.get(self.attrs.key, b"")) - - checksum = h.digest() - - return checksum - - def hexdigest(self, hashname="sha1"): - """ - Compute a checksum for the data. Default uses sha1 for speed. - - Examples - -------- - >>> import zarr - >>> z = zarr.v2.empty(shape=(10000, 10000), chunks=(1000, 1000)) - >>> z.hexdigest() - '041f90bc7a571452af4f850a8ca2c6cddfa8a1ac' - >>> z = zarr.v2.zeros(shape=(10000, 10000), chunks=(1000, 1000)) - >>> z.hexdigest() - '7162d416d26a68063b66ed1f30e0a866e4abed60' - >>> z = zarr.v2.zeros(shape=(10000, 10000), dtype="u1", chunks=(1000, 1000)) - >>> z.hexdigest() - 'cb387af37410ae5a3222e893cf3373e4e4f22816' - """ - - checksum = binascii.hexlify(self.digest(hashname=hashname)) - - # This is a bytes object on Python 3 and we want a str. - if not isinstance(checksum, str): - checksum = checksum.decode("utf8") - - return checksum - - def __getstate__(self): - return { - "store": self._store, - "path": self._path, - "read_only": self._read_only, - "chunk_store": self._chunk_store, - "synchronizer": self._synchronizer, - "cache_metadata": self._cache_metadata, - "cache_attrs": self._attrs.cache, - "partial_decompress": self._partial_decompress, - "write_empty_chunks": self._write_empty_chunks, - "meta_array": self._meta_array, - } - - def __setstate__(self, state): - self.__init__(**state) - - def _synchronized_op(self, f, *args, **kwargs): - if self._synchronizer is None: - # no synchronization - lock = nolock - - else: - # synchronize on the array - mkey = _prefix_to_array_key(self._store, self._key_prefix) - lock = self._synchronizer[mkey] - - with lock: - self._refresh_metadata_nosync() - result = f(*args, **kwargs) - - return result - - def _write_op(self, f, *args, **kwargs): - # guard condition - if self._read_only: - raise ReadOnlyError - - return self._synchronized_op(f, *args, **kwargs) - - def resize(self, *args): - """Change the shape of the array by growing or shrinking one or more - dimensions. - - Examples - -------- - >>> import zarr - >>> z = zarr.v2.zeros(shape=(10000, 10000), chunks=(1000, 1000)) - >>> z.shape - (10000, 10000) - >>> z.resize(20000, 10000) - >>> z.shape - (20000, 10000) - >>> z.resize(30000, 1000) - >>> z.shape - (30000, 1000) - - Notes - ----- - When resizing an array, the data are not rearranged in any way. - - If one or more dimensions are shrunk, any chunks falling outside the - new array shape will be deleted from the underlying store. - However, it is noteworthy that the chunks partially falling inside the new array - (i.e. boundary chunks) will remain intact, and therefore, - the data falling outside the new array but inside the boundary chunks - would be restored by a subsequent resize operation that grows the array size. - - """ - - return self._write_op(self._resize_nosync, *args) - - def _resize_nosync(self, *args): - # normalize new shape argument - old_shape = self._shape - new_shape = normalize_resize_args(old_shape, *args) - old_cdata_shape = self._cdata_shape - - # update metadata - self._shape = new_shape - self._flush_metadata_nosync() - - # determine the new number and arrangement of chunks - chunks = self._chunks - new_cdata_shape = tuple(math.ceil(s / c) for s, c in zip(new_shape, chunks)) - - # remove any chunks not within range - # The idea is that, along each dimension, - # only find and remove the chunk slices that exist in 'old' but not 'new' data. - # Note that a mutable list ('old_cdata_shape_working_list') is introduced here - # to dynamically adjust the number of chunks along the already-processed dimensions - # in order to avoid duplicate chunk removal. - chunk_store = self.chunk_store - old_cdata_shape_working_list = list(old_cdata_shape) - for idx_cdata, (val_old_cdata, val_new_cdata) in enumerate( - zip(old_cdata_shape_working_list, new_cdata_shape) - ): - for cidx in itertools.product( - *[ - range(n_new, n_old) if (idx == idx_cdata) else range(n_old) - for idx, (n_old, n_new) in enumerate( - zip(old_cdata_shape_working_list, new_cdata_shape) - ) - ] - ): - key = self._chunk_key(cidx) - try: - del chunk_store[key] - except KeyError: - # chunk not initialized - pass - old_cdata_shape_working_list[idx_cdata] = min(val_old_cdata, val_new_cdata) - - def append(self, data, axis=0): - """Append `data` to `axis`. - - Parameters - ---------- - data : array-like - Data to be appended. - axis : int - Axis along which to append. - - Returns - ------- - new_shape : tuple - - Notes - ----- - The size of all dimensions other than `axis` must match between this - array and `data`. - - Examples - -------- - >>> import numpy as np - >>> import zarr - >>> a = np.arange(10000000, dtype='i4').reshape(10000, 1000) - >>> z = zarr.v2.array(a, chunks=(1000, 100)) - >>> z.shape - (10000, 1000) - >>> z.append(a) - (20000, 1000) - >>> z.append(np.vstack([a, a]), axis=1) - (20000, 2000) - >>> z.shape - (20000, 2000) - - """ - return self._write_op(self._append_nosync, data, axis=axis) - - def _append_nosync(self, data, axis=0): - # ensure data is array-like - if not hasattr(data, "shape"): - data = np.asanyarray(data, like=self._meta_array) - - # ensure shapes are compatible for non-append dimensions - self_shape_preserved = tuple(s for i, s in enumerate(self._shape) if i != axis) - data_shape_preserved = tuple(s for i, s in enumerate(data.shape) if i != axis) - if self_shape_preserved != data_shape_preserved: - raise ValueError( - "shape of data to append is not compatible with the array; " - "all dimensions must match except for the dimension being " - "appended" - ) - - # remember old shape - old_shape = self._shape - - # determine new shape - new_shape = tuple( - self._shape[i] if i != axis else self._shape[i] + data.shape[i] - for i in range(len(self._shape)) - ) - - # resize - self._resize_nosync(new_shape) - - # store data - # noinspection PyTypeChecker - append_selection = tuple( - slice(None) if i != axis else slice(old_shape[i], new_shape[i]) - for i in range(len(self._shape)) - ) - self[append_selection] = data - - return new_shape - - def view( - self, - shape=None, - chunks=None, - dtype=None, - fill_value=None, - filters=None, - read_only=None, - synchronizer=None, - ): - """Return an array sharing the same data. - - Parameters - ---------- - shape : int or tuple of ints - Array shape. - chunks : int or tuple of ints, optional - Chunk shape. - dtype : string or dtype, optional - NumPy dtype. - fill_value : object - Default value to use for uninitialized portions of the array. - filters : sequence, optional - Sequence of filters to use to encode chunk data prior to - compression. - read_only : bool, optional - True if array should be protected against modification. - synchronizer : object, optional - Array synchronizer. - - Notes - ----- - WARNING: This is an experimental feature and should be used with care. - There are plenty of ways to generate errors and/or cause data - corruption. - - Examples - -------- - - Bypass filters: - - >>> import zarr - >>> import numpy as np - >>> np.random.seed(42) - >>> labels = ['female', 'male'] - >>> data = np.random.choice(labels, size=10000) - >>> filters = [zarr.v2.Categorize(labels=labels, - ... dtype=data.dtype, - ... astype='u1')] - >>> a = zarr.v2.array(data, chunks=1000, filters=filters) - >>> a[:] - array(['female', 'male', 'female', ..., 'male', 'male', 'female'], - dtype='>> v = a.view(dtype='u1', filters=[]) - >>> v.is_view - True - >>> v[:] - array([1, 2, 1, ..., 2, 2, 1], dtype=uint8) - - Views can be used to modify data: - - >>> x = v[:] - >>> x.sort() - >>> v[:] = x - >>> v[:] - array([1, 1, 1, ..., 2, 2, 2], dtype=uint8) - >>> a[:] - array(['female', 'female', 'female', ..., 'male', 'male', 'male'], - dtype='>> data = np.random.randint(0, 2, size=10000, dtype='u1') - >>> a = zarr.v2.array(data, chunks=1000) - >>> a[:] - array([0, 0, 1, ..., 1, 0, 0], dtype=uint8) - >>> v = a.view(dtype=bool) - >>> v[:] - array([False, False, True, ..., True, False, False]) - >>> np.all(a[:].view(dtype=bool) == v[:]) - True - - An array can be viewed with a dtype with a different item size, however - some care is needed to adjust the shape and chunk shape so that chunk - data is interpreted correctly: - - >>> data = np.arange(10000, dtype='u2') - >>> a = zarr.v2.array(data, chunks=1000) - >>> a[:10] - array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=uint16) - >>> v = a.view(dtype='u1', shape=20000, chunks=2000) - >>> v[:10] - array([0, 0, 1, 0, 2, 0, 3, 0, 4, 0], dtype=uint8) - >>> np.all(a[:].view('u1') == v[:]) - True - - Change fill value for uninitialized chunks: - - >>> a = zarr.v2.full(10000, chunks=1000, fill_value=-1, dtype='i1') - >>> a[:] - array([-1, -1, -1, ..., -1, -1, -1], dtype=int8) - >>> v = a.view(fill_value=42) - >>> v[:] - array([42, 42, 42, ..., 42, 42, 42], dtype=int8) - - Note that resizing or appending to views is not permitted: - - >>> a = zarr.v2.empty(10000) - >>> v = a.view() - >>> try: - ... v.resize(20000) - ... except PermissionError as e: - ... print(e) - operation not permitted for views - - """ - - store = self._store - chunk_store = self._chunk_store - path = self._path - if read_only is None: - read_only = self._read_only - if synchronizer is None: - synchronizer = self._synchronizer - a = Array( - store=store, - path=path, - chunk_store=chunk_store, - read_only=read_only, - synchronizer=synchronizer, - cache_metadata=True, - ) - a._is_view = True - - # allow override of some properties - if dtype is None: - dtype = self._dtype - else: - dtype = np.dtype(dtype) - a._dtype = dtype - if shape is None: - shape = self._shape - else: - shape = normalize_shape(shape) - a._shape = shape - if chunks is not None: - chunks = normalize_chunks(chunks, shape, dtype.itemsize) - a._chunks = chunks - if fill_value is not None: - a._fill_value = fill_value - if filters is not None: - a._filters = filters - - return a - - def astype(self, dtype): - """Returns a view that does on the fly type conversion of the underlying data. - - Parameters - ---------- - dtype : string or dtype - NumPy dtype. - - Notes - ----- - This method returns a new Array object which is a view on the same - underlying chunk data. Modifying any data via the view is currently - not permitted and will result in an error. This is an experimental - feature and its behavior is subject to change in the future. - - See Also - -------- - Array.view - - Examples - -------- - - >>> import zarr - >>> import numpy as np - >>> data = np.arange(100, dtype=np.uint8) - >>> a = zarr.v2.array(data, chunks=10) - >>> a[:] - array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99], dtype=uint8) - >>> v = a.astype(np.float32) - >>> v.is_view - True - >>> v[:] - array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., - 10., 11., 12., 13., 14., 15., 16., 17., 18., 19., - 20., 21., 22., 23., 24., 25., 26., 27., 28., 29., - 30., 31., 32., 33., 34., 35., 36., 37., 38., 39., - 40., 41., 42., 43., 44., 45., 46., 47., 48., 49., - 50., 51., 52., 53., 54., 55., 56., 57., 58., 59., - 60., 61., 62., 63., 64., 65., 66., 67., 68., 69., - 70., 71., 72., 73., 74., 75., 76., 77., 78., 79., - 80., 81., 82., 83., 84., 85., 86., 87., 88., 89., - 90., 91., 92., 93., 94., 95., 96., 97., 98., 99.], - dtype=float32) - """ - - dtype = np.dtype(dtype) - - filters = [] - if self._filters: - filters.extend(self._filters) - filters.insert(0, AsType(encode_dtype=self._dtype, decode_dtype=dtype)) - - return self.view(filters=filters, dtype=dtype, read_only=True) diff --git a/src/zarr/v2/creation.py b/src/zarr/v2/creation.py deleted file mode 100644 index d0ba00603d..0000000000 --- a/src/zarr/v2/creation.py +++ /dev/null @@ -1,707 +0,0 @@ -from typing import Optional -from warnings import warn - -import numpy as np -from numcodecs.registry import codec_registry - -from zarr.v2.core import Array -from zarr.v2.errors import ( - ArrayNotFoundError, - ContainsArrayError, - ContainsGroupError, -) -from zarr.v2.storage import ( - contains_array, - contains_group, - default_compressor, - init_array, - normalize_storage_path, - normalize_store_arg, -) -from zarr.v2.util import normalize_dimension_separator - - -def create( - shape, - chunks=True, - dtype=None, - compressor="default", - fill_value: Optional[int] = 0, - order="C", - store=None, - synchronizer=None, - overwrite=False, - path=None, - chunk_store=None, - filters=None, - cache_metadata=True, - cache_attrs=True, - read_only=False, - object_codec=None, - dimension_separator=None, - write_empty_chunks=True, - *, - meta_array=None, - **kwargs, -): - """Create an array. - - Parameters - ---------- - shape : int or tuple of ints - Array shape. - chunks : int or tuple of ints, optional - Chunk shape. If True, will be guessed from `shape` and `dtype`. If - False, will be set to `shape`, i.e., single chunk for the whole array. - If an int, the chunk size in each dimension will be given by the value - of `chunks`. Default is True. - dtype : string or dtype, optional - NumPy dtype. - compressor : Codec, optional - Primary compressor. - fill_value : object - Default value to use for uninitialized portions of the array. - order : {'C', 'F'}, optional - Memory layout to be used within each chunk. - store : MutableMapping or string - Store or path to directory in file system or name of zip file. - synchronizer : object, optional - Array synchronizer. - overwrite : bool, optional - If True, delete all pre-existing data in `store` at `path` before - creating the array. - path : string, optional - Path under which array is stored. - chunk_store : MutableMapping, optional - Separate storage for chunks. If not provided, `store` will be used - for storage of both chunks and metadata. - filters : sequence of Codecs, optional - Sequence of filters to use to encode chunk data prior to compression. - cache_metadata : bool, optional - If True, array configuration metadata will be cached for the - lifetime of the object. If False, array metadata will be reloaded - prior to all data access and modification operations (may incur - overhead depending on storage and data access pattern). - cache_attrs : bool, optional - If True (default), user attributes will be cached for attribute read - operations. If False, user attributes are reloaded from the store prior - to all attribute read operations. - read_only : bool, optional - True if array should be protected against modification. - object_codec : Codec, optional - A codec to encode object arrays, only needed if dtype=object. - dimension_separator : {'.', '/'}, optional - Separator placed between the dimensions of a chunk. - - .. versionadded:: 2.8 - - write_empty_chunks : bool, optional - If True (default), all chunks will be stored regardless of their - contents. If False, each chunk is compared to the array's fill value - prior to storing. If a chunk is uniformly equal to the fill value, then - that chunk is not be stored, and the store entry for that chunk's key - is deleted. This setting enables sparser storage, as only chunks with - non-fill-value data are stored, at the expense of overhead associated - with checking the data of each chunk. - - .. versionadded:: 2.11 - - meta_array : array-like, optional - An array instance to use for determining arrays to create and return - to users. Use `numpy.empty(())` by default. - - .. versionadded:: 2.13 - - Returns - ------- - z : zarr.v2.core.Array - - Examples - -------- - - Create an array with default settings:: - - >>> import zarr - >>> z = zarr.v2.create((10000, 10000), chunks=(1000, 1000)) - >>> z - - - Create an array with different some different configuration options:: - - >>> from numcodecs import Blosc - >>> compressor = Blosc(cname='zstd', clevel=1, shuffle=Blosc.BITSHUFFLE) - >>> z = zarr.v2.create((10000, 10000), chunks=(1000, 1000), dtype='i1', order='F', - ... compressor=compressor) - >>> z - - - To create an array with object dtype requires a filter that can handle Python object - encoding, e.g., `MsgPack` or `Pickle` from `numcodecs`:: - - >>> from numcodecs import MsgPack - >>> z = zarr.v2.create((10000, 10000), chunks=(1000, 1000), dtype=object, - ... object_codec=MsgPack()) - >>> z - - - Example with some filters, and also storing chunks separately from metadata:: - - >>> from numcodecs import Quantize, Adler32 - >>> store, chunk_store = dict(), dict() - >>> z = zarr.v2.create((10000, 10000), chunks=(1000, 1000), dtype='f8', - ... filters=[Quantize(digits=2, dtype='f8'), Adler32()], - ... store=store, chunk_store=chunk_store) - >>> z - - - """ - - # handle polymorphic store arg - store = normalize_store_arg(store, mode="w") - - # API compatibility with h5py - compressor, fill_value = _kwargs_compat(compressor, fill_value, kwargs) - - # optional array metadata - if dimension_separator is None: - dimension_separator = getattr(store, "_dimension_separator", None) - else: - store_separator = getattr(store, "_dimension_separator", None) - if store_separator not in (None, dimension_separator): - raise ValueError( - f"Specified dimension_separator: {dimension_separator}" - f"conflicts with store's separator: " - f"{store_separator}" - ) - dimension_separator = normalize_dimension_separator(dimension_separator) - - # initialize array metadata - init_array( - store, - shape=shape, - chunks=chunks, - dtype=dtype, - compressor=compressor, - fill_value=fill_value, - order=order, - overwrite=overwrite, - path=path, - chunk_store=chunk_store, - filters=filters, - object_codec=object_codec, - dimension_separator=dimension_separator, - ) - - # instantiate array - z = Array( - store, - path=path, - chunk_store=chunk_store, - synchronizer=synchronizer, - cache_metadata=cache_metadata, - cache_attrs=cache_attrs, - read_only=read_only, - write_empty_chunks=write_empty_chunks, - meta_array=meta_array, - ) - - return z - - -def _kwargs_compat(compressor, fill_value, kwargs): - # to be compatible with h5py, as well as backwards-compatible with Zarr - # 1.x, accept 'compression' and 'compression_opts' keyword arguments - - if compressor != "default": - # 'compressor' overrides 'compression' - if "compression" in kwargs: - warn( - "'compression' keyword argument overridden by 'compressor'", - stacklevel=3, - ) - del kwargs["compression"] - if "compression_opts" in kwargs: - warn( - "'compression_opts' keyword argument overridden by 'compressor'", - stacklevel=3, - ) - del kwargs["compression_opts"] - - elif "compression" in kwargs: - compression = kwargs.pop("compression") - compression_opts = kwargs.pop("compression_opts", None) - - if compression is None or compression == "none": - compressor = None - - elif compression == "default": - compressor = default_compressor - - elif isinstance(compression, str): - codec_cls = codec_registry[compression] - - # handle compression_opts - if isinstance(compression_opts, dict): - compressor = codec_cls(**compression_opts) - elif isinstance(compression_opts, (list, tuple)): - compressor = codec_cls(*compression_opts) - elif compression_opts is None: - compressor = codec_cls() - else: - # assume single argument, e.g., int - compressor = codec_cls(compression_opts) - - # be lenient here if user gives compressor as 'compression' - elif hasattr(compression, "get_config"): - compressor = compression - - else: - raise ValueError("bad value for compression: %r" % compression) - - # handle 'fillvalue' - if "fillvalue" in kwargs: - # to be compatible with h5py, accept 'fillvalue' instead of - # 'fill_value' - fill_value = kwargs.pop("fillvalue") - - # ignore other keyword arguments - for k in kwargs: - warn("ignoring keyword argument %r" % k) - - return compressor, fill_value - - -def empty(shape, **kwargs): - """Create an empty array. - - For parameter definitions see :func:`zarr.v2.creation.create`. - - Notes - ----- - The contents of an empty Zarr array are not defined. On attempting to - retrieve data from an empty Zarr array, any values may be returned, - and these are not guaranteed to be stable from one access to the next. - - """ - return create(shape=shape, fill_value=None, **kwargs) - - -def zeros(shape, **kwargs): - """Create an array, with zero being used as the default value for - uninitialized portions of the array. - - For parameter definitions see :func:`zarr.v2.creation.create`. - - Examples - -------- - >>> import zarr - >>> z = zarr.v2.zeros((10000, 10000), chunks=(1000, 1000)) - >>> z - - >>> z[:2, :2] - array([[0., 0.], - [0., 0.]]) - - """ - - return create(shape=shape, fill_value=0, **kwargs) - - -def ones(shape, **kwargs): - """Create an array, with one being used as the default value for - uninitialized portions of the array. - - For parameter definitions see :func:`zarr.v2.creation.create`. - - Examples - -------- - >>> import zarr - >>> z = zarr.v2.ones((10000, 10000), chunks=(1000, 1000)) - >>> z - - >>> z[:2, :2] - array([[1., 1.], - [1., 1.]]) - - """ - - return create(shape=shape, fill_value=1, **kwargs) - - -def full(shape, fill_value, **kwargs): - """Create an array, with `fill_value` being used as the default value for - uninitialized portions of the array. - - For parameter definitions see :func:`zarr.v2.creation.create`. - - Examples - -------- - >>> import zarr - >>> z = zarr.v2.full((10000, 10000), chunks=(1000, 1000), fill_value=42) - >>> z - - >>> z[:2, :2] - array([[42., 42.], - [42., 42.]]) - - """ - - return create(shape=shape, fill_value=fill_value, **kwargs) - - -def _get_shape_chunks(a): - shape = None - chunks = None - - if hasattr(a, "shape") and isinstance(a.shape, tuple): - shape = a.shape - - if hasattr(a, "chunks") and isinstance(a.chunks, tuple) and (len(a.chunks) == len(a.shape)): - chunks = a.chunks - - elif hasattr(a, "chunklen"): - # bcolz carray - chunks = (a.chunklen,) + a.shape[1:] - - return shape, chunks - - -def array(data, **kwargs): - """Create an array filled with `data`. - - The `data` argument should be a NumPy array or array-like object. For - other parameter definitions see :func:`zarr.v2.creation.create`. - - Examples - -------- - >>> import numpy as np - >>> import zarr - >>> a = np.arange(100000000).reshape(10000, 10000) - >>> z = zarr.v2.array(a, chunks=(1000, 1000)) - >>> z - - - """ - - # ensure data is array-like - if not hasattr(data, "shape") or not hasattr(data, "dtype"): - data = np.asanyarray(data) - - # setup dtype - kw_dtype = kwargs.get("dtype") - if kw_dtype is None: - kwargs["dtype"] = data.dtype - else: - kwargs["dtype"] = kw_dtype - - # setup shape and chunks - data_shape, data_chunks = _get_shape_chunks(data) - kwargs["shape"] = data_shape - kw_chunks = kwargs.get("chunks") - if kw_chunks is None: - kwargs["chunks"] = data_chunks - else: - kwargs["chunks"] = kw_chunks - - # pop read-only to apply after storing the data - read_only = kwargs.pop("read_only", False) - - # instantiate array - z = create(**kwargs) - - # fill with data - z[...] = data - - # set read_only property afterwards - z.read_only = read_only - - return z - - -def open_array( - store=None, - mode="a", - shape=None, - chunks=True, - dtype=None, - compressor="default", - fill_value=0, - order="C", - synchronizer=None, - filters=None, - cache_metadata=True, - cache_attrs=True, - path=None, - object_codec=None, - chunk_store=None, - storage_options=None, - partial_decompress=False, - write_empty_chunks=True, - *, - dimension_separator=None, - meta_array=None, - **kwargs, -): - """Open an array using file-mode-like semantics. - - Parameters - ---------- - store : MutableMapping or string, optional - Store or path to directory in file system or name of zip file. - mode : {'r', 'r+', 'a', 'w', 'w-'}, optional - Persistence mode: 'r' means read only (must exist); 'r+' means - read/write (must exist); 'a' means read/write (create if doesn't - exist); 'w' means create (overwrite if exists); 'w-' means create - (fail if exists). - shape : int or tuple of ints, optional - Array shape. - chunks : int or tuple of ints, optional - Chunk shape. If True, will be guessed from `shape` and `dtype`. If - False, will be set to `shape`, i.e., single chunk for the whole array. - If an int, the chunk size in each dimension will be given by the value - of `chunks`. Default is True. - dtype : string or dtype, optional - NumPy dtype. - compressor : Codec, optional - Primary compressor. - fill_value : object, optional - Default value to use for uninitialized portions of the array. - order : {'C', 'F'}, optional - Memory layout to be used within each chunk. - synchronizer : object, optional - Array synchronizer. - filters : sequence, optional - Sequence of filters to use to encode chunk data prior to compression. - cache_metadata : bool, optional - If True, array configuration metadata will be cached for the - lifetime of the object. If False, array metadata will be reloaded - prior to all data access and modification operations (may incur - overhead depending on storage and data access pattern). - cache_attrs : bool, optional - If True (default), user attributes will be cached for attribute read - operations. If False, user attributes are reloaded from the store prior - to all attribute read operations. - path : string, optional - Array path within store. - object_codec : Codec, optional - A codec to encode object arrays, only needed if dtype=object. - chunk_store : MutableMapping or string, optional - Store or path to directory in file system or name of zip file. - storage_options : dict - If using an fsspec URL to create the store, these will be passed to - the backend implementation. Ignored otherwise. - partial_decompress : bool, optional - If True and while the chunk_store is a FSStore and the compression used - is Blosc, when getting data from the array chunks will be partially - read and decompressed when possible. - write_empty_chunks : bool, optional - If True (default), all chunks will be stored regardless of their - contents. If False, each chunk is compared to the array's fill value - prior to storing. If a chunk is uniformly equal to the fill value, then - that chunk is not be stored, and the store entry for that chunk's key - is deleted. This setting enables sparser storage, as only chunks with - non-fill-value data are stored, at the expense of overhead associated - with checking the data of each chunk. - - .. versionadded:: 2.11 - - dimension_separator : {None, '.', '/'}, optional - Can be used to specify whether the array is in a flat ('.') or nested - ('/') format. If None, the appropriate value will be read from `store` - when present. Otherwise, defaults to '.'. - meta_array : array-like, optional - An array instance to use for determining arrays to create and return - to users. Use `numpy.empty(())` by default. - - .. versionadded:: 2.15 - - Returns - ------- - z : zarr.v2.core.Array - - Examples - -------- - >>> import numpy as np - >>> import zarr - >>> z1 = zarr.v2.open_array('data/example.zarr', mode='w', shape=(10000, 10000), - ... chunks=(1000, 1000), fill_value=0) - >>> z1[:] = np.arange(100000000).reshape(10000, 10000) - >>> z1 - - >>> z2 = zarr.v2.open_array('data/example.zarr', mode='r') - >>> z2 - - >>> np.all(z1[:] == z2[:]) - True - - Notes - ----- - There is no need to close an array. Data are automatically flushed to the - file system. - - """ - - # use same mode semantics as h5py - # r : read only, must exist - # r+ : read/write, must exist - # w : create, delete if exists - # w- or x : create, fail if exists - # a : read/write if exists, create otherwise (default) - - # handle polymorphic store arg - store = normalize_store_arg(store, storage_options=storage_options, mode=mode) - - if chunk_store is not None: - chunk_store = normalize_store_arg(chunk_store, storage_options=storage_options, mode=mode) - - # respect the dimension separator specified in a store, if present - if dimension_separator is None: - if hasattr(store, "_dimension_separator"): - dimension_separator = store._dimension_separator - else: - dimension_separator = "." - - path = normalize_storage_path(path) - - # API compatibility with h5py - compressor, fill_value = _kwargs_compat(compressor, fill_value, kwargs) - - # ensure fill_value of correct type - if fill_value is not None: - fill_value = np.array(fill_value, dtype=dtype)[()] - - # ensure store is initialized - - if mode in ["r", "r+"]: - if not contains_array(store, path=path): - if contains_group(store, path=path): - raise ContainsGroupError(path) - raise ArrayNotFoundError(path) - - elif mode == "w": - init_array( - store, - shape=shape, - chunks=chunks, - dtype=dtype, - compressor=compressor, - fill_value=fill_value, - order=order, - filters=filters, - overwrite=True, - path=path, - object_codec=object_codec, - chunk_store=chunk_store, - dimension_separator=dimension_separator, - ) - - elif mode == "a": - if not contains_array(store, path=path): - if contains_group(store, path=path): - raise ContainsGroupError(path) - init_array( - store, - shape=shape, - chunks=chunks, - dtype=dtype, - compressor=compressor, - fill_value=fill_value, - order=order, - filters=filters, - path=path, - object_codec=object_codec, - chunk_store=chunk_store, - dimension_separator=dimension_separator, - ) - - elif mode in ["w-", "x"]: - if contains_group(store, path=path): - raise ContainsGroupError(path) - elif contains_array(store, path=path): - raise ContainsArrayError(path) - else: - init_array( - store, - shape=shape, - chunks=chunks, - dtype=dtype, - compressor=compressor, - fill_value=fill_value, - order=order, - filters=filters, - path=path, - object_codec=object_codec, - chunk_store=chunk_store, - dimension_separator=dimension_separator, - ) - - # determine read only status - read_only = mode == "r" - - # instantiate array - z = Array( - store, - read_only=read_only, - synchronizer=synchronizer, - cache_metadata=cache_metadata, - cache_attrs=cache_attrs, - path=path, - chunk_store=chunk_store, - write_empty_chunks=write_empty_chunks, - meta_array=meta_array, - ) - - return z - - -def _like_args(a, kwargs): - shape, chunks = _get_shape_chunks(a) - if shape is not None: - kwargs.setdefault("shape", shape) - if chunks is not None: - kwargs.setdefault("chunks", chunks) - - if hasattr(a, "dtype"): - kwargs.setdefault("dtype", a.dtype) - - if isinstance(a, Array): - kwargs.setdefault("compressor", a.compressor) - kwargs.setdefault("order", a.order) - kwargs.setdefault("filters", a.filters) - else: - kwargs.setdefault("compressor", "default") - kwargs.setdefault("order", "C") - - -def empty_like(a, **kwargs): - """Create an empty array like `a`.""" - _like_args(a, kwargs) - return empty(**kwargs) - - -def zeros_like(a, **kwargs): - """Create an array of zeros like `a`.""" - _like_args(a, kwargs) - return zeros(**kwargs) - - -def ones_like(a, **kwargs): - """Create an array of ones like `a`.""" - _like_args(a, kwargs) - return ones(**kwargs) - - -def full_like(a, **kwargs): - """Create a filled array like `a`.""" - _like_args(a, kwargs) - if isinstance(a, Array): - kwargs.setdefault("fill_value", a.fill_value) - return full(**kwargs) - - -def open_like(a, path, **kwargs): - """Open a persistent array like `a`.""" - _like_args(a, kwargs) - if isinstance(a, Array): - kwargs.setdefault("fill_value", a.fill_value) - return open_array(path, **kwargs) diff --git a/src/zarr/v2/errors.py b/src/zarr/v2/errors.py deleted file mode 100644 index 30c9b13d39..0000000000 --- a/src/zarr/v2/errors.py +++ /dev/null @@ -1,80 +0,0 @@ -class MetadataError(Exception): - pass - - -class CopyError(RuntimeError): - pass - - -class _BaseZarrError(ValueError): - _msg = "" - - def __init__(self, *args): - super().__init__(self._msg.format(*args)) - - -class ArrayIndexError(IndexError): - pass - - -class _BaseZarrIndexError(IndexError): - _msg = "" - - def __init__(self, *args): - super().__init__(self._msg.format(*args)) - - -class ContainsGroupError(_BaseZarrError): - _msg = "path {0!r} contains a group" - - -class ContainsArrayError(_BaseZarrError): - _msg = "path {0!r} contains an array" - - -class ArrayNotFoundError(_BaseZarrError): - _msg = "array not found at path %r' {0!r}" - - -class GroupNotFoundError(_BaseZarrError): - _msg = "group not found at path {0!r}" - - -class PathNotFoundError(_BaseZarrError): - _msg = "nothing found at path {0!r}" - - -class BadCompressorError(_BaseZarrError): - _msg = "bad compressor; expected Codec object, found {0!r}" - - -class FSPathExistNotDir(GroupNotFoundError): - _msg = "path exists but is not a directory: %r" - - -class ReadOnlyError(PermissionError): - def __init__(self): - super().__init__("object is read-only") - - -class BoundsCheckError(_BaseZarrIndexError): - _msg = "index out of bounds for dimension with length {0}" - - -class NegativeStepError(IndexError): - def __init__(self): - super().__init__("only slices with step >= 1 are supported") - - -def err_too_many_indices(selection, shape): - raise IndexError( - "too many indices for array; expected {}, got {}".format(len(shape), len(selection)) - ) - - -class VindexInvalidSelectionError(_BaseZarrIndexError): - _msg = ( - "unsupported selection type for vectorized indexing; only " - "coordinate selection (tuple of integer arrays) and mask selection " - "(single Boolean array) are supported; got {0!r}" - ) diff --git a/src/zarr/v2/hierarchy.py b/src/zarr/v2/hierarchy.py deleted file mode 100644 index 25e47311b6..0000000000 --- a/src/zarr/v2/hierarchy.py +++ /dev/null @@ -1,1401 +0,0 @@ -from collections.abc import MutableMapping -from itertools import islice -from typing import Any - -import numpy as np - -from zarr.v2.attrs import Attributes -from zarr.v2.core import Array -from zarr.v2.creation import ( - array, - create, - empty, - empty_like, - full, - full_like, - ones, - ones_like, - zeros, - zeros_like, -) -from zarr.v2.errors import ( - ContainsArrayError, - ContainsGroupError, - GroupNotFoundError, - ReadOnlyError, -) -from zarr.v2.storage import ( - _prefix_to_group_key, - BaseStore, - MemoryStore, - group_meta_key, - attrs_key, - contains_array, - contains_group, - init_group, - listdir, - normalize_store_arg, - rename, - rmdir, -) - -from zarr.v2.util import ( - InfoReporter, - TreeViewer, - is_valid_python_name, - nolock, - normalize_shape, - normalize_storage_path, -) - - -class Group(MutableMapping[str, Any]): - """Instantiate a group from an initialized store. - - Parameters - ---------- - store : MutableMapping - Group store, already initialized. - If the Group is used in a context manager, and the store has a ``close`` method, - it will be called on exit. - path : string, optional - Group path. - read_only : bool, optional - True if group should be protected against modification. - chunk_store : MutableMapping, optional - Separate storage for chunks. If not provided, `store` will be used - for storage of both chunks and metadata. - cache_attrs : bool, optional - If True (default), user attributes will be cached for attribute read - operations. If False, user attributes are reloaded from the store prior - to all attribute read operations. - synchronizer : object, optional - Array synchronizer. - - meta_array : array-like, optional - An array instance to use for determining arrays to create and return - to users. Use `numpy.empty(())` by default. - - .. versionadded:: 2.13 - - Attributes - ---------- - store - path - name - read_only - chunk_store - synchronizer - attrs - info - meta_array - - Methods - ------- - __len__ - __iter__ - __contains__ - __getitem__ - __enter__ - __exit__ - group_keys - groups - array_keys - arrays - visit - visitkeys - visitvalues - visititems - tree - create_group - require_group - create_groups - require_groups - create_dataset - require_dataset - create - empty - zeros - ones - full - array - empty_like - zeros_like - ones_like - full_like - info - move - - """ - - def __init__( - self, - store, - path=None, - read_only=False, - chunk_store=None, - cache_attrs=True, - synchronizer=None, - *, - meta_array=None, - ): - store: BaseStore = _normalize_store_arg(store) - if chunk_store is not None: - chunk_store: BaseStore = _normalize_store_arg(chunk_store) - self._store = store - self._chunk_store = chunk_store - self._path = normalize_storage_path(path) - if self._path: - self._key_prefix = self._path + "/" - else: - self._key_prefix = "" - self._read_only = read_only - self._synchronizer = synchronizer - if meta_array is not None: - self._meta_array = np.empty_like(meta_array, shape=()) - else: - self._meta_array = np.empty(()) - - # guard conditions - if contains_array(store, path=self._path): - raise ContainsArrayError(path) - - # initialize metadata - mkey = None - try: - mkey = _prefix_to_group_key(self._store, self._key_prefix) - assert not mkey.endswith("root/.group") - meta_bytes = store[mkey] - except KeyError: - raise GroupNotFoundError(path) - else: - self._meta = self._store._metadata_class.decode_group_metadata(meta_bytes) - - # setup attributes - akey = self._key_prefix + attrs_key - - self._attrs = Attributes( - store, key=akey, read_only=read_only, cache=cache_attrs, synchronizer=synchronizer - ) - - # setup info - self._info = InfoReporter(self) - - @property - def store(self): - """A MutableMapping providing the underlying storage for the group.""" - return self._store - - @property - def path(self): - """Storage path.""" - return self._path - - @property - def name(self): - """Group name following h5py convention.""" - if self._path: - # follow h5py convention: add leading slash - name = self._path - if name[0] != "/": - name = "/" + name - return name - return "/" - - @property - def basename(self): - """Final component of name.""" - return self.name.split("/")[-1] - - @property - def read_only(self): - """A boolean, True if modification operations are not permitted.""" - return self._read_only - - @property - def chunk_store(self): - """A MutableMapping providing the underlying storage for array chunks.""" - if self._chunk_store is None: - return self._store - else: - return self._chunk_store - - @property - def synchronizer(self): - """Object used to synchronize write access to groups and arrays.""" - return self._synchronizer - - @property - def attrs(self): - """A MutableMapping containing user-defined attributes. Note that - attribute values must be JSON serializable.""" - return self._attrs - - @property - def info(self): - """Return diagnostic information about the group.""" - return self._info - - @property - def meta_array(self): - """An array-like instance to use for determining arrays to create and return - to users. - """ - return self._meta_array - - def __eq__(self, other): - return ( - isinstance(other, Group) - and self._store == other.store - and self._read_only == other.read_only - and self._path == other.path - # N.B., no need to compare attributes, should be covered by - # store comparison - ) - - def __iter__(self): - """Return an iterator over group member names. - - Examples - -------- - >>> import zarr - >>> g1 = zarr.v2.group() - >>> g2 = g1.create_group('foo') - >>> g3 = g1.create_group('bar') - >>> d1 = g1.create_dataset('baz', shape=100, chunks=10) - >>> d2 = g1.create_dataset('quux', shape=200, chunks=20) - >>> for name in g1: - ... print(name) - bar - baz - foo - quux - - """ - - for key in sorted(listdir(self._store, self._path)): - path = self._key_prefix + key - if contains_array(self._store, path) or contains_group(self._store, path): - yield key - - def __len__(self): - """Number of members.""" - return sum(1 for _ in self) - - def __repr__(self): - t = type(self) - r = "<{}.{}".format(t.__module__, t.__name__) - if self.name: - r += " %r" % self.name - if self._read_only: - r += " read-only" - r += ">" - return r - - def __enter__(self): - """Return the Group for use as a context manager.""" - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - """Call the close method of the underlying Store.""" - self.store.close() - - def info_items(self): - def typestr(o): - return "{}.{}".format(type(o).__module__, type(o).__name__) - - items = [] - - # basic info - if self.name is not None: - items += [("Name", self.name)] - items += [ - ("Type", typestr(self)), - ("Read-only", str(self.read_only)), - ] - - # synchronizer - if self._synchronizer is not None: - items += [("Synchronizer type", typestr(self._synchronizer))] - - # storage info - items += [("Store type", typestr(self._store))] - if self._chunk_store is not None: - items += [("Chunk store type", typestr(self._chunk_store))] - - # members - items += [("No. members", len(self))] - array_keys = sorted(self.array_keys()) - group_keys = sorted(self.group_keys()) - items += [("No. arrays", len(array_keys))] - items += [("No. groups", len(group_keys))] - if array_keys: - items += [("Arrays", ", ".join(array_keys))] - if group_keys: - items += [("Groups", ", ".join(group_keys))] - - return items - - def __getstate__(self): - return { - "store": self._store, - "path": self._path, - "read_only": self._read_only, - "chunk_store": self._chunk_store, - "cache_attrs": self._attrs.cache, - "synchronizer": self._synchronizer, - "meta_array": self._meta_array, - } - - def __setstate__(self, state): - self.__init__(**state) - - def _item_path(self, item): - absolute = isinstance(item, str) and item and item[0] == "/" - path = normalize_storage_path(item) - if not absolute and self._path: - path = self._key_prefix + path - return path - - def __contains__(self, item): - """Test for group membership. - - Examples - -------- - >>> import zarr - >>> g1 = zarr.v2.group() - >>> g2 = g1.create_group('foo') - >>> d1 = g1.create_dataset('bar', shape=100, chunks=10) - >>> 'foo' in g1 - True - >>> 'bar' in g1 - True - >>> 'baz' in g1 - False - - """ - path = self._item_path(item) - return contains_array(self._store, path) or contains_group( - self._store, path, explicit_only=False - ) - - def __getitem__(self, item): - """Obtain a group member. - - Parameters - ---------- - item : string - Member name or path. - - Examples - -------- - >>> import zarr - >>> g1 = zarr.v2.group() - >>> d1 = g1.create_dataset('foo/bar/baz', shape=100, chunks=10) - >>> g1['foo'] - - >>> g1['foo/bar'] - - >>> g1['foo/bar/baz'] - - - """ - path = self._item_path(item) - if contains_array(self._store, path): - return Array( - self._store, - read_only=self._read_only, - path=path, - chunk_store=self._chunk_store, - synchronizer=self._synchronizer, - cache_attrs=self.attrs.cache, - meta_array=self._meta_array, - ) - elif contains_group(self._store, path, explicit_only=True): - return Group( - self._store, - read_only=self._read_only, - path=path, - chunk_store=self._chunk_store, - cache_attrs=self.attrs.cache, - synchronizer=self._synchronizer, - meta_array=self._meta_array, - ) - else: - raise KeyError(item) - - def __setitem__(self, item, value): - self.array(item, value, overwrite=True) - - def __delitem__(self, item): - return self._write_op(self._delitem_nosync, item) - - def _delitem_nosync(self, item): - path = self._item_path(item) - if contains_array(self._store, path) or contains_group( - self._store, path, explicit_only=False - ): - rmdir(self._store, path) - else: - raise KeyError(item) - - def __getattr__(self, item): - # allow access to group members via dot notation - try: - return self.__getitem__(item) - except KeyError: - raise AttributeError - - def __dir__(self): - # noinspection PyUnresolvedReferences - base = super().__dir__() - keys = sorted(set(base + list(self))) - keys = [k for k in keys if is_valid_python_name(k)] - return keys - - def _ipython_key_completions_(self): - return sorted(self) - - def group_keys(self): - """Return an iterator over member names for groups only. - - Examples - -------- - >>> import zarr - >>> g1 = zarr.v2.group() - >>> g2 = g1.create_group('foo') - >>> g3 = g1.create_group('bar') - >>> d1 = g1.create_dataset('baz', shape=100, chunks=10) - >>> d2 = g1.create_dataset('quux', shape=200, chunks=20) - >>> sorted(g1.group_keys()) - ['bar', 'foo'] - - """ - - for key in sorted(listdir(self._store, self._path)): - path = self._key_prefix + key - if contains_group(self._store, path): - yield key - - def groups(self): - """Return an iterator over (name, value) pairs for groups only. - - Examples - -------- - >>> import zarr - >>> g1 = zarr.v2.group() - >>> g2 = g1.create_group('foo') - >>> g3 = g1.create_group('bar') - >>> d1 = g1.create_dataset('baz', shape=100, chunks=10) - >>> d2 = g1.create_dataset('quux', shape=200, chunks=20) - >>> for n, v in g1.groups(): - ... print(n, type(v)) - bar - foo - - """ - - for key in sorted(listdir(self._store, self._path)): - path = self._key_prefix + key - if contains_group(self._store, path, explicit_only=False): - yield ( - key, - Group( - self._store, - path=path, - read_only=self._read_only, - chunk_store=self._chunk_store, - cache_attrs=self.attrs.cache, - synchronizer=self._synchronizer, - ), - ) - - def array_keys(self, recurse=False): - """Return an iterator over member names for arrays only. - - Parameters - ---------- - recurse : recurse, optional - Option to return member names for all arrays, even from groups - below the current one. If False, only member names for arrays in - the current group will be returned. Default value is False. - - Examples - -------- - >>> import zarr - >>> g1 = zarr.v2.group() - >>> g2 = g1.create_group('foo') - >>> g3 = g1.create_group('bar') - >>> d1 = g1.create_dataset('baz', shape=100, chunks=10) - >>> d2 = g1.create_dataset('quux', shape=200, chunks=20) - >>> sorted(g1.array_keys()) - ['baz', 'quux'] - - """ - return self._array_iter(keys_only=True, method="array_keys", recurse=recurse) - - def arrays(self, recurse=False): - """Return an iterator over (name, value) pairs for arrays only. - - Parameters - ---------- - recurse : recurse, optional - Option to return (name, value) pairs for all arrays, even from groups - below the current one. If False, only (name, value) pairs for arrays in - the current group will be returned. Default value is False. - - Examples - -------- - >>> import zarr - >>> g1 = zarr.v2.group() - >>> g2 = g1.create_group('foo') - >>> g3 = g1.create_group('bar') - >>> d1 = g1.create_dataset('baz', shape=100, chunks=10) - >>> d2 = g1.create_dataset('quux', shape=200, chunks=20) - >>> for n, v in g1.arrays(): - ... print(n, type(v)) - baz - quux - - """ - return self._array_iter(keys_only=False, method="arrays", recurse=recurse) - - def _array_iter(self, keys_only, method, recurse): - for key in sorted(listdir(self._store, self._path)): - path = self._key_prefix + key - if contains_array(self._store, path): - _key = key.rstrip("/") - yield _key if keys_only else (_key, self[key]) - elif recurse and contains_group(self._store, path): - group = self[key] - yield from getattr(group, method)(recurse=recurse) - - def visitvalues(self, func): - """Run ``func`` on each object. - - Note: If ``func`` returns ``None`` (or doesn't return), - iteration continues. However, if ``func`` returns - anything else, it ceases and returns that value. - - Examples - -------- - >>> import zarr - >>> g1 = zarr.v2.group() - >>> g2 = g1.create_group('foo') - >>> g3 = g1.create_group('bar') - >>> g4 = g3.create_group('baz') - >>> g5 = g3.create_group('quux') - >>> def print_visitor(obj): - ... print(obj) - >>> g1.visitvalues(print_visitor) - - - - - >>> g3.visitvalues(print_visitor) - - - - """ - - def _visit(obj): - yield obj - keys = sorted(getattr(obj, "keys", lambda: [])()) - for k in keys: - yield from _visit(obj[k]) - - for each_obj in islice(_visit(self), 1, None): - value = func(each_obj) - if value is not None: - return value - - def visit(self, func): - """Run ``func`` on each object's path. - - Note: If ``func`` returns ``None`` (or doesn't return), - iteration continues. However, if ``func`` returns - anything else, it ceases and returns that value. - - Examples - -------- - >>> import zarr - >>> g1 = zarr.v2.group() - >>> g2 = g1.create_group('foo') - >>> g3 = g1.create_group('bar') - >>> g4 = g3.create_group('baz') - >>> g5 = g3.create_group('quux') - >>> def print_visitor(name): - ... print(name) - >>> g1.visit(print_visitor) - bar - bar/baz - bar/quux - foo - >>> g3.visit(print_visitor) - baz - quux - - Search for members matching some name query can be implemented using - ``visit`` that is, ``find`` and ``findall``. Consider the following - tree:: - - / - ├── aaa - │ └── bbb - │ └── ccc - │ └── aaa - ├── bar - └── foo - - It is created as follows: - - >>> root = zarr.v2.group() - >>> foo = root.create_group("foo") - >>> bar = root.create_group("bar") - >>> root.create_group("aaa").create_group("bbb").create_group("ccc").create_group("aaa") - - - For ``find``, the first path that matches a given pattern (for example - "aaa") is returned. Note that a non-None value is returned in the visit - function to stop further iteration. - - >>> import re - >>> pattern = re.compile("aaa") - >>> found = None - >>> def find(path): - ... global found - ... if pattern.search(path) is not None: - ... found = path - ... return True - ... - >>> root.visit(find) - True - >>> print(found) - aaa - - For ``findall``, all the results are gathered into a list - - >>> pattern = re.compile("aaa") - >>> found = [] - >>> def findall(path): - ... if pattern.search(path) is not None: - ... found.append(path) - ... - >>> root.visit(findall) - >>> print(found) - ['aaa', 'aaa/bbb', 'aaa/bbb/ccc', 'aaa/bbb/ccc/aaa'] - - To match only on the last part of the path, use a greedy regex to filter - out the prefix: - - >>> prefix_pattern = re.compile(r".*/") - >>> pattern = re.compile("aaa") - >>> found = [] - >>> def findall(path): - ... match = prefix_pattern.match(path) - ... if match is None: - ... name = path - ... else: - ... _, end = match.span() - ... name = path[end:] - ... if pattern.search(name) is not None: - ... found.append(path) - ... return None - ... - >>> root.visit(findall) - >>> print(found) - ['aaa', 'aaa/bbb/ccc/aaa'] - """ - - base_len = len(self.name) - return self.visitvalues(lambda o: func(o.name[base_len:].lstrip("/"))) - - def visitkeys(self, func): - """An alias for :py:meth:`~Group.visit`.""" - - return self.visit(func) - - def visititems(self, func): - """Run ``func`` on each object's path and the object itself. - - Note: If ``func`` returns ``None`` (or doesn't return), - iteration continues. However, if ``func`` returns - anything else, it ceases and returns that value. - - Examples - -------- - >>> import zarr - >>> g1 = zarr.v2.group() - >>> g2 = g1.create_group('foo') - >>> g3 = g1.create_group('bar') - >>> g4 = g3.create_group('baz') - >>> g5 = g3.create_group('quux') - >>> def print_visitor(name, obj): - ... print((name, obj)) - >>> g1.visititems(print_visitor) - ('bar', ) - ('bar/baz', ) - ('bar/quux', ) - ('foo', ) - >>> g3.visititems(print_visitor) - ('baz', ) - ('quux', ) - - """ - - base_len = len(self.name) - return self.visitvalues(lambda o: func(o.name[base_len:].lstrip("/"), o)) - - def tree(self, expand=False, level=None): - """Provide a ``print``-able display of the hierarchy. - - Parameters - ---------- - expand : bool, optional - Only relevant for HTML representation. If True, tree will be fully expanded. - level : int, optional - Maximum depth to descend into hierarchy. - - Examples - -------- - >>> import zarr - >>> g1 = zarr.v2.group() - >>> g2 = g1.create_group('foo') - >>> g3 = g1.create_group('bar') - >>> g4 = g3.create_group('baz') - >>> g5 = g3.create_group('quux') - >>> d1 = g5.create_dataset('baz', shape=100, chunks=10) - >>> g1.tree() - / - ├── bar - │ ├── baz - │ └── quux - │ └── baz (100,) float64 - └── foo - >>> g1.tree(level=2) - / - ├── bar - │ ├── baz - │ └── quux - └── foo - >>> g3.tree() - bar - ├── baz - └── quux - └── baz (100,) float64 - - Notes - ----- - Please note that this is an experimental feature. The behaviour of this - function is still evolving and the default output and/or parameters may change - in future versions. - - """ - - return TreeViewer(self, expand=expand, level=level) - - def _write_op(self, f, *args, **kwargs): - # guard condition - if self._read_only: - raise ReadOnlyError - - if self._synchronizer is None: - # no synchronization - lock = nolock - else: - # synchronize on the root group - lock = self._synchronizer[group_meta_key] - - with lock: - return f(*args, **kwargs) - - def create_group(self, name, overwrite=False): - """Create a sub-group. - - Parameters - ---------- - name : string - Group name. - overwrite : bool, optional - If True, overwrite any existing array with the given name. - - Returns - ------- - g : zarr.v2.hierarchy.Group - - Examples - -------- - >>> import zarr - >>> g1 = zarr.v2.group() - >>> g2 = g1.create_group('foo') - >>> g3 = g1.create_group('bar') - >>> g4 = g1.create_group('baz/quux') - - """ - - return self._write_op(self._create_group_nosync, name, overwrite=overwrite) - - def _create_group_nosync(self, name, overwrite=False): - path = self._item_path(name) - - # create terminal group - init_group(self._store, path=path, chunk_store=self._chunk_store, overwrite=overwrite) - - return Group( - self._store, - path=path, - read_only=self._read_only, - chunk_store=self._chunk_store, - cache_attrs=self.attrs.cache, - synchronizer=self._synchronizer, - ) - - def create_groups(self, *names, **kwargs): - """Convenience method to create multiple groups in a single call.""" - return tuple(self.create_group(name, **kwargs) for name in names) - - def require_group(self, name, overwrite=False): - """Obtain a sub-group, creating one if it doesn't exist. - - Parameters - ---------- - name : string - Group name. - overwrite : bool, optional - Overwrite any existing array with given `name` if present. - - Returns - ------- - g : zarr.v2.hierarchy.Group - - Examples - -------- - >>> import zarr - >>> g1 = zarr.v2.group() - >>> g2 = g1.require_group('foo') - >>> g3 = g1.require_group('foo') - >>> g2 == g3 - True - - """ - - return self._write_op(self._require_group_nosync, name, overwrite=overwrite) - - def _require_group_nosync(self, name, overwrite=False): - path = self._item_path(name) - - # create terminal group if necessary - if not contains_group(self._store, path): - init_group( - store=self._store, path=path, chunk_store=self._chunk_store, overwrite=overwrite - ) - - return Group( - self._store, - path=path, - read_only=self._read_only, - chunk_store=self._chunk_store, - cache_attrs=self.attrs.cache, - synchronizer=self._synchronizer, - ) - - def require_groups(self, *names): - """Convenience method to require multiple groups in a single call.""" - return tuple(self.require_group(name) for name in names) - - # noinspection PyIncorrectDocstring - def create_dataset(self, name, **kwargs): - """Create an array. - - Arrays are known as "datasets" in HDF5 terminology. For compatibility - with h5py, Zarr groups also implement the require_dataset() method. - - Parameters - ---------- - name : string - Array name. - data : array-like, optional - Initial data. - shape : int or tuple of ints - Array shape. - chunks : int or tuple of ints, optional - Chunk shape. If not provided, will be guessed from `shape` and - `dtype`. - dtype : string or dtype, optional - NumPy dtype. - compressor : Codec, optional - Primary compressor. - fill_value : object - Default value to use for uninitialized portions of the array. - order : {'C', 'F'}, optional - Memory layout to be used within each chunk. - synchronizer : zarr.v2.sync.ArraySynchronizer, optional - Array synchronizer. - filters : sequence of Codecs, optional - Sequence of filters to use to encode chunk data prior to - compression. - overwrite : bool, optional - If True, replace any existing array or group with the given name. - cache_metadata : bool, optional - If True, array configuration metadata will be cached for the - lifetime of the object. If False, array metadata will be reloaded - prior to all data access and modification operations (may incur - overhead depending on storage and data access pattern). - dimension_separator : {'.', '/'}, optional - Separator placed between the dimensions of a chunk. - - Returns - ------- - a : zarr.v2.core.Array - - Examples - -------- - >>> import zarr - >>> g1 = zarr.v2.group() - >>> d1 = g1.create_dataset('foo', shape=(10000, 10000), - ... chunks=(1000, 1000)) - >>> d1 - - >>> d2 = g1.create_dataset('bar/baz/qux', shape=(100, 100, 100), - ... chunks=(100, 10, 10)) - >>> d2 - - - """ - assert "mode" not in kwargs - - return self._write_op(self._create_dataset_nosync, name, **kwargs) - - def _create_dataset_nosync(self, name, data=None, **kwargs): - assert "mode" not in kwargs - path = self._item_path(name) - - # determine synchronizer - kwargs.setdefault("synchronizer", self._synchronizer) - kwargs.setdefault("cache_attrs", self.attrs.cache) - - # create array - if data is None: - a = create(store=self._store, path=path, chunk_store=self._chunk_store, **kwargs) - - else: - a = array(data, store=self._store, path=path, chunk_store=self._chunk_store, **kwargs) - - return a - - def require_dataset(self, name, shape, dtype=None, exact=False, **kwargs): - """Obtain an array, creating if it doesn't exist. - - Arrays are known as "datasets" in HDF5 terminology. For compatibility - with h5py, Zarr groups also implement the create_dataset() method. - - Other `kwargs` are as per :func:`zarr.v2.hierarchy.Group.create_dataset`. - - Parameters - ---------- - name : string - Array name. - shape : int or tuple of ints - Array shape. - dtype : string or dtype, optional - NumPy dtype. - exact : bool, optional - If True, require `dtype` to match exactly. If false, require - `dtype` can be cast from array dtype. - - """ - - return self._write_op( - self._require_dataset_nosync, name, shape=shape, dtype=dtype, exact=exact, **kwargs - ) - - def _require_dataset_nosync(self, name, shape, dtype=None, exact=False, **kwargs): - path = self._item_path(name) - - if contains_array(self._store, path): - # array already exists at path, validate that it is the right shape and type - - synchronizer = kwargs.get("synchronizer", self._synchronizer) - cache_metadata = kwargs.get("cache_metadata", True) - cache_attrs = kwargs.get("cache_attrs", self.attrs.cache) - a = Array( - self._store, - path=path, - read_only=self._read_only, - chunk_store=self._chunk_store, - synchronizer=synchronizer, - cache_metadata=cache_metadata, - cache_attrs=cache_attrs, - meta_array=self._meta_array, - ) - shape = normalize_shape(shape) - if shape != a.shape: - raise TypeError( - "shape do not match existing array; expected {}, got {}".format(a.shape, shape) - ) - dtype = np.dtype(dtype) - if exact: - if dtype != a.dtype: - raise TypeError( - "dtypes do not match exactly; expected {}, got {}".format(a.dtype, dtype) - ) - else: - if not np.can_cast(dtype, a.dtype): - raise TypeError("dtypes ({}, {}) cannot be safely cast".format(dtype, a.dtype)) - return a - - else: - return self._create_dataset_nosync(name, shape=shape, dtype=dtype, **kwargs) - - def create(self, name, **kwargs): - """Create an array. Keyword arguments as per - :func:`zarr.v2.creation.create`.""" - return self._write_op(self._create_nosync, name, **kwargs) - - def _create_nosync(self, name, **kwargs): - path = self._item_path(name) - kwargs.setdefault("synchronizer", self._synchronizer) - kwargs.setdefault("cache_attrs", self.attrs.cache) - return create(store=self._store, path=path, chunk_store=self._chunk_store, **kwargs) - - def empty(self, name, **kwargs): - """Create an array. Keyword arguments as per - :func:`zarr.v2.creation.empty`.""" - return self._write_op(self._empty_nosync, name, **kwargs) - - def _empty_nosync(self, name, **kwargs): - path = self._item_path(name) - kwargs.setdefault("synchronizer", self._synchronizer) - kwargs.setdefault("cache_attrs", self.attrs.cache) - return empty(store=self._store, path=path, chunk_store=self._chunk_store, **kwargs) - - def zeros(self, name, **kwargs): - """Create an array. Keyword arguments as per - :func:`zarr.v2.creation.zeros`.""" - return self._write_op(self._zeros_nosync, name, **kwargs) - - def _zeros_nosync(self, name, **kwargs): - path = self._item_path(name) - kwargs.setdefault("synchronizer", self._synchronizer) - kwargs.setdefault("cache_attrs", self.attrs.cache) - return zeros(store=self._store, path=path, chunk_store=self._chunk_store, **kwargs) - - def ones(self, name, **kwargs): - """Create an array. Keyword arguments as per - :func:`zarr.v2.creation.ones`.""" - return self._write_op(self._ones_nosync, name, **kwargs) - - def _ones_nosync(self, name, **kwargs): - path = self._item_path(name) - kwargs.setdefault("synchronizer", self._synchronizer) - kwargs.setdefault("cache_attrs", self.attrs.cache) - return ones(store=self._store, path=path, chunk_store=self._chunk_store, **kwargs) - - def full(self, name, fill_value, **kwargs): - """Create an array. Keyword arguments as per - :func:`zarr.v2.creation.full`.""" - return self._write_op(self._full_nosync, name, fill_value, **kwargs) - - def _full_nosync(self, name, fill_value, **kwargs): - path = self._item_path(name) - kwargs.setdefault("synchronizer", self._synchronizer) - kwargs.setdefault("cache_attrs", self.attrs.cache) - return full( - store=self._store, - path=path, - chunk_store=self._chunk_store, - fill_value=fill_value, - **kwargs, - ) - - def array(self, name, data, **kwargs): - """Create an array. Keyword arguments as per - :func:`zarr.v2.creation.array`.""" - return self._write_op(self._array_nosync, name, data, **kwargs) - - def _array_nosync(self, name, data, **kwargs): - path = self._item_path(name) - kwargs.setdefault("synchronizer", self._synchronizer) - kwargs.setdefault("cache_attrs", self.attrs.cache) - return array(data, store=self._store, path=path, chunk_store=self._chunk_store, **kwargs) - - def empty_like(self, name, data, **kwargs): - """Create an array. Keyword arguments as per - :func:`zarr.v2.creation.empty_like`.""" - return self._write_op(self._empty_like_nosync, name, data, **kwargs) - - def _empty_like_nosync(self, name, data, **kwargs): - path = self._item_path(name) - kwargs.setdefault("synchronizer", self._synchronizer) - kwargs.setdefault("cache_attrs", self.attrs.cache) - return empty_like( - data, store=self._store, path=path, chunk_store=self._chunk_store, **kwargs - ) - - def zeros_like(self, name, data, **kwargs): - """Create an array. Keyword arguments as per - :func:`zarr.v2.creation.zeros_like`.""" - return self._write_op(self._zeros_like_nosync, name, data, **kwargs) - - def _zeros_like_nosync(self, name, data, **kwargs): - path = self._item_path(name) - kwargs.setdefault("synchronizer", self._synchronizer) - kwargs.setdefault("cache_attrs", self.attrs.cache) - return zeros_like( - data, store=self._store, path=path, chunk_store=self._chunk_store, **kwargs - ) - - def ones_like(self, name, data, **kwargs): - """Create an array. Keyword arguments as per - :func:`zarr.v2.creation.ones_like`.""" - return self._write_op(self._ones_like_nosync, name, data, **kwargs) - - def _ones_like_nosync(self, name, data, **kwargs): - path = self._item_path(name) - kwargs.setdefault("synchronizer", self._synchronizer) - kwargs.setdefault("cache_attrs", self.attrs.cache) - return ones_like( - data, store=self._store, path=path, chunk_store=self._chunk_store, **kwargs - ) - - def full_like(self, name, data, **kwargs): - """Create an array. Keyword arguments as per - :func:`zarr.v2.creation.full_like`.""" - return self._write_op(self._full_like_nosync, name, data, **kwargs) - - def _full_like_nosync(self, name, data, **kwargs): - path = self._item_path(name) - kwargs.setdefault("synchronizer", self._synchronizer) - kwargs.setdefault("cache_attrs", self.attrs.cache) - return full_like( - data, store=self._store, path=path, chunk_store=self._chunk_store, **kwargs - ) - - def _move_nosync(self, path, new_path): - rename(self._store, path, new_path) - if self._chunk_store is not None: - rename(self._chunk_store, path, new_path) - - def move(self, source, dest): - """Move contents from one path to another relative to the Group. - - Parameters - ---------- - source : string - Name or path to a Zarr object to move. - dest : string - New name or path of the Zarr object. - """ - - source = self._item_path(source) - dest = self._item_path(dest) - - # Check that source exists. - if not ( - contains_array(self._store, source) - or contains_group(self._store, source, explicit_only=False) - ): - raise ValueError(f'The source, "{source}", does not exist.') - if contains_array(self._store, dest) or contains_group( - self._store, dest, explicit_only=False - ): - raise ValueError(f'The dest, "{dest}", already exists.') - - # Ensure groups needed for `dest` exist. - if "/" in dest: - self.require_group("/" + dest.rsplit("/", 1)[0]) - - self._write_op(self._move_nosync, source, dest) - - -def _normalize_store_arg(store, *, storage_options=None, mode="r"): - if store is None: - return MemoryStore() - return normalize_store_arg(store, storage_options=storage_options, mode=mode) - - -def group( - store=None, - overwrite=False, - chunk_store=None, - cache_attrs=True, - synchronizer=None, - path=None, - *, - meta_array=None, -): - """Create a group. - - Parameters - ---------- - store : MutableMapping or string, optional - Store or path to directory in file system. - overwrite : bool, optional - If True, delete any pre-existing data in `store` at `path` before - creating the group. - chunk_store : MutableMapping, optional - Separate storage for chunks. If not provided, `store` will be used - for storage of both chunks and metadata. - cache_attrs : bool, optional - If True (default), user attributes will be cached for attribute read - operations. If False, user attributes are reloaded from the store prior - to all attribute read operations. - synchronizer : object, optional - Array synchronizer. - path : string, optional - Group path within store. - meta_array : array-like, optional - An array instance to use for determining arrays to create and return - to users. Use `numpy.empty(())` by default. - - .. versionadded:: 2.16.1 - - Returns - ------- - g : zarr.v2.hierarchy.Group - - Examples - -------- - Create a group in memory:: - - >>> import zarr - >>> g = zarr.v2.group() - >>> g - - - Create a group with a different store:: - - >>> store = zarr.v2.DirectoryStore('data/example.zarr') - >>> g = zarr.v2.group(store=store, overwrite=True) - >>> g - - - """ - - # handle polymorphic store arg - store = _normalize_store_arg(store, mode="w") - - path = normalize_storage_path(path) - - requires_init = overwrite or not contains_group(store) - - if requires_init: - init_group(store, overwrite=overwrite, chunk_store=chunk_store, path=path) - - return Group( - store, - read_only=False, - chunk_store=chunk_store, - cache_attrs=cache_attrs, - synchronizer=synchronizer, - path=path, - meta_array=meta_array, - ) - - -def open_group( - store=None, - mode="a", - cache_attrs=True, - synchronizer=None, - path=None, - chunk_store=None, - storage_options=None, - *, - meta_array=None, -): - """Open a group using file-mode-like semantics. - - Parameters - ---------- - store : MutableMapping or string, optional - Store or path to directory in file system or name of zip file. - mode : {'r', 'r+', 'a', 'w', 'w-'}, optional - Persistence mode: 'r' means read only (must exist); 'r+' means - read/write (must exist); 'a' means read/write (create if doesn't - exist); 'w' means create (overwrite if exists); 'w-' means create - (fail if exists). - cache_attrs : bool, optional - If True (default), user attributes will be cached for attribute read - operations. If False, user attributes are reloaded from the store prior - to all attribute read operations. - synchronizer : object, optional - Array synchronizer. - path : string, optional - Group path within store. - chunk_store : MutableMapping or string, optional - Store or path to directory in file system or name of zip file. - storage_options : dict - If using an fsspec URL to create the store, these will be passed to - the backend implementation. Ignored otherwise. - meta_array : array-like, optional - An array instance to use for determining arrays to create and return - to users. Use `numpy.empty(())` by default. - - .. versionadded:: 2.13 - - Returns - ------- - g : zarr.v2.hierarchy.Group - - Examples - -------- - >>> import zarr - >>> root = zarr.v2.open_group('data/example.zarr', mode='w') - >>> foo = root.create_group('foo') - >>> bar = root.create_group('bar') - >>> root - - >>> root2 = zarr.v2.open_group('data/example.zarr', mode='a') - >>> root2 - - >>> root == root2 - True - - """ - - # handle polymorphic store arg - store = _normalize_store_arg(store, storage_options=storage_options, mode=mode) - - if chunk_store is not None: - chunk_store = _normalize_store_arg(chunk_store, storage_options=storage_options, mode=mode) - - path = normalize_storage_path(path) - - # ensure store is initialized - - if mode in ["r", "r+"]: - if not contains_group(store, path=path): - if contains_array(store, path=path): - raise ContainsArrayError(path) - raise GroupNotFoundError(path) - - elif mode == "w": - init_group(store, overwrite=True, path=path, chunk_store=chunk_store) - - elif mode == "a": - if not contains_group(store, path=path): - if contains_array(store, path=path): - raise ContainsArrayError(path) - init_group(store, path=path, chunk_store=chunk_store) - - elif mode in ["w-", "x"]: - if contains_array(store, path=path): - raise ContainsArrayError(path) - elif contains_group(store, path=path): - raise ContainsGroupError(path) - else: - init_group(store, path=path, chunk_store=chunk_store) - - # determine read only status - read_only = mode == "r" - - return Group( - store, - read_only=read_only, - cache_attrs=cache_attrs, - synchronizer=synchronizer, - path=path, - chunk_store=chunk_store, - meta_array=meta_array, - ) diff --git a/src/zarr/v2/indexing.py b/src/zarr/v2/indexing.py deleted file mode 100644 index 880baf3f72..0000000000 --- a/src/zarr/v2/indexing.py +++ /dev/null @@ -1,1074 +0,0 @@ -import collections -import itertools -import math -import numbers - -import numpy as np - - -from zarr.v2.errors import ( - ArrayIndexError, - NegativeStepError, - err_too_many_indices, - VindexInvalidSelectionError, - BoundsCheckError, -) - - -def is_integer(x): - """True if x is an integer (both pure Python or NumPy). - - Note that Python's bool is considered an integer too. - """ - return isinstance(x, numbers.Integral) - - -def is_integer_list(x): - """True if x is a list of integers. - - This function assumes ie *does not check* that all elements of the list - have the same type. Mixed type lists will result in other errors that will - bubble up anyway. - """ - return isinstance(x, list) and len(x) > 0 and is_integer(x[0]) - - -def is_integer_array(x, ndim=None): - t = not np.isscalar(x) and hasattr(x, "shape") and hasattr(x, "dtype") and x.dtype.kind in "ui" - if ndim is not None: - t = t and len(x.shape) == ndim - return t - - -def is_bool_array(x, ndim=None): - t = hasattr(x, "shape") and hasattr(x, "dtype") and x.dtype == bool - if ndim is not None: - t = t and len(x.shape) == ndim - return t - - -def is_scalar(value, dtype): - if np.isscalar(value): - return True - if isinstance(value, tuple) and dtype.names and len(value) == len(dtype.names): - return True - return False - - -def is_pure_fancy_indexing(selection, ndim): - """Check whether a selection contains only scalars or integer array-likes. - - Parameters - ---------- - selection : tuple, slice, or scalar - A valid selection value for indexing into arrays. - - Returns - ------- - is_pure : bool - True if the selection is a pure fancy indexing expression (ie not mixed - with boolean or slices). - """ - if ndim == 1: - if is_integer_list(selection) or is_integer_array(selection): - return True - # if not, we go through the normal path below, because a 1-tuple - # of integers is also allowed. - no_slicing = ( - isinstance(selection, tuple) - and len(selection) == ndim - and not (any(isinstance(elem, slice) or elem is Ellipsis for elem in selection)) - ) - return ( - no_slicing - and all( - is_integer(elem) or is_integer_list(elem) or is_integer_array(elem) - for elem in selection - ) - and any(is_integer_list(elem) or is_integer_array(elem) for elem in selection) - ) - - -def is_pure_orthogonal_indexing(selection, ndim): - if not ndim: - return False - - # Case 1: Selection is a single iterable of integers - if is_integer_list(selection) or is_integer_array(selection, ndim=1): - return True - - # Case two: selection contains either zero or one integer iterables. - # All other selection elements are slices or integers - return ( - isinstance(selection, tuple) - and len(selection) == ndim - and sum(is_integer_list(elem) or is_integer_array(elem) for elem in selection) <= 1 - and all( - is_integer_list(elem) or is_integer_array(elem) or isinstance(elem, (int, slice)) - for elem in selection - ) - ) - - -def normalize_integer_selection(dim_sel, dim_len): - # normalize type to int - dim_sel = int(dim_sel) - - # handle wraparound - if dim_sel < 0: - dim_sel = dim_len + dim_sel - - # handle out of bounds - if dim_sel >= dim_len or dim_sel < 0: - raise BoundsCheckError(dim_len) - - return dim_sel - - -ChunkDimProjection = collections.namedtuple( - "ChunkDimProjection", ("dim_chunk_ix", "dim_chunk_sel", "dim_out_sel") -) -"""A mapping from chunk to output array for a single dimension. - -Parameters ----------- -dim_chunk_ix - Index of chunk. -dim_chunk_sel - Selection of items from chunk array. -dim_out_sel - Selection of items in target (output) array. - -""" - - -class IntDimIndexer: - def __init__(self, dim_sel, dim_len, dim_chunk_len): - # normalize - dim_sel = normalize_integer_selection(dim_sel, dim_len) - - # store attributes - self.dim_sel = dim_sel - self.dim_len = dim_len - self.dim_chunk_len = dim_chunk_len - self.nitems = 1 - - def __iter__(self): - dim_chunk_ix = self.dim_sel // self.dim_chunk_len - dim_offset = dim_chunk_ix * self.dim_chunk_len - dim_chunk_sel = self.dim_sel - dim_offset - dim_out_sel = None - yield ChunkDimProjection(dim_chunk_ix, dim_chunk_sel, dim_out_sel) - - -def ceildiv(a, b): - return math.ceil(a / b) - - -class SliceDimIndexer: - def __init__(self, dim_sel, dim_len, dim_chunk_len): - # normalize - self.start, self.stop, self.step = dim_sel.indices(dim_len) - if self.step < 1: - raise NegativeStepError - - # store attributes - self.dim_len = dim_len - self.dim_chunk_len = dim_chunk_len - self.nitems = max(0, ceildiv((self.stop - self.start), self.step)) - self.nchunks = ceildiv(self.dim_len, self.dim_chunk_len) - - def __iter__(self): - # figure out the range of chunks we need to visit - dim_chunk_ix_from = self.start // self.dim_chunk_len - dim_chunk_ix_to = ceildiv(self.stop, self.dim_chunk_len) - - # iterate over chunks in range - for dim_chunk_ix in range(dim_chunk_ix_from, dim_chunk_ix_to): - # compute offsets for chunk within overall array - dim_offset = dim_chunk_ix * self.dim_chunk_len - dim_limit = min(self.dim_len, (dim_chunk_ix + 1) * self.dim_chunk_len) - - # determine chunk length, accounting for trailing chunk - dim_chunk_len = dim_limit - dim_offset - - if self.start < dim_offset: - # selection starts before current chunk - dim_chunk_sel_start = 0 - remainder = (dim_offset - self.start) % self.step - if remainder: - dim_chunk_sel_start += self.step - remainder - # compute number of previous items, provides offset into output array - dim_out_offset = ceildiv((dim_offset - self.start), self.step) - - else: - # selection starts within current chunk - dim_chunk_sel_start = self.start - dim_offset - dim_out_offset = 0 - - if self.stop > dim_limit: - # selection ends after current chunk - dim_chunk_sel_stop = dim_chunk_len - - else: - # selection ends within current chunk - dim_chunk_sel_stop = self.stop - dim_offset - - dim_chunk_sel = slice(dim_chunk_sel_start, dim_chunk_sel_stop, self.step) - dim_chunk_nitems = ceildiv((dim_chunk_sel_stop - dim_chunk_sel_start), self.step) - - # If there are no elements on the selection within this chunk, then skip - if dim_chunk_nitems == 0: - continue - - dim_out_sel = slice(dim_out_offset, dim_out_offset + dim_chunk_nitems) - - yield ChunkDimProjection(dim_chunk_ix, dim_chunk_sel, dim_out_sel) - - -def check_selection_length(selection, shape): - if len(selection) > len(shape): - err_too_many_indices(selection, shape) - - -def replace_ellipsis(selection, shape): - selection = ensure_tuple(selection) - - # count number of ellipsis present - n_ellipsis = sum(1 for i in selection if i is Ellipsis) - - if n_ellipsis > 1: - # more than 1 is an error - raise IndexError("an index can only have a single ellipsis ('...')") - - elif n_ellipsis == 1: - # locate the ellipsis, count how many items to left and right - n_items_l = selection.index(Ellipsis) # items to left of ellipsis - n_items_r = len(selection) - (n_items_l + 1) # items to right of ellipsis - n_items = len(selection) - 1 # all non-ellipsis items - - if n_items >= len(shape): - # ellipsis does nothing, just remove it - selection = tuple(i for i in selection if i != Ellipsis) - - else: - # replace ellipsis with as many slices are needed for number of dims - new_item = selection[:n_items_l] + ((slice(None),) * (len(shape) - n_items)) - if n_items_r: - new_item += selection[-n_items_r:] - selection = new_item - - # fill out selection if not completely specified - if len(selection) < len(shape): - selection += (slice(None),) * (len(shape) - len(selection)) - - # check selection not too long - check_selection_length(selection, shape) - - return selection - - -def replace_lists(selection): - return tuple( - np.asarray(dim_sel) if isinstance(dim_sel, list) else dim_sel for dim_sel in selection - ) - - -def ensure_tuple(v): - if not isinstance(v, tuple): - v = (v,) - return v - - -ChunkProjection = collections.namedtuple( - "ChunkProjection", ("chunk_coords", "chunk_selection", "out_selection") -) -"""A mapping of items from chunk to output array. Can be used to extract items from the -chunk array for loading into an output array. Can also be used to extract items from a -value array for setting/updating in a chunk array. - -Parameters ----------- -chunk_coords - Indices of chunk. -chunk_selection - Selection of items from chunk array. -out_selection - Selection of items in target (output) array. - -""" - - -def is_slice(s): - return isinstance(s, slice) - - -def is_contiguous_slice(s): - return is_slice(s) and (s.step is None or s.step == 1) - - -def is_positive_slice(s): - return is_slice(s) and (s.step is None or s.step >= 1) - - -def is_contiguous_selection(selection): - selection = ensure_tuple(selection) - return all((is_integer_array(s) or is_contiguous_slice(s) or s == Ellipsis) for s in selection) - - -def is_basic_selection(selection): - selection = ensure_tuple(selection) - return all(is_integer(s) or is_positive_slice(s) for s in selection) - - -# noinspection PyProtectedMember -class BasicIndexer: - def __init__(self, selection, array): - # handle ellipsis - selection = replace_ellipsis(selection, array._shape) - - # setup per-dimension indexers - dim_indexers = [] - for dim_sel, dim_len, dim_chunk_len in zip(selection, array._shape, array._chunks): - if is_integer(dim_sel): - dim_indexer = IntDimIndexer(dim_sel, dim_len, dim_chunk_len) - - elif is_slice(dim_sel): - dim_indexer = SliceDimIndexer(dim_sel, dim_len, dim_chunk_len) - - else: - raise IndexError( - "unsupported selection item for basic indexing; " - "expected integer or slice, got {!r}".format(type(dim_sel)) - ) - - dim_indexers.append(dim_indexer) - - self.dim_indexers = dim_indexers - self.shape = tuple(s.nitems for s in self.dim_indexers if not isinstance(s, IntDimIndexer)) - self.drop_axes = () - - def __iter__(self): - for dim_projections in itertools.product(*self.dim_indexers): - chunk_coords = tuple(p.dim_chunk_ix for p in dim_projections) - chunk_selection = tuple(p.dim_chunk_sel for p in dim_projections) - out_selection = tuple( - p.dim_out_sel for p in dim_projections if p.dim_out_sel is not None - ) - - yield ChunkProjection(chunk_coords, chunk_selection, out_selection) - - -class BoolArrayDimIndexer: - def __init__(self, dim_sel, dim_len, dim_chunk_len): - # check number of dimensions - if not is_bool_array(dim_sel, 1): - raise IndexError("Boolean arrays in an orthogonal selection must be 1-dimensional only") - - # check shape - if dim_sel.shape[0] != dim_len: - raise IndexError( - "Boolean array has the wrong length for dimension; expected {}, got {}".format( - dim_len, dim_sel.shape[0] - ) - ) - - # store attributes - self.dim_sel = dim_sel - self.dim_len = dim_len - self.dim_chunk_len = dim_chunk_len - self.nchunks = ceildiv(self.dim_len, self.dim_chunk_len) - - # precompute number of selected items for each chunk - self.chunk_nitems = np.zeros(self.nchunks, dtype="i8") - for dim_chunk_ix in range(self.nchunks): - dim_offset = dim_chunk_ix * self.dim_chunk_len - self.chunk_nitems[dim_chunk_ix] = np.count_nonzero( - self.dim_sel[dim_offset : dim_offset + self.dim_chunk_len] - ) - self.chunk_nitems_cumsum = np.cumsum(self.chunk_nitems) - self.nitems = self.chunk_nitems_cumsum[-1] - self.dim_chunk_ixs = np.nonzero(self.chunk_nitems)[0] - - def __iter__(self): - # iterate over chunks with at least one item - for dim_chunk_ix in self.dim_chunk_ixs: - # find region in chunk - dim_offset = dim_chunk_ix * self.dim_chunk_len - dim_chunk_sel = self.dim_sel[dim_offset : dim_offset + self.dim_chunk_len] - - # pad out if final chunk - if dim_chunk_sel.shape[0] < self.dim_chunk_len: - tmp = np.zeros(self.dim_chunk_len, dtype=bool) - tmp[: dim_chunk_sel.shape[0]] = dim_chunk_sel - dim_chunk_sel = tmp - - # find region in output - if dim_chunk_ix == 0: - start = 0 - else: - start = self.chunk_nitems_cumsum[dim_chunk_ix - 1] - stop = self.chunk_nitems_cumsum[dim_chunk_ix] - dim_out_sel = slice(start, stop) - - yield ChunkDimProjection(dim_chunk_ix, dim_chunk_sel, dim_out_sel) - - -class Order: - UNKNOWN = 0 - INCREASING = 1 - DECREASING = 2 - UNORDERED = 3 - - @staticmethod - def check(a): - diff = np.diff(a) - diff_positive = diff >= 0 - n_diff_positive = np.count_nonzero(diff_positive) - all_increasing = n_diff_positive == len(diff_positive) - any_increasing = n_diff_positive > 0 - if all_increasing: - order = Order.INCREASING - elif any_increasing: - order = Order.UNORDERED - else: - order = Order.DECREASING - return order - - -def wraparound_indices(x, dim_len): - loc_neg = x < 0 - if np.any(loc_neg): - x[loc_neg] = x[loc_neg] + dim_len - - -def boundscheck_indices(x, dim_len): - if np.any(x < 0) or np.any(x >= dim_len): - raise BoundsCheckError(dim_len) - - -class IntArrayDimIndexer: - """Integer array selection against a single dimension.""" - - def __init__( - self, - dim_sel, - dim_len, - dim_chunk_len, - wraparound=True, - boundscheck=True, - order=Order.UNKNOWN, - ): - # ensure 1d array - dim_sel = np.asanyarray(dim_sel) - if not is_integer_array(dim_sel, 1): - raise IndexError("integer arrays in an orthogonal selection must be 1-dimensional only") - - # handle wraparound - if wraparound: - wraparound_indices(dim_sel, dim_len) - - # handle out of bounds - if boundscheck: - boundscheck_indices(dim_sel, dim_len) - - # store attributes - self.dim_len = dim_len - self.dim_chunk_len = dim_chunk_len - self.nchunks = ceildiv(self.dim_len, self.dim_chunk_len) - self.nitems = len(dim_sel) - - # determine which chunk is needed for each selection item - # note: for dense integer selections, the division operation here is the - # bottleneck - dim_sel_chunk = dim_sel // dim_chunk_len - - # determine order of indices - if order == Order.UNKNOWN: - order = Order.check(dim_sel) - self.order = order - - if self.order == Order.INCREASING: - self.dim_sel = dim_sel - self.dim_out_sel = None - elif self.order == Order.DECREASING: - self.dim_sel = dim_sel[::-1] - # TODO should be possible to do this without creating an arange - self.dim_out_sel = np.arange(self.nitems - 1, -1, -1) - else: - # sort indices to group by chunk - self.dim_out_sel = np.argsort(dim_sel_chunk) - self.dim_sel = np.take(dim_sel, self.dim_out_sel) - - # precompute number of selected items for each chunk - self.chunk_nitems = np.bincount(dim_sel_chunk, minlength=self.nchunks) - - # find chunks that we need to visit - self.dim_chunk_ixs = np.nonzero(self.chunk_nitems)[0] - - # compute offsets into the output array - self.chunk_nitems_cumsum = np.cumsum(self.chunk_nitems) - - def __iter__(self): - for dim_chunk_ix in self.dim_chunk_ixs: - # find region in output - if dim_chunk_ix == 0: - start = 0 - else: - start = self.chunk_nitems_cumsum[dim_chunk_ix - 1] - stop = self.chunk_nitems_cumsum[dim_chunk_ix] - if self.order == Order.INCREASING: - dim_out_sel = slice(start, stop) - else: - dim_out_sel = self.dim_out_sel[start:stop] - - # find region in chunk - dim_offset = dim_chunk_ix * self.dim_chunk_len - dim_chunk_sel = self.dim_sel[start:stop] - dim_offset - - yield ChunkDimProjection(dim_chunk_ix, dim_chunk_sel, dim_out_sel) - - -def slice_to_range(s: slice, l: int): # noqa: E741 - return range(*s.indices(l)) - - -def ix_(selection, shape): - """Convert an orthogonal selection to a numpy advanced (fancy) selection, like ``numpy.ix_`` - but with support for slices and single ints.""" - - # normalisation - selection = replace_ellipsis(selection, shape) - - # replace slice and int as these are not supported by numpy.ix_ - selection = [ - slice_to_range(dim_sel, dim_len) - if isinstance(dim_sel, slice) - else [dim_sel] - if is_integer(dim_sel) - else dim_sel - for dim_sel, dim_len in zip(selection, shape) - ] - - # now get numpy to convert to a coordinate selection - selection = np.ix_(*selection) - - return selection - - -def oindex(a, selection): - """Implementation of orthogonal indexing with slices and ints.""" - selection = replace_ellipsis(selection, a.shape) - drop_axes = tuple(i for i, s in enumerate(selection) if is_integer(s)) - selection = ix_(selection, a.shape) - result = a[selection] - if drop_axes: - result = result.squeeze(axis=drop_axes) - return result - - -def oindex_set(a, selection, value): - selection = replace_ellipsis(selection, a.shape) - drop_axes = tuple(i for i, s in enumerate(selection) if is_integer(s)) - selection = ix_(selection, a.shape) - if not np.isscalar(value) and drop_axes: - value = np.asanyarray(value) - value_selection = [slice(None)] * len(a.shape) - for i in drop_axes: - value_selection[i] = np.newaxis - value_selection = tuple(value_selection) - value = value[value_selection] - a[selection] = value - - -# noinspection PyProtectedMember -class OrthogonalIndexer: - def __init__(self, selection, array): - # handle ellipsis - selection = replace_ellipsis(selection, array._shape) - - # normalize list to array - selection = replace_lists(selection) - - # setup per-dimension indexers - dim_indexers = [] - for dim_sel, dim_len, dim_chunk_len in zip(selection, array._shape, array._chunks): - if is_integer(dim_sel): - dim_indexer = IntDimIndexer(dim_sel, dim_len, dim_chunk_len) - - elif isinstance(dim_sel, slice): - dim_indexer = SliceDimIndexer(dim_sel, dim_len, dim_chunk_len) - - elif is_integer_array(dim_sel): - dim_indexer = IntArrayDimIndexer(dim_sel, dim_len, dim_chunk_len) - - elif is_bool_array(dim_sel): - dim_indexer = BoolArrayDimIndexer(dim_sel, dim_len, dim_chunk_len) - - else: - raise IndexError( - "unsupported selection item for orthogonal indexing; " - "expected integer, slice, integer array or Boolean " - "array, got {!r}".format(type(dim_sel)) - ) - - dim_indexers.append(dim_indexer) - - self.array = array - self.dim_indexers = dim_indexers - self.shape = tuple(s.nitems for s in self.dim_indexers if not isinstance(s, IntDimIndexer)) - self.is_advanced = not is_basic_selection(selection) - if self.is_advanced: - self.drop_axes = tuple( - i - for i, dim_indexer in enumerate(self.dim_indexers) - if isinstance(dim_indexer, IntDimIndexer) - ) - else: - self.drop_axes = () - - def __iter__(self): - for dim_projections in itertools.product(*self.dim_indexers): - chunk_coords = tuple(p.dim_chunk_ix for p in dim_projections) - chunk_selection = tuple(p.dim_chunk_sel for p in dim_projections) - out_selection = tuple( - p.dim_out_sel for p in dim_projections if p.dim_out_sel is not None - ) - - # handle advanced indexing arrays orthogonally - if self.is_advanced: - # N.B., numpy doesn't support orthogonal indexing directly as yet, - # so need to work around via np.ix_. Also np.ix_ does not support a - # mixture of arrays and slices or integers, so need to convert slices - # and integers into ranges. - chunk_selection = ix_(chunk_selection, self.array._chunks) - - # special case for non-monotonic indices - if not is_basic_selection(out_selection): - out_selection = ix_(out_selection, self.shape) - - yield ChunkProjection(chunk_coords, chunk_selection, out_selection) - - -class OIndex: - def __init__(self, array): - self.array = array - - def __getitem__(self, selection): - fields, selection = pop_fields(selection) - selection = ensure_tuple(selection) - selection = replace_lists(selection) - return self.array.get_orthogonal_selection(selection, fields=fields) - - def __setitem__(self, selection, value): - fields, selection = pop_fields(selection) - selection = ensure_tuple(selection) - selection = replace_lists(selection) - return self.array.set_orthogonal_selection(selection, value, fields=fields) - - -# noinspection PyProtectedMember -class BlockIndexer: - def __init__(self, selection, array): - # handle ellipsis - selection = replace_ellipsis(selection, array._shape) - - # normalize list to array - selection = replace_lists(selection) - - # setup per-dimension indexers - dim_indexers = [] - for dim_sel, dim_len, dim_chunk_size in zip(selection, array._shape, array._chunks): - dim_numchunks = int(np.ceil(dim_len / dim_chunk_size)) - - if is_integer(dim_sel): - if dim_sel < 0: - dim_sel = dim_numchunks + dim_sel - - start = dim_sel * dim_chunk_size - stop = start + dim_chunk_size - slice_ = slice(start, stop) - - elif is_slice(dim_sel): - start = dim_sel.start if dim_sel.start is not None else 0 - stop = dim_sel.stop if dim_sel.stop is not None else dim_numchunks - - if dim_sel.step not in {1, None}: - raise IndexError( - "unsupported selection item for block indexing; " - "expected integer or slice with step=1, got {!r}".format(type(dim_sel)) - ) - - # Can't reuse wraparound_indices because it expects a numpy array - # We have integers here. - if start < 0: - start = dim_numchunks + start - if stop < 0: - stop = dim_numchunks + stop - - start = start * dim_chunk_size - stop = stop * dim_chunk_size - slice_ = slice(start, stop) - - else: - raise IndexError( - "unsupported selection item for block indexing; " - "expected integer or slice, got {!r}".format(type(dim_sel)) - ) - - dim_indexer = SliceDimIndexer(slice_, dim_len, dim_chunk_size) - dim_indexers.append(dim_indexer) - - if start >= dim_len or start < 0: - raise BoundsCheckError(dim_len) - - self.dim_indexers = dim_indexers - self.shape = tuple(s.nitems for s in self.dim_indexers) - self.drop_axes = () - - def __iter__(self): - for dim_projections in itertools.product(*self.dim_indexers): - chunk_coords = tuple(p.dim_chunk_ix for p in dim_projections) - chunk_selection = tuple(p.dim_chunk_sel for p in dim_projections) - out_selection = tuple( - p.dim_out_sel for p in dim_projections if p.dim_out_sel is not None - ) - - yield ChunkProjection(chunk_coords, chunk_selection, out_selection) - - -class BlockIndex: - def __init__(self, array): - self.array = array - - def __getitem__(self, selection): - fields, selection = pop_fields(selection) - selection = ensure_tuple(selection) - selection = replace_lists(selection) - return self.array.get_block_selection(selection, fields=fields) - - def __setitem__(self, selection, value): - fields, selection = pop_fields(selection) - selection = ensure_tuple(selection) - selection = replace_lists(selection) - return self.array.set_block_selection(selection, value, fields=fields) - - -# noinspection PyProtectedMember -def is_coordinate_selection(selection, array): - return (len(selection) == len(array._shape)) and all( - is_integer(dim_sel) or is_integer_array(dim_sel) for dim_sel in selection - ) - - -# noinspection PyProtectedMember -def is_mask_selection(selection, array): - return ( - len(selection) == 1 and is_bool_array(selection[0]) and selection[0].shape == array._shape - ) - - -# noinspection PyProtectedMember -class CoordinateIndexer: - def __init__(self, selection, array): - # some initial normalization - selection = ensure_tuple(selection) - selection = tuple([i] if is_integer(i) else i for i in selection) - selection = replace_lists(selection) - - # validation - if not is_coordinate_selection(selection, array): - raise IndexError( - "invalid coordinate selection; expected one integer " - "(coordinate) array per dimension of the target array, " - "got {!r}".format(selection) - ) - - # handle wraparound, boundscheck - for dim_sel, dim_len in zip(selection, array.shape): - # handle wraparound - wraparound_indices(dim_sel, dim_len) - - # handle out of bounds - boundscheck_indices(dim_sel, dim_len) - - # compute chunk index for each point in the selection - chunks_multi_index = tuple( - dim_sel // dim_chunk_len for (dim_sel, dim_chunk_len) in zip(selection, array._chunks) - ) - - # broadcast selection - this will raise error if array dimensions don't match - selection = np.broadcast_arrays(*selection) - chunks_multi_index = np.broadcast_arrays(*chunks_multi_index) - - # remember shape of selection, because we will flatten indices for processing - self.sel_shape = selection[0].shape if selection[0].shape else (1,) - - # flatten selection - selection = [dim_sel.reshape(-1) for dim_sel in selection] - chunks_multi_index = [dim_chunks.reshape(-1) for dim_chunks in chunks_multi_index] - - # ravel chunk indices - chunks_raveled_indices = np.ravel_multi_index(chunks_multi_index, dims=array._cdata_shape) - - # group points by chunk - if np.any(np.diff(chunks_raveled_indices) < 0): - # optimisation, only sort if needed - sel_sort = np.argsort(chunks_raveled_indices) - selection = tuple(dim_sel[sel_sort] for dim_sel in selection) - else: - sel_sort = None - - # store attributes - self.selection = selection - self.sel_sort = sel_sort - self.shape = selection[0].shape if selection[0].shape else (1,) - self.drop_axes = () - self.array = array - - # precompute number of selected items for each chunk - self.chunk_nitems = np.bincount(chunks_raveled_indices, minlength=array.nchunks) - self.chunk_nitems_cumsum = np.cumsum(self.chunk_nitems) - # locate the chunks we need to process - self.chunk_rixs = np.nonzero(self.chunk_nitems)[0] - - # unravel chunk indices - self.chunk_mixs = np.unravel_index(self.chunk_rixs, array._cdata_shape) - - def __iter__(self): - # iterate over chunks - for i, chunk_rix in enumerate(self.chunk_rixs): - chunk_coords = tuple(m[i] for m in self.chunk_mixs) - if chunk_rix == 0: - start = 0 - else: - start = self.chunk_nitems_cumsum[chunk_rix - 1] - stop = self.chunk_nitems_cumsum[chunk_rix] - if self.sel_sort is None: - out_selection = slice(start, stop) - else: - out_selection = self.sel_sort[start:stop] - - chunk_offsets = tuple( - dim_chunk_ix * dim_chunk_len - for dim_chunk_ix, dim_chunk_len in zip(chunk_coords, self.array._chunks) - ) - chunk_selection = tuple( - dim_sel[start:stop] - dim_chunk_offset - for (dim_sel, dim_chunk_offset) in zip(self.selection, chunk_offsets) - ) - - yield ChunkProjection(chunk_coords, chunk_selection, out_selection) - - -# noinspection PyProtectedMember -class MaskIndexer(CoordinateIndexer): - def __init__(self, selection, array): - # some initial normalization - selection = ensure_tuple(selection) - selection = replace_lists(selection) - - # validation - if not is_mask_selection(selection, array): - raise IndexError( - "invalid mask selection; expected one Boolean (mask)" - "array with the same shape as the target array, got {!r}".format(selection) - ) - - # convert to indices - selection = np.nonzero(selection[0]) - - # delegate the rest to superclass - super().__init__(selection, array) - - -class VIndex: - def __init__(self, array): - self.array = array - - def __getitem__(self, selection): - fields, selection = pop_fields(selection) - selection = ensure_tuple(selection) - selection = replace_lists(selection) - if is_coordinate_selection(selection, self.array): - return self.array.get_coordinate_selection(selection, fields=fields) - elif is_mask_selection(selection, self.array): - return self.array.get_mask_selection(selection, fields=fields) - else: - raise VindexInvalidSelectionError(selection) - - def __setitem__(self, selection, value): - fields, selection = pop_fields(selection) - selection = ensure_tuple(selection) - selection = replace_lists(selection) - if is_coordinate_selection(selection, self.array): - self.array.set_coordinate_selection(selection, value, fields=fields) - elif is_mask_selection(selection, self.array): - self.array.set_mask_selection(selection, value, fields=fields) - else: - raise VindexInvalidSelectionError(selection) - - -def check_fields(fields, dtype): - # early out - if fields is None: - return dtype - # check type - if not isinstance(fields, (str, list, tuple)): - raise IndexError( - "'fields' argument must be a string or list of strings; found {!r}".format(type(fields)) - ) - if fields: - if dtype.names is None: - raise IndexError("invalid 'fields' argument, array does not have any fields") - try: - if isinstance(fields, str): - # single field selection - out_dtype = dtype[fields] - else: - # multiple field selection - out_dtype = np.dtype([(f, dtype[f]) for f in fields]) - except KeyError as e: - raise IndexError("invalid 'fields' argument, field not found: {!r}".format(e)) - else: - return out_dtype - else: - return dtype - - -def check_no_multi_fields(fields): - if isinstance(fields, list): - if len(fields) == 1: - return fields[0] - elif len(fields) > 1: - raise IndexError("multiple fields are not supported for this operation") - return fields - - -def pop_fields(selection): - if isinstance(selection, str): - # single field selection - fields = selection - selection = () - elif not isinstance(selection, tuple): - # single selection item, no fields - fields = None - # leave selection as-is - else: - # multiple items, split fields from selection items - fields = [f for f in selection if isinstance(f, str)] - fields = fields[0] if len(fields) == 1 else fields - selection = tuple(s for s in selection if not isinstance(s, str)) - selection = selection[0] if len(selection) == 1 else selection - return fields, selection - - -def make_slice_selection(selection): - ls = [] - for dim_selection in selection: - if is_integer(dim_selection): - ls.append(slice(int(dim_selection), int(dim_selection) + 1, 1)) - elif isinstance(dim_selection, np.ndarray): - if len(dim_selection) == 1: - ls.append(slice(int(dim_selection[0]), int(dim_selection[0]) + 1, 1)) - else: - raise ArrayIndexError - else: - ls.append(dim_selection) - return ls - - -class PartialChunkIterator: - """Iterator to retrieve the specific coordinates of requested data - from within a compressed chunk. - - Parameters - ---------- - selection : tuple - tuple of slice objects to take from the chunk - arr_shape : shape of chunk to select data from - - Attributes - ---------- - arr_shape - selection - - Returns - ------- - Tuple with 3 elements: - - start: int - elements offset in the chunk to read from - nitems: int - number of elements to read in the chunk from start - partial_out_selection: list of slices - indices of a temporary empty array of size `Array._chunks` to assign - the decompressed data to after the partial read. - - Notes - ----- - An array is flattened when compressed with blosc, so this iterator takes - the wanted selection of an array and determines the wanted coordinates - of the flattened, compressed data to be read and then decompressed. The - decompressed data is then placed in a temporary empty array of size - `Array._chunks` at the indices yielded as partial_out_selection. - Once all the slices yielded by this iterator have been read, decompressed - and written to the temporary array, the wanted slice of the chunk can be - indexed from the temporary array and written to the out_selection slice - of the out array. - - """ - - def __init__(self, selection, arr_shape): - selection = make_slice_selection(selection) - self.arr_shape = arr_shape - - # number of selection dimensions can't be greater than the number of chunk dimensions - if len(selection) > len(self.arr_shape): - raise ValueError( - "Selection has more dimensions then the array:\n" - f"selection dimensions = {len(selection)}\n" - f"array dimensions = {len(self.arr_shape)}" - ) - - # any selection can not be out of the range of the chunk - selection_shape = np.empty(self.arr_shape)[tuple(selection)].shape - if any( - selection_dim < 0 or selection_dim > arr_dim - for selection_dim, arr_dim in zip(selection_shape, self.arr_shape) - ): - raise IndexError( - "a selection index is out of range for the dimension" - ) # pragma: no cover - - for i, dim_size in enumerate(self.arr_shape[::-1]): - index = len(self.arr_shape) - (i + 1) - if index <= len(selection) - 1: - slice_size = selection_shape[index] - if slice_size == dim_size and index > 0: - selection.pop() - else: - break - - chunk_loc_slices = [] - last_dim_slice = None if selection[-1].step > 1 else selection.pop() - for arr_shape_i, sl in zip(arr_shape, selection): - dim_chunk_loc_slices = [] - assert isinstance(sl, slice) - for x in slice_to_range(sl, arr_shape_i): - dim_chunk_loc_slices.append(slice(x, x + 1, 1)) - chunk_loc_slices.append(dim_chunk_loc_slices) - if last_dim_slice: - chunk_loc_slices.append([last_dim_slice]) - self.chunk_loc_slices = list(itertools.product(*chunk_loc_slices)) - - def __iter__(self): - chunk1 = self.chunk_loc_slices[0] - nitems = (chunk1[-1].stop - chunk1[-1].start) * np.prod( - self.arr_shape[len(chunk1) :], dtype=int - ) - for partial_out_selection in self.chunk_loc_slices: - start = 0 - for i, sl in enumerate(partial_out_selection): - start += sl.start * np.prod(self.arr_shape[i + 1 :], dtype=int) - yield start, nitems, partial_out_selection diff --git a/src/zarr/v2/meta.py b/src/zarr/v2/meta.py deleted file mode 100644 index 2f7ce1242e..0000000000 --- a/src/zarr/v2/meta.py +++ /dev/null @@ -1,302 +0,0 @@ -import base64 -import itertools -from collections.abc import Mapping - -import numpy as np - -from zarr.v2.errors import MetadataError -from zarr.v2.util import json_dumps, json_loads - -from typing import cast, Union, Any, List, Mapping as MappingType, TYPE_CHECKING - -if TYPE_CHECKING: # pragma: no cover - pass - - -ZARR_FORMAT = 2 - -# FLOAT_FILLS = {"NaN": np.nan, "Infinity": np.PINF, "-Infinity": np.NINF} - -_v3_core_types = {"bool", "i1", "u1"} | set("".join(d) for d in itertools.product("<>", ("u", "i", "f"), ("2", "4", "8"))) - -# The set of complex types allowed ({"c8", ">c16"}) -_v3_complex_types = set(f"{end}c{_bytes}" for end, _bytes in itertools.product("<>", ("8", "16"))) - -# All dtype.str values corresponding to datetime64 and timedelta64 -# see: https://numpy.org/doc/stable/reference/arrays.datetime.html#datetime-units -_date_units = ["Y", "M", "W", "D"] -_time_units = ["h", "m", "s", "ms", "us", "μs", "ns", "ps", "fs", "as"] -_v3_datetime_types = set( - f"{end}{kind}8[{unit}]" - for end, unit, kind in itertools.product("<>", _date_units + _time_units, ("m", "M")) -) - - -def get_extended_dtype_info(dtype) -> dict: - if dtype.str in _v3_complex_types: - return dict( - extension="https://zarr-specs.readthedocs.io/en/core-protocol-v3.0-dev/protocol/extensions/complex-dtypes/v1.0.html", - type=dtype.str, - fallback=None, - ) - elif dtype.str == "|O": - return dict( - extension="TODO: object array protocol URL", - type=dtype.str, - fallback=None, - ) - elif dtype.str.startswith("|S"): - return dict( - extension="TODO: bytestring array protocol URL", - type=dtype.str, - fallback=None, - ) - elif dtype.str.startswith("U"): - return dict( - extension="TODO: unicode array protocol URL", - type=dtype.str, - fallback=None, - ) - elif dtype.str.startswith("|V"): - return dict( - extension="TODO: structured array protocol URL", - type=dtype.descr, - fallback=None, - ) - elif dtype.str in _v3_datetime_types: - return dict( - extension="https://zarr-specs.readthedocs.io/en/latest/extensions/data-types/datetime/v1.0.html", - type=dtype.str, - fallback=None, - ) - else: - raise ValueError(f"Unsupported dtype: {dtype}") - - -class Metadata2: - ZARR_FORMAT = ZARR_FORMAT - - @classmethod - def parse_metadata(cls, s: Union[MappingType, bytes, str]) -> MappingType[str, Any]: - # Here we allow that a store may return an already-parsed metadata object, - # or a string of JSON that we will parse here. We allow for an already-parsed - # object to accommodate a consolidated metadata store, where all the metadata for - # all groups and arrays will already have been parsed from JSON. - - if isinstance(s, Mapping): - # assume metadata has already been parsed into a mapping object - meta = s - - else: - # assume metadata needs to be parsed as JSON - meta = json_loads(s) - - return meta - - @classmethod - def decode_array_metadata(cls, s: Union[MappingType, bytes, str]) -> MappingType[str, Any]: - meta = cls.parse_metadata(s) - - # check metadata format - zarr_format = meta.get("zarr_format", None) - if zarr_format != cls.ZARR_FORMAT: - raise MetadataError("unsupported zarr format: %s" % zarr_format) - - # extract array metadata fields - try: - dtype = cls.decode_dtype(meta["dtype"]) - if dtype.hasobject: - import numcodecs - - object_codec = numcodecs.get_codec(meta["filters"][0]) - else: - object_codec = None - - dimension_separator = meta.get("dimension_separator", None) - fill_value = cls.decode_fill_value(meta["fill_value"], dtype, object_codec) - meta = dict( - zarr_format=meta["zarr_format"], - shape=tuple(meta["shape"]), - chunks=tuple(meta["chunks"]), - dtype=dtype, - compressor=meta["compressor"], - fill_value=fill_value, - order=meta["order"], - filters=meta["filters"], - ) - if dimension_separator: - meta["dimension_separator"] = dimension_separator - except Exception as e: - raise MetadataError("error decoding metadata") from e - else: - return meta - - @classmethod - def encode_array_metadata(cls, meta: MappingType[str, Any]) -> bytes: - dtype = meta["dtype"] - sdshape = () - if dtype.subdtype is not None: - dtype, sdshape = dtype.subdtype - - dimension_separator = meta.get("dimension_separator") - if dtype.hasobject: - import numcodecs - - object_codec = numcodecs.get_codec(meta["filters"][0]) - else: - object_codec = None - - meta = dict( - zarr_format=cls.ZARR_FORMAT, - shape=meta["shape"] + sdshape, - chunks=meta["chunks"], - dtype=cls.encode_dtype(dtype), - compressor=meta["compressor"], - fill_value=cls.encode_fill_value(meta["fill_value"], dtype, object_codec), - order=meta["order"], - filters=meta["filters"], - ) - if dimension_separator: - meta["dimension_separator"] = dimension_separator - - return json_dumps(meta) - - @classmethod - def encode_dtype(cls, d: np.dtype): - if d.fields is None: - return d.str - else: - return d.descr - - @classmethod - def _decode_dtype_descr(cls, d) -> List[Any]: - # need to convert list of lists to list of tuples - if isinstance(d, list): - # recurse to handle nested structures - d = [(k[0], cls._decode_dtype_descr(k[1])) + tuple(k[2:]) for k in d] - return d - - @classmethod - def decode_dtype(cls, d) -> np.dtype: - d = cls._decode_dtype_descr(d) - return np.dtype(d) - - @classmethod - def decode_group_metadata(cls, s: Union[MappingType, bytes, str]) -> MappingType[str, Any]: - meta = cls.parse_metadata(s) - - # check metadata format version - zarr_format = meta.get("zarr_format", None) - if zarr_format != cls.ZARR_FORMAT: - raise MetadataError("unsupported zarr format: %s" % zarr_format) - - meta = dict(zarr_format=zarr_format) - return meta - - # N.B., keep `meta` parameter as a placeholder for future - # noinspection PyUnusedLocal - @classmethod - def encode_group_metadata(cls, meta=None) -> bytes: - meta = dict(zarr_format=cls.ZARR_FORMAT) - return json_dumps(meta) - - @classmethod - def decode_fill_value(cls, v: Any, dtype: np.dtype, object_codec: Any = None) -> Any: - # early out - if v is None: - return v - if dtype.kind == "V" and dtype.hasobject: - if object_codec is None: - raise ValueError("missing object_codec for object array") - v = base64.standard_b64decode(v) - v = object_codec.decode(v) - v = np.array(v, dtype=dtype)[()] - return v - if dtype.kind == "f": - if v == "NaN": - return np.nan - elif v == "Infinity": - return np.inf - elif v == "-Infinity": - return -np.inf - else: - return np.array(v, dtype=dtype)[()] - elif dtype.kind == "c": - v = ( - cls.decode_fill_value(v[0], dtype.type().real.dtype), - cls.decode_fill_value(v[1], dtype.type().imag.dtype), - ) - v = v[0] + 1j * v[1] - return np.array(v, dtype=dtype)[()] - elif dtype.kind == "S": - # noinspection PyBroadException - try: - v = base64.standard_b64decode(v) - except Exception: - # be lenient, allow for other values that may have been used before base64 - # encoding and may work as fill values, e.g., the number 0 - pass - v = np.array(v, dtype=dtype)[()] - return v - elif dtype.kind == "V": - v = base64.standard_b64decode(v) - v = np.array(v, dtype=dtype.str).view(dtype)[()] - return v - elif dtype.kind == "U": - # leave as-is - return v - else: - return np.array(v, dtype=dtype)[()] - - @classmethod - def encode_fill_value(cls, v: Any, dtype: np.dtype, object_codec: Any = None) -> Any: - # early out - if v is None: - return v - if dtype.kind == "V" and dtype.hasobject: - if object_codec is None: - raise ValueError("missing object_codec for object array") - v = object_codec.encode(v) - v = str(base64.standard_b64encode(v), "ascii") - return v - if dtype.kind == "f": - if np.isnan(v): - return "NaN" - elif np.isposinf(v): - return "Infinity" - elif np.isneginf(v): - return "-Infinity" - else: - return float(v) - elif dtype.kind in ("u", "i"): - return int(v) - elif dtype.kind == "b": - return bool(v) - elif dtype.kind == "c": - c = cast(np.complex128, np.dtype(complex).type()) - v = ( - cls.encode_fill_value(v.real, c.real.dtype, object_codec), - cls.encode_fill_value(v.imag, c.imag.dtype, object_codec), - ) - return v - elif dtype.kind in ("S", "V"): - v = str(base64.standard_b64encode(v), "ascii") - return v - elif dtype.kind == "U": - return v - elif dtype.kind in ("m", "M"): - return int(v.view("i8")) - else: - return v - - -parse_metadata = Metadata2.parse_metadata -decode_array_metadata = Metadata2.decode_array_metadata -encode_array_metadata = Metadata2.encode_array_metadata -encode_dtype = Metadata2.encode_dtype -_decode_dtype_descr = Metadata2._decode_dtype_descr -decode_dtype = Metadata2.decode_dtype -decode_group_metadata = Metadata2.decode_group_metadata -encode_group_metadata = Metadata2.encode_group_metadata -decode_fill_value = Metadata2.decode_fill_value -encode_fill_value = Metadata2.encode_fill_value diff --git a/src/zarr/v2/meta_v1.py b/src/zarr/v2/meta_v1.py deleted file mode 100644 index 881b9191eb..0000000000 --- a/src/zarr/v2/meta_v1.py +++ /dev/null @@ -1,64 +0,0 @@ -import json - -import numpy as np - -from zarr.v2.errors import MetadataError - - -def decode_metadata(b): - s = str(b, "ascii") - meta = json.loads(s) - zarr_format = meta.get("zarr_format", None) - if zarr_format != 1: - raise MetadataError("unsupported zarr format: %s" % zarr_format) - try: - meta = dict( - zarr_format=meta["zarr_format"], - shape=tuple(meta["shape"]), - chunks=tuple(meta["chunks"]), - dtype=decode_dtype(meta["dtype"]), - compression=meta["compression"], - compression_opts=meta["compression_opts"], - fill_value=meta["fill_value"], - order=meta["order"], - ) - except Exception as e: - raise MetadataError("error decoding metadata: %s" % e) - else: - return meta - - -def encode_metadata(meta): - meta = dict( - zarr_format=1, - shape=meta["shape"], - chunks=meta["chunks"], - dtype=encode_dtype(meta["dtype"]), - compression=meta["compression"], - compression_opts=meta["compression_opts"], - fill_value=meta["fill_value"], - order=meta["order"], - ) - s = json.dumps(meta, indent=4, sort_keys=True, ensure_ascii=True) - b = s.encode("ascii") - return b - - -def encode_dtype(d): - if d.fields is None: - return d.str - else: - return d.descr - - -def _decode_dtype_descr(d): - # need to convert list of lists to list of tuples - if isinstance(d, list): - # recurse to handle nested structures - d = [(f, _decode_dtype_descr(v)) for f, v in d] - return d - - -def decode_dtype(d): - d = _decode_dtype_descr(d) - return np.dtype(d) diff --git a/src/zarr/v2/n5.py b/src/zarr/v2/n5.py deleted file mode 100644 index ece110f49d..0000000000 --- a/src/zarr/v2/n5.py +++ /dev/null @@ -1,897 +0,0 @@ -"""This module contains a storage class and codec to support the N5 format.""" - -import os -import struct -import sys -from typing import Any, Dict, Optional, cast -import warnings - -import numpy as np -from numcodecs.abc import Codec -from numcodecs.compat import ndarray_copy -from numcodecs.registry import get_codec, register_codec - -from zarr.v2.meta import ZARR_FORMAT, json_dumps, json_loads -from zarr.v2.storage import FSStore -from zarr.v2.storage import NestedDirectoryStore, _prog_ckey, _prog_number, normalize_storage_path -from zarr.v2.storage import array_meta_key as zarr_array_meta_key -from zarr.v2.storage import attrs_key as zarr_attrs_key -from zarr.v2.storage import group_meta_key as zarr_group_meta_key - -N5_FORMAT = "2.0.0" - -zarr_to_n5_keys = [ - ("chunks", "blockSize"), - ("dtype", "dataType"), - ("compressor", "compression"), - ("shape", "dimensions"), -] -n5_attrs_key = "attributes.json" -n5_keywords = ["n5", "dataType", "dimensions", "blockSize", "compression"] - - -class N5Store(NestedDirectoryStore): - """Storage class using directories and files on a standard file system, - following the N5 format (https://github.com/saalfeldlab/n5). - - Parameters - ---------- - path : string - Location of directory to use as the root of the storage hierarchy. - normalize_keys : bool, optional - If True, all store keys will be normalized to use lower case characters - (e.g. 'foo' and 'FOO' will be treated as equivalent). This can be - useful to avoid potential discrepancies between case-sensitive and - case-insensitive file system. Default value is False. - - Examples - -------- - Store a single array:: - - >>> import zarr - >>> store = zarr.N5Store('data/array.n5') - >>> z = zarr.zeros((10, 10), chunks=(5, 5), store=store, overwrite=True) - >>> z[...] = 42 - - Store a group:: - - >>> store = zarr.N5Store('data/group.n5') - >>> root = zarr.group(store=store, overwrite=True) - >>> foo = root.create_group('foo') - >>> bar = foo.zeros('bar', shape=(10, 10), chunks=(5, 5)) - >>> bar[...] = 42 - - Notes - ----- - - This is an experimental feature. - - Safe to write in multiple threads or processes. - - """ - - def __getitem__(self, key: str) -> bytes: - if key.endswith(zarr_group_meta_key): - key_new = key.replace(zarr_group_meta_key, n5_attrs_key) - value = group_metadata_to_zarr(self._load_n5_attrs(key_new)) - - return json_dumps(value) - - elif key.endswith(zarr_array_meta_key): - key_new = key.replace(zarr_array_meta_key, n5_attrs_key) - top_level = key == zarr_array_meta_key - value = array_metadata_to_zarr(self._load_n5_attrs(key_new), top_level=top_level) - return json_dumps(value) - - elif key.endswith(zarr_attrs_key): - key_new = key.replace(zarr_attrs_key, n5_attrs_key) - value = attrs_to_zarr(self._load_n5_attrs(key_new)) - - if len(value) == 0: - raise KeyError(key_new) - else: - return json_dumps(value) - - elif is_chunk_key(key): - key_new = invert_chunk_coords(key) - - else: - key_new = key - - return super().__getitem__(key_new) - - def __setitem__(self, key: str, value: Any): - if key.endswith(zarr_group_meta_key): - key_new = key.replace(zarr_group_meta_key, n5_attrs_key) - - n5_attrs = self._load_n5_attrs(key_new) - n5_attrs.update(**group_metadata_to_n5(json_loads(value))) - - value = json_dumps(n5_attrs) - - elif key.endswith(zarr_array_meta_key): - key_new = key.replace(zarr_array_meta_key, n5_attrs_key) - top_level = key == zarr_array_meta_key - n5_attrs = self._load_n5_attrs(key_new) - n5_attrs.update(**array_metadata_to_n5(json_loads(value), top_level=top_level)) - value = json_dumps(n5_attrs) - - elif key.endswith(zarr_attrs_key): - key_new = key.replace(zarr_attrs_key, n5_attrs_key) - - n5_attrs = self._load_n5_attrs(key_new) - zarr_attrs = json_loads(value) - - for k in n5_keywords: - if k in zarr_attrs: - warnings.warn(f"Attribute {k} is a reserved N5 keyword", UserWarning) - - # remove previous user attributes - for k in list(n5_attrs.keys()): - if k not in n5_keywords: - del n5_attrs[k] - - # add new user attributes - n5_attrs.update(**zarr_attrs) - - value = json_dumps(n5_attrs) - - elif is_chunk_key(key): - key_new = invert_chunk_coords(key) - - else: - key_new = key - - super().__setitem__(key_new, value) - - def __delitem__(self, key: str): - if key.endswith(zarr_group_meta_key): - key_new = key.replace(zarr_group_meta_key, n5_attrs_key) - elif key.endswith(zarr_array_meta_key): - key_new = key.replace(zarr_array_meta_key, n5_attrs_key) - elif key.endswith(zarr_attrs_key): - key_new = key.replace(zarr_attrs_key, n5_attrs_key) - elif is_chunk_key(key): - key_new = invert_chunk_coords(key) - else: - key_new = key - - super().__delitem__(key_new) - - def __contains__(self, key): - if key.endswith(zarr_group_meta_key): - key_new = key.replace(zarr_group_meta_key, n5_attrs_key) - if key_new not in self: - return False - # group if not a dataset (attributes do not contain 'dimensions') - return "dimensions" not in self._load_n5_attrs(key_new) - - elif key.endswith(zarr_array_meta_key): - key_new = key.replace(zarr_array_meta_key, n5_attrs_key) - # array if attributes contain 'dimensions' - return "dimensions" in self._load_n5_attrs(key_new) - - elif key.endswith(zarr_attrs_key): - key_new = key.replace(zarr_attrs_key, n5_attrs_key) - return self._contains_attrs(key_new) - - elif is_chunk_key(key): - key_new = invert_chunk_coords(key) - else: - key_new = key - - return super().__contains__(key_new) - - def __eq__(self, other): - return isinstance(other, N5Store) and self.path == other.path - - def listdir(self, path: Optional[str] = None): - if path is not None: - path = invert_chunk_coords(path) - path = cast(str, path) - # We can't use NestedDirectoryStore's listdir, as it requires - # array_meta_key to be present in array directories, which this store - # doesn't provide. - children = super().listdir(path=path) - - if self._is_array(path): - # replace n5 attribute file with respective zarr attribute files - children.remove(n5_attrs_key) - children.append(zarr_array_meta_key) - if self._contains_attrs(path): - children.append(zarr_attrs_key) - - # special handling of directories containing an array to map - # inverted nested chunk keys back to standard chunk keys - new_children = [] - root_path = self.dir_path(path) - for entry in children: - entry_path = os.path.join(root_path, entry) - if _prog_number.match(entry) and os.path.isdir(entry_path): - for dir_path, _, file_names in os.walk(entry_path): - for file_name in file_names: - file_path = os.path.join(dir_path, file_name) - rel_path = file_path.split(root_path + os.path.sep)[1] - new_child = rel_path.replace(os.path.sep, ".") - new_children.append(invert_chunk_coords(new_child)) - else: - new_children.append(entry) - - return sorted(new_children) - - elif self._is_group(path): - # replace n5 attribute file with respective zarr attribute files - children.remove(n5_attrs_key) - children.append(zarr_group_meta_key) - if self._contains_attrs(path): - children.append(zarr_attrs_key) - - return sorted(children) - - else: - return children - - def _load_n5_attrs(self, path: str) -> Dict[str, Any]: - try: - s = super().__getitem__(path) - return json_loads(s) - except KeyError: - return {} - - def _is_group(self, path: str): - if path is None: - attrs_key = n5_attrs_key - else: - attrs_key = os.path.join(path, n5_attrs_key) - - n5_attrs = self._load_n5_attrs(attrs_key) - return len(n5_attrs) > 0 and "dimensions" not in n5_attrs - - def _is_array(self, path: str): - if path is None: - attrs_key = n5_attrs_key - else: - attrs_key = os.path.join(path, n5_attrs_key) - - return "dimensions" in self._load_n5_attrs(attrs_key) - - def _contains_attrs(self, path: str): - if path is None: - attrs_key = n5_attrs_key - else: - if not path.endswith(n5_attrs_key): - attrs_key = os.path.join(path, n5_attrs_key) - else: - attrs_key = path - - attrs = attrs_to_zarr(self._load_n5_attrs(attrs_key)) - return len(attrs) > 0 - - -class N5FSStore(FSStore): - """Implementation of the N5 format (https://github.com/saalfeldlab/n5) - using `fsspec`, which allows storage on a variety of filesystems. Based - on `zarr.N5Store`. - - Parameters - ---------- - path : string - Location of directory to use as the root of the storage hierarchy. - normalize_keys : bool, optional - If True, all store keys will be normalized to use lower case characters - (e.g. 'foo' and 'FOO' will be treated as equivalent). This can be - useful to avoid potential discrepancies between case-sensitive and - case-insensitive file system. Default value is False. - - Examples - -------- - Store a single array:: - - >>> import zarr - >>> store = zarr.N5FSStore('data/array.n5', auto_mkdir=True) - >>> z = zarr.zeros((10, 10), chunks=(5, 5), store=store, overwrite=True) - >>> z[...] = 42 - - Store a group:: - - >>> store = zarr.N5FSStore('data/group.n5', auto_mkdir=True) - >>> root = zarr.group(store=store, overwrite=True) - >>> foo = root.create_group('foo') - >>> bar = foo.zeros('bar', shape=(10, 10), chunks=(5, 5)) - >>> bar[...] = 42 - - Notes - ----- - This is an experimental feature. - Safe to write in multiple threads or processes. - - Be advised that the `_dimension_separator` property of this store - (and arrays it creates) is ".", but chunks saved by this store will - in fact be "/" separated, as proscribed by the N5 format. - - This is counter-intuitive (to say the least), but not arbitrary. - Chunks in N5 format are stored with reversed dimension order - relative to Zarr chunks: a chunk of a 3D Zarr array would be stored - on a file system as `/0/1/2`, but in N5 the same chunk would be - stored as `/2/1/0`. Therefore, stores targeting N5 must intercept - chunk keys and flip the order of the dimensions before writing to - storage, and this procedure requires chunk keys with "." separated - dimensions, hence the Zarr arrays targeting N5 have the deceptive - "." dimension separator. - """ - - _array_meta_key = "attributes.json" - _group_meta_key = "attributes.json" - _attrs_key = "attributes.json" - - def __init__(self, *args, **kwargs): - if "dimension_separator" in kwargs: - warnings.warn("Keyword argument `dimension_separator` will be ignored") - kwargs["dimension_separator"] = "." - super().__init__(*args, **kwargs) - - @staticmethod - def _swap_separator(key: str): - segments = list(key.split("/")) - if segments: - last_segment = segments[-1] - if _prog_ckey.match(last_segment): - coords = list(last_segment.split(".")) - last_segment = "/".join(coords[::-1]) - segments = segments[:-1] + [last_segment] - key = "/".join(segments) - return key - - def _normalize_key(self, key: str): - if is_chunk_key(key): - key = invert_chunk_coords(key) - - key = normalize_storage_path(key).lstrip("/") - if key: - *bits, end = key.split("/") - - if end not in (self._array_meta_key, self._group_meta_key, self._attrs_key): - end = end.replace(".", "/") - key = "/".join(bits + [end]) - return key.lower() if self.normalize_keys else key - - def __getitem__(self, key: str) -> bytes: - if key.endswith(zarr_group_meta_key): - key_new = key.replace(zarr_group_meta_key, self._group_meta_key) - value = group_metadata_to_zarr(self._load_n5_attrs(key_new)) - - return json_dumps(value) - - elif key.endswith(zarr_array_meta_key): - key_new = key.replace(zarr_array_meta_key, self._array_meta_key) - top_level = key == zarr_array_meta_key - value = array_metadata_to_zarr(self._load_n5_attrs(key_new), top_level=top_level) - return json_dumps(value) - - elif key.endswith(zarr_attrs_key): - key_new = key.replace(zarr_attrs_key, self._attrs_key) - value = attrs_to_zarr(self._load_n5_attrs(key_new)) - - if len(value) == 0: - raise KeyError(key_new) - else: - return json_dumps(value) - - elif is_chunk_key(key): - key_new = self._swap_separator(key) - - else: - key_new = key - - return super().__getitem__(key_new) - - def __setitem__(self, key: str, value: Any): - if key.endswith(zarr_group_meta_key): - key_new = key.replace(zarr_group_meta_key, self._group_meta_key) - - n5_attrs = self._load_n5_attrs(key_new) - n5_attrs.update(**group_metadata_to_n5(json_loads(value))) - - value = json_dumps(n5_attrs) - - elif key.endswith(zarr_array_meta_key): - key_new = key.replace(zarr_array_meta_key, self._array_meta_key) - top_level = key == zarr_array_meta_key - n5_attrs = self._load_n5_attrs(key_new) - n5_attrs.update(**array_metadata_to_n5(json_loads(value), top_level=top_level)) - - value = json_dumps(n5_attrs) - - elif key.endswith(zarr_attrs_key): - key_new = key.replace(zarr_attrs_key, self._attrs_key) - - n5_attrs = self._load_n5_attrs(key_new) - zarr_attrs = json_loads(value) - - for k in n5_keywords: - if k in zarr_attrs.keys(): - warnings.warn(f"Attribute {k} is a reserved N5 keyword", UserWarning) - - # replace previous user attributes - for k in list(n5_attrs.keys()): - if k not in n5_keywords: - del n5_attrs[k] - - # add new user attributes - n5_attrs.update(**zarr_attrs) - - value = json_dumps(n5_attrs) - - elif is_chunk_key(key): - key_new = self._swap_separator(key) - - else: - key_new = key - - super().__setitem__(key_new, value) - - def __delitem__(self, key: str): - if key.endswith(zarr_group_meta_key): - key_new = key.replace(zarr_group_meta_key, self._group_meta_key) - elif key.endswith(zarr_array_meta_key): - key_new = key.replace(zarr_array_meta_key, self._array_meta_key) - elif key.endswith(zarr_attrs_key): - key_new = key.replace(zarr_attrs_key, self._attrs_key) - elif is_chunk_key(key): - key_new = self._swap_separator(key) - else: - key_new = key - super().__delitem__(key_new) - - def __contains__(self, key: Any): - if key.endswith(zarr_group_meta_key): - key_new = key.replace(zarr_group_meta_key, self._group_meta_key) - if key_new not in self: - return False - # group if not a dataset (attributes do not contain 'dimensions') - return "dimensions" not in self._load_n5_attrs(key_new) - - elif key.endswith(zarr_array_meta_key): - key_new = key.replace(zarr_array_meta_key, self._array_meta_key) - # array if attributes contain 'dimensions' - return "dimensions" in self._load_n5_attrs(key_new) - - elif key.endswith(zarr_attrs_key): - key_new = key.replace(zarr_attrs_key, self._attrs_key) - return self._contains_attrs(key_new) - - elif is_chunk_key(key): - key_new = self._swap_separator(key) - - else: - key_new = key - return super().__contains__(key_new) - - def __eq__(self, other: Any): - return isinstance(other, N5FSStore) and self.path == other.path - - def listdir(self, path: Optional[str] = None): - if path is not None: - path = invert_chunk_coords(path) - - # We can't use NestedDirectoryStore's listdir, as it requires - # array_meta_key to be present in array directories, which this store - # doesn't provide. - children = super().listdir(path=path) - if self._is_array(path): - # replace n5 attribute file with respective zarr attribute files - children.remove(self._array_meta_key) - children.append(zarr_array_meta_key) - if self._contains_attrs(path): - children.append(zarr_attrs_key) - - # special handling of directories containing an array to map - # inverted nested chunk keys back to standard chunk keys - new_children = [] - root_path = self.dir_path(path) - for entry in children: - entry_path = os.path.join(root_path, entry) - if _prog_number.match(entry) and self.fs.isdir(entry_path): - for file_name in self.fs.find(entry_path): - file_path = os.path.join(root_path, file_name) - rel_path = file_path.split(root_path)[1] - new_child = rel_path.lstrip("/").replace("/", ".") - new_children.append(invert_chunk_coords(new_child)) - else: - new_children.append(entry) - return sorted(new_children) - - elif self._is_group(path): - # replace n5 attribute file with respective zarr attribute files - children.remove(self._group_meta_key) - children.append(zarr_group_meta_key) - if self._contains_attrs(path): - children.append(zarr_attrs_key) - return sorted(children) - else: - return children - - def _load_n5_attrs(self, path: str): - try: - s = super().__getitem__(path) - return json_loads(s) - except KeyError: - return {} - - def _is_group(self, path: Optional[str]): - if path is None: - attrs_key = self._attrs_key - else: - attrs_key = os.path.join(path, self._attrs_key) - - n5_attrs = self._load_n5_attrs(attrs_key) - return len(n5_attrs) > 0 and "dimensions" not in n5_attrs - - def _is_array(self, path: Optional[str]): - if path is None: - attrs_key = self._attrs_key - else: - attrs_key = os.path.join(path, self._attrs_key) - - return "dimensions" in self._load_n5_attrs(attrs_key) - - def _contains_attrs(self, path: Optional[str]): - if path is None: - attrs_key = self._attrs_key - else: - if not path.endswith(self._attrs_key): - attrs_key = os.path.join(path, self._attrs_key) - else: - attrs_key = path - - attrs = attrs_to_zarr(self._load_n5_attrs(attrs_key)) - return len(attrs) > 0 - - -def is_chunk_key(key: str): - rv = False - segments = list(key.split("/")) - if segments: - last_segment = segments[-1] - rv = bool(_prog_ckey.match(last_segment)) - return rv - - -def invert_chunk_coords(key: str): - segments = list(key.split("/")) - if segments: - last_segment = segments[-1] - if _prog_ckey.match(last_segment): - coords = list(last_segment.split(".")) - last_segment = "/".join(coords[::-1]) - segments = segments[:-1] + [last_segment] - key = "/".join(segments) - return key - - -def group_metadata_to_n5(group_metadata: Dict[str, Any]) -> Dict[str, Any]: - """Convert group metadata from zarr to N5 format.""" - del group_metadata["zarr_format"] - # TODO: This should only exist at the top-level - group_metadata["n5"] = N5_FORMAT - return group_metadata - - -def group_metadata_to_zarr(group_metadata: Dict[str, Any]) -> Dict[str, Any]: - """Convert group metadata from N5 to zarr format.""" - # This only exists at the top level - group_metadata.pop("n5", None) - group_metadata["zarr_format"] = ZARR_FORMAT - return group_metadata - - -def array_metadata_to_n5(array_metadata: Dict[str, Any], top_level=False) -> Dict[str, Any]: - """Convert array metadata from zarr to N5 format. If the `top_level` keyword argument is True, - then the `N5` : N5_FORMAT key : value pair will be inserted into the metadata.""" - - for f, t in zarr_to_n5_keys: - array_metadata[t] = array_metadata.pop(f) - del array_metadata["zarr_format"] - if top_level: - array_metadata["n5"] = N5_FORMAT - try: - dtype = np.dtype(array_metadata["dataType"]) - except TypeError: - raise TypeError(f"Data type {array_metadata['dataType']} is not supported by N5") - - array_metadata["dataType"] = dtype.name - array_metadata["dimensions"] = array_metadata["dimensions"][::-1] - array_metadata["blockSize"] = array_metadata["blockSize"][::-1] - - if "fill_value" in array_metadata: - if array_metadata["fill_value"] != 0 and array_metadata["fill_value"] is not None: - raise ValueError( - f"""Received fill_value = {array_metadata['fill_value']}, - but N5 only supports fill_value = 0""" - ) - del array_metadata["fill_value"] - - if "order" in array_metadata: - if array_metadata["order"] != "C": - raise ValueError( - f"Received order = {array_metadata['order']}, but N5 only supports order = C" - ) - del array_metadata["order"] - - if "filters" in array_metadata: - if array_metadata["filters"] != [] and array_metadata["filters"] is not None: - raise ValueError("Received filters, but N5 storage does not support zarr filters") - del array_metadata["filters"] - - assert "compression" in array_metadata - compressor_config = array_metadata["compression"] - compressor_config = compressor_config_to_n5(compressor_config) - array_metadata["compression"] = compressor_config - - if "dimension_separator" in array_metadata: - del array_metadata["dimension_separator"] - - return array_metadata - - -def array_metadata_to_zarr( - array_metadata: Dict[str, Any], top_level: bool = False -) -> Dict[str, Any]: - """Convert array metadata from N5 to zarr format. - If the `top_level` keyword argument is True, then the `N5` key will be removed from metadata""" - for t, f in zarr_to_n5_keys: - array_metadata[t] = array_metadata.pop(f) - if top_level: - array_metadata.pop("n5") - array_metadata["zarr_format"] = ZARR_FORMAT - - array_metadata["shape"] = array_metadata["shape"][::-1] - array_metadata["chunks"] = array_metadata["chunks"][::-1] - array_metadata["fill_value"] = 0 # also if None was requested - array_metadata["order"] = "C" - array_metadata["filters"] = [] - array_metadata["dimension_separator"] = "." - array_metadata["dtype"] = np.dtype(array_metadata["dtype"]).str - - compressor_config = array_metadata["compressor"] - compressor_config = compressor_config_to_zarr(compressor_config) - array_metadata["compressor"] = { - "id": N5ChunkWrapper.codec_id, - "compressor_config": compressor_config, - "dtype": array_metadata["dtype"], - "chunk_shape": array_metadata["chunks"], - } - - return array_metadata - - -def attrs_to_zarr(attrs: Dict[str, Any]) -> Dict[str, Any]: - """Get all zarr attributes from an N5 attributes dictionary (i.e., - all non-keyword attributes).""" - - # remove all N5 keywords - for n5_key in n5_keywords: - if n5_key in attrs: - del attrs[n5_key] - - return attrs - - -def compressor_config_to_n5(compressor_config: Optional[Dict[str, Any]]) -> Dict[str, Any]: - if compressor_config is None: - return {"type": "raw"} - else: - _compressor_config = compressor_config - - # peel wrapper, if present - if _compressor_config["id"] == N5ChunkWrapper.codec_id: - _compressor_config = _compressor_config["compressor_config"] - - codec_id = _compressor_config["id"] - n5_config = {"type": codec_id} - - if codec_id == "bz2": - n5_config["type"] = "bzip2" - n5_config["blockSize"] = _compressor_config["level"] - - elif codec_id == "blosc": - n5_config["cname"] = _compressor_config["cname"] - n5_config["clevel"] = _compressor_config["clevel"] - n5_config["shuffle"] = _compressor_config["shuffle"] - n5_config["blocksize"] = _compressor_config["blocksize"] - - elif codec_id == "lzma": - # Switch to XZ for N5 if we are using the default XZ format. - # Note: 4 is the default, which is lzma.CHECK_CRC64. - if _compressor_config["format"] == 1 and _compressor_config["check"] in [-1, 4]: - n5_config["type"] = "xz" - else: - warnings.warn( - "Not all N5 implementations support lzma compression (yet). You " - "might not be able to open the dataset with another N5 library.", - RuntimeWarning, - ) - n5_config["format"] = _compressor_config["format"] - n5_config["check"] = _compressor_config["check"] - n5_config["filters"] = _compressor_config["filters"] - - # The default is lzma.PRESET_DEFAULT, which is 6. - if _compressor_config["preset"]: - n5_config["preset"] = _compressor_config["preset"] - else: - n5_config["preset"] = 6 - - elif codec_id == "zlib": - n5_config["type"] = "gzip" - n5_config["level"] = _compressor_config["level"] - n5_config["useZlib"] = True - - elif codec_id == "gzip": - n5_config["type"] = "gzip" - n5_config["level"] = _compressor_config["level"] - n5_config["useZlib"] = False - - else: - n5_config.update({k: v for k, v in _compressor_config.items() if k != "type"}) - - return n5_config - - -def compressor_config_to_zarr(compressor_config: Dict[str, Any]) -> Optional[Dict[str, Any]]: - codec_id = compressor_config["type"] - zarr_config = {"id": codec_id} - - if codec_id == "bzip2": - zarr_config["id"] = "bz2" - zarr_config["level"] = compressor_config["blockSize"] - - elif codec_id == "blosc": - zarr_config["cname"] = compressor_config["cname"] - zarr_config["clevel"] = compressor_config["clevel"] - zarr_config["shuffle"] = compressor_config["shuffle"] - zarr_config["blocksize"] = compressor_config["blocksize"] - - elif codec_id == "lzma": - zarr_config["format"] = compressor_config["format"] - zarr_config["check"] = compressor_config["check"] - zarr_config["preset"] = compressor_config["preset"] - zarr_config["filters"] = compressor_config["filters"] - - elif codec_id == "xz": - zarr_config["id"] = "lzma" - zarr_config["format"] = 1 # lzma.FORMAT_XZ - zarr_config["check"] = -1 - zarr_config["preset"] = compressor_config["preset"] - zarr_config["filters"] = None - - elif codec_id == "gzip": - if compressor_config.get("useZlib"): - zarr_config["id"] = "zlib" - zarr_config["level"] = compressor_config["level"] - else: - zarr_config["id"] = "gzip" - zarr_config["level"] = compressor_config["level"] - - elif codec_id == "raw": - return None - - else: - zarr_config.update({k: v for k, v in compressor_config.items() if k != "type"}) - - return zarr_config - - -class N5ChunkWrapper(Codec): # type: ignore[misc] - codec_id = "n5_wrapper" - - def __init__(self, dtype, chunk_shape, compressor_config=None, compressor=None): - self.dtype = np.dtype(dtype) - self.chunk_shape = tuple(chunk_shape) - # is the dtype a little endian format? - self._little_endian = self.dtype.byteorder == "<" or ( - self.dtype.byteorder == "=" and sys.byteorder == "little" - ) - - if compressor: - if compressor_config is not None: - raise ValueError("Only one of compressor_config or compressor should be given.") - compressor_config = compressor.get_config() - - if compressor_config is None and compressor is None or compressor_config["id"] == "raw": - self.compressor_config = None - self._compressor = None - else: - self._compressor = get_codec(compressor_config) - self.compressor_config = self._compressor.get_config() - - def get_config(self): - config = {"id": self.codec_id, "compressor_config": self.compressor_config} - return config - - def encode(self, chunk): - assert chunk.flags.c_contiguous - - header = self._create_header(chunk) - chunk = self._to_big_endian(chunk) - - if self._compressor: - return header + self._compressor.encode(chunk) - else: - return header + chunk.tobytes(order="A") - - def decode(self, chunk, out=None) -> bytes: - len_header, chunk_shape = self._read_header(chunk) - chunk = chunk[len_header:] - - if out is not None: - # out should only be used if we read a complete chunk - assert chunk_shape == self.chunk_shape, "Expected chunk of shape {}, found {}".format( - self.chunk_shape, chunk_shape - ) - - if self._compressor: - self._compressor.decode(chunk, out) - else: - ndarray_copy(chunk, out) - - # we can byteswap in-place - if self._little_endian: - out.byteswap(True) - - return out - - else: - if self._compressor: - chunk = self._compressor.decode(chunk) - - # more expensive byteswap - chunk = self._from_big_endian(chunk) - - # read partial chunk - if chunk_shape != self.chunk_shape: - chunk = np.frombuffer(chunk, dtype=self.dtype) - chunk = chunk.reshape(chunk_shape) - complete_chunk = np.zeros(self.chunk_shape, dtype=self.dtype) - target_slices = tuple(slice(0, s) for s in chunk_shape) - complete_chunk[target_slices] = chunk - chunk = complete_chunk - - return chunk - - @staticmethod - def _create_header(chunk): - mode = struct.pack(">H", 0) - num_dims = struct.pack(">H", len(chunk.shape)) - shape = b"".join(struct.pack(">I", d) for d in chunk.shape[::-1]) - - return mode + num_dims + shape - - @staticmethod - def _read_header(chunk): - num_dims = struct.unpack(">H", chunk[2:4])[0] - shape = tuple( - struct.unpack(">I", chunk[i : i + 4])[0] for i in range(4, num_dims * 4 + 4, 4) - )[::-1] - - len_header = 4 + num_dims * 4 - - return len_header, shape - - def _to_big_endian(self, data): - # assumes data is ndarray - - if self._little_endian: - return data.byteswap() - return data - - def _from_big_endian(self, data): - # assumes data is byte array in big endian - - if not self._little_endian: - return data - - a = np.frombuffer(data, self.dtype.newbyteorder(">")) - return a.astype(self.dtype) - - -register_codec(N5ChunkWrapper, N5ChunkWrapper.codec_id) diff --git a/src/zarr/v2/storage.py b/src/zarr/v2/storage.py deleted file mode 100644 index 67240e520d..0000000000 --- a/src/zarr/v2/storage.py +++ /dev/null @@ -1,2822 +0,0 @@ -"""This module contains storage classes for use with Zarr arrays and groups. - -Note that any object implementing the :class:`MutableMapping` interface from the -:mod:`collections` module in the Python standard library can be used as a Zarr -array store, as long as it accepts string (str) keys and bytes values. - -In addition to the :class:`MutableMapping` interface, store classes may also implement -optional methods `listdir` (list members of a "directory") and `rmdir` (remove all -members of a "directory"). These methods should be implemented if the store class is -aware of the hierarchical organisation of resources within the store and can provide -efficient implementations. If these methods are not available, Zarr will fall back to -slower implementations that work via the :class:`MutableMapping` interface. Store -classes may also optionally implement a `rename` method (rename all members under a given -path) and a `getsize` method (return the size in bytes of a given value). - -""" - -import atexit -import errno -import glob -import multiprocessing -import operator -import os -import re -import shutil -import sys -import tempfile -import warnings -import zipfile -from collections import OrderedDict -from collections.abc import MutableMapping -from os import scandir -from pickle import PicklingError -from threading import Lock, RLock -from typing import Sequence, Mapping, Optional, Union, List, Tuple, Dict, Any -import uuid -import time - -from numcodecs.compat import ensure_bytes, ensure_text, ensure_contiguous_ndarray_like -from numcodecs.registry import codec_registry -from zarr.v2.context import Context - -from zarr.v2.errors import ( - MetadataError, - BadCompressorError, - ContainsArrayError, - ContainsGroupError, - FSPathExistNotDir, - ReadOnlyError, -) -from zarr.v2.meta import encode_array_metadata, encode_group_metadata -from zarr.v2.util import ( - buffer_size, - json_loads, - nolock, - normalize_chunks, - normalize_dimension_separator, - normalize_dtype, - normalize_fill_value, - normalize_order, - normalize_shape, - normalize_storage_path, - retry_call, - ensure_contiguous_ndarray_or_bytes, -) - -from zarr.v2._storage.absstore import ABSStore # noqa: F401 -from zarr.v2._storage.store import ( # noqa: F401 - _listdir_from_keys, - _rename_from_keys, - _rmdir_from_keys, - _path_to_prefix, - _prefix_to_array_key, - _prefix_to_group_key, - array_meta_key, - attrs_key, - group_meta_key, - DEFAULT_ZARR_VERSION, - BaseStore, - Store, -) - -__doctest_requires__ = { - ("RedisStore", "RedisStore.*"): ["redis"], - ("MongoDBStore", "MongoDBStore.*"): ["pymongo"], - ("LRUStoreCache", "LRUStoreCache.*"): ["s3fs"], -} - - -try: - # noinspection PyUnresolvedReferences - from zarr.v2.codecs import Blosc - - default_compressor = Blosc() -except ImportError: # pragma: no cover - from zarr.v2.codecs import Zlib - - default_compressor = Zlib() - - -Path = Union[str, bytes, None] -# allow MutableMapping for backwards compatibility -StoreLike = Union[BaseStore, MutableMapping[str, Any]] - - -def contains_array(store: StoreLike, path: Path = None) -> bool: - """Return True if the store contains an array at the given logical path.""" - path = normalize_storage_path(path) - prefix = _path_to_prefix(path) - key = _prefix_to_array_key(store, prefix) - return key in store - - -def contains_group(store: StoreLike, path: Path = None, explicit_only=True) -> bool: - """Return True if the store contains a group at the given logical path.""" - path = normalize_storage_path(path) - prefix = _path_to_prefix(path) - key = _prefix_to_group_key(store, prefix) - return key in store - - -def normalize_store_arg(store: Any, storage_options=None, mode="r") -> BaseStore: - if store is None: - store = KVStore(dict()) - return store - if isinstance(store, os.PathLike): - store = os.fspath(store) - if FSStore._fsspec_installed(): - import fsspec - - if isinstance(store, fsspec.FSMap): - return FSStore( - store.root, - fs=store.fs, - mode=mode, - check=store.check, - create=store.create, - missing_exceptions=store.missing_exceptions, - **(storage_options or {}), - ) - if isinstance(store, str): - if "://" in store or "::" in store: - return FSStore(store, mode=mode, **(storage_options or {})) - elif storage_options: - raise ValueError("storage_options passed with non-fsspec path") - if store.endswith(".zip"): - return ZipStore(store, mode=mode) - elif store.endswith(".n5"): - from zarr.v2.n5 import N5Store - - return N5Store(store) - else: - return DirectoryStore(store) - else: - store = Store._ensure_store(store) - return store - - -def rmdir(store: StoreLike, path: Path = None): - """Remove all items under the given path. If `store` provides a `rmdir` method, - this will be called, otherwise will fall back to implementation via the - `Store` interface.""" - path = normalize_storage_path(path) - if hasattr(store, "rmdir") and store.is_erasable(): # type: ignore - # pass through - store.rmdir(path) - else: - # slow version, delete one key at a time - _rmdir_from_keys(store, path) - - -def rename(store: Store, src_path: Path, dst_path: Path): - """Rename all items under the given path. If `store` provides a `rename` method, - this will be called, otherwise will fall back to implementation via the - `Store` interface.""" - src_path = normalize_storage_path(src_path) - dst_path = normalize_storage_path(dst_path) - if hasattr(store, "rename"): - # pass through - store.rename(src_path, dst_path) - else: - # slow version, delete one key at a time - _rename_from_keys(store, src_path, dst_path) - - -def listdir(store: BaseStore, path: Path = None): - """Obtain a directory listing for the given path. If `store` provides a `listdir` - method, this will be called, otherwise will fall back to implementation via the - `MutableMapping` interface.""" - path = normalize_storage_path(path) - if hasattr(store, "listdir"): - # pass through - return store.listdir(path) - else: - # slow version, iterate through all keys - warnings.warn( - f"Store {store} has no `listdir` method. From zarr 2.9 onwards " - "may want to inherit from `Store`.", - stacklevel=2, - ) - return _listdir_from_keys(store, path) - - -def _getsize(store: BaseStore, path: Path = None) -> int: - # compute from size of values - if isinstance(path, str) and path in store: - v = store[path] - size = buffer_size(v) - else: - path = "" if path is None else normalize_storage_path(path) - size = 0 - - members = listdir(store, path) - prefix = _path_to_prefix(path) - members = [prefix + k for k in members] - for k in members: - try: - v = store[k] - except KeyError: - pass - else: - try: - size += buffer_size(v) - except TypeError: - return -1 - return size - - -def getsize(store: BaseStore, path: Path = None) -> int: - """Compute size of stored items for a given path. If `store` provides a `getsize` - method, this will be called, otherwise will return -1.""" - if hasattr(store, "getsize"): - # pass through - path = normalize_storage_path(path) - return store.getsize(path) - elif isinstance(store, MutableMapping): - return _getsize(store, path) - else: - return -1 - - -def _require_parent_group( - path: Optional[str], - store: StoreLike, - chunk_store: Optional[StoreLike], - overwrite: bool, -): - # assume path is normalized - if path: - segments = path.split("/") - for i in range(len(segments)): - p = "/".join(segments[:i]) - if contains_array(store, p): - _init_group_metadata(store, path=p, chunk_store=chunk_store, overwrite=overwrite) - elif not contains_group(store, p): - _init_group_metadata(store, path=p, chunk_store=chunk_store) - - -def init_array( - store: StoreLike, - shape: Union[int, Tuple[int, ...]], - chunks: Union[bool, int, Tuple[int, ...]] = True, - dtype=None, - compressor="default", - fill_value=None, - order: str = "C", - overwrite: bool = False, - path: Optional[Path] = None, - chunk_store: Optional[StoreLike] = None, - filters=None, - object_codec=None, - dimension_separator=None, - storage_transformers=(), -): - """Initialize an array store with the given configuration. Note that this is a low-level - function and there should be no need to call this directly from user code. - - Parameters - ---------- - store : Store - A mapping that supports string keys and bytes-like values. - shape : int or tuple of ints - Array shape. - chunks : bool, int or tuple of ints, optional - Chunk shape. If True, will be guessed from `shape` and `dtype`. If - False, will be set to `shape`, i.e., single chunk for the whole array. - dtype : string or dtype, optional - NumPy dtype. - compressor : Codec, optional - Primary compressor. - fill_value : object - Default value to use for uninitialized portions of the array. - order : {'C', 'F'}, optional - Memory layout to be used within each chunk. - overwrite : bool, optional - If True, erase all data in `store` prior to initialisation. - path : string, bytes, optional - Path under which array is stored. - chunk_store : Store, optional - Separate storage for chunks. If not provided, `store` will be used - for storage of both chunks and metadata. - filters : sequence, optional - Sequence of filters to use to encode chunk data prior to compression. - object_codec : Codec, optional - A codec to encode object arrays, only needed if dtype=object. - dimension_separator : {'.', '/'}, optional - Separator placed between the dimensions of a chunk. - - Examples - -------- - Initialize an array store:: - - >>> from zarr.v2.storage import init_array, KVStore - >>> store = KVStore(dict()) - >>> init_array(store, shape=(10000, 10000), chunks=(1000, 1000)) - >>> sorted(store.keys()) - ['.zarray'] - - Array metadata is stored as JSON:: - - >>> print(store['.zarray'].decode()) - { - "chunks": [ - 1000, - 1000 - ], - "compressor": { - "blocksize": 0, - "clevel": 5, - "cname": "lz4", - "id": "blosc", - "shuffle": 1 - }, - "dtype": ">> store = KVStore(dict()) - >>> init_array(store, shape=100000000, chunks=1000000, dtype='i1', path='foo') - >>> sorted(store.keys()) - ['.zgroup', 'foo/.zarray'] - >>> print(store['foo/.zarray'].decode()) - { - "chunks": [ - 1000000 - ], - "compressor": { - "blocksize": 0, - "clevel": 5, - "cname": "lz4", - "id": "blosc", - "shuffle": 1 - }, - "dtype": "|i1", - "fill_value": null, - "filters": null, - "order": "C", - "shape": [ - 100000000 - ], - "zarr_format": 2 - } - - Notes - ----- - The initialisation process involves normalising all array metadata, encoding - as JSON and storing under the '.zarray' key. - - """ - - # normalize path - path = normalize_storage_path(path) - - # ensure parent group initialized - - _require_parent_group(path, store=store, chunk_store=chunk_store, overwrite=overwrite) - - if not compressor: - # compatibility with legacy tests using compressor=[] - compressor = None - _init_array_metadata( - store, - shape=shape, - chunks=chunks, - dtype=dtype, - compressor=compressor, - fill_value=fill_value, - order=order, - overwrite=overwrite, - path=path, - chunk_store=chunk_store, - filters=filters, - object_codec=object_codec, - dimension_separator=dimension_separator, - storage_transformers=storage_transformers, - ) - - -def _init_array_metadata( - store: StoreLike, - shape, - chunks=None, - dtype=None, - compressor="default", - fill_value=None, - order="C", - overwrite=False, - path: Optional[str] = None, - chunk_store: Optional[StoreLike] = None, - filters=None, - object_codec=None, - dimension_separator=None, - storage_transformers=(), -): - path = normalize_storage_path(path) - - # guard conditions - if overwrite: - # attempt to delete any pre-existing array in store - rmdir(store, path) - if chunk_store is not None: - rmdir(chunk_store, path) - - if not overwrite: - if contains_array(store, path): - raise ContainsArrayError(path) - if contains_group(store, path, explicit_only=False): - raise ContainsGroupError(path) - - # normalize metadata - dtype, object_codec = normalize_dtype(dtype, object_codec) - shape = normalize_shape(shape) + dtype.shape - dtype = dtype.base - chunks = normalize_chunks(chunks, shape, dtype.itemsize) - order = normalize_order(order) - fill_value = normalize_fill_value(fill_value, dtype) - - # optional array metadata - if dimension_separator is None: - dimension_separator = getattr(store, "_dimension_separator", None) - dimension_separator = normalize_dimension_separator(dimension_separator) - - # compressor prep - if shape == (): - # no point in compressing a 0-dimensional array, only a single value - compressor = None - elif compressor == "none": - # compatibility - compressor = None - elif compressor == "default": - compressor = default_compressor - - # obtain compressor config - compressor_config = None - if compressor: - try: - compressor_config = compressor.get_config() - except AttributeError as e: - raise BadCompressorError(compressor) from e - - # obtain filters config - if filters: - # TODO: filters was removed from the metadata in v3 - # raise error here if store_version > 2? - filters_config = [f.get_config() for f in filters] - else: - filters_config = [] - - # deal with object encoding - if dtype.hasobject: - if object_codec is None: - if not filters: - # there are no filters so we can be sure there is no object codec - raise ValueError("missing object_codec for object array") - else: - # one of the filters may be an object codec, issue a warning rather - # than raise an error to maintain backwards-compatibility - warnings.warn( - "missing object_codec for object array; this will raise a " - "ValueError in version 3.0", - FutureWarning, - ) - else: - filters_config.insert(0, object_codec.get_config()) - elif object_codec is not None: - warnings.warn("an object_codec is only needed for object arrays") - - # use null to indicate no filters - if not filters_config: - filters_config = None # type: ignore - - # initialize metadata - _compressor = compressor_config - meta = dict( - shape=shape, - compressor=_compressor, - fill_value=fill_value, - dimension_separator=dimension_separator, - ) - - meta.update(dict(chunks=chunks, dtype=dtype, order=order, filters=filters_config)) - assert not storage_transformers - - key = _prefix_to_array_key(store, _path_to_prefix(path)) - if hasattr(store, "_metadata_class"): - store[key] = store._metadata_class.encode_array_metadata(meta) - else: - store[key] = encode_array_metadata(meta) - - -# backwards compatibility -init_store = init_array - - -def init_group( - store: StoreLike, - overwrite: bool = False, - path: Path = None, - chunk_store: Optional[StoreLike] = None, -): - """Initialize a group store. Note that this is a low-level function and there should be no - need to call this directly from user code. - - Parameters - ---------- - store : Store - A mapping that supports string keys and byte sequence values. - overwrite : bool, optional - If True, erase all data in `store` prior to initialisation. - path : string, optional - Path under which array is stored. - chunk_store : Store, optional - Separate storage for chunks. If not provided, `store` will be used - for storage of both chunks and metadata. - - """ - - # normalize path - path = normalize_storage_path(path) - - _require_parent_group(path, store=store, chunk_store=chunk_store, overwrite=overwrite) - - # initialise metadata - _init_group_metadata(store=store, overwrite=overwrite, path=path, chunk_store=chunk_store) - - -def _init_group_metadata( - store: StoreLike, - overwrite: Optional[bool] = False, - path: Optional[str] = None, - chunk_store: Optional[StoreLike] = None, -): - path = normalize_storage_path(path) - - # guard conditions - if overwrite: - # attempt to delete any pre-existing items in store - rmdir(store, path) - if chunk_store is not None: - rmdir(chunk_store, path) - - if not overwrite: - if contains_array(store, path): - raise ContainsArrayError(path) - elif contains_group(store, path): - raise ContainsGroupError(path) - - # initialize metadata - # N.B., currently no metadata properties are needed, however there may - # be in future - meta: dict[str, Any] = {} - key = _prefix_to_group_key(store, _path_to_prefix(path)) - if hasattr(store, "_metadata_class"): - store[key] = store._metadata_class.encode_group_metadata(meta) - else: - store[key] = encode_group_metadata(meta) - - -def _dict_store_keys(d: dict[str, Any], prefix="", cls=dict): - for k in d.keys(): - v = d[k] - if isinstance(v, cls): - yield from _dict_store_keys(v, prefix + k + "/", cls) - else: - yield prefix + k - - -class KVStore(Store): - """ - This provides a default implementation of a store interface around - a mutable mapping, to avoid having to test stores for presence of methods. - - This, for most methods should just be a pass-through to the underlying KV - store which is likely to expose a MuttableMapping interface, - """ - - def __init__(self, mutablemapping): - self._mutable_mapping = mutablemapping - - def __getitem__(self, key): - return self._mutable_mapping[key] - - def __setitem__(self, key, value): - self._mutable_mapping[key] = value - - def __delitem__(self, key): - del self._mutable_mapping[key] - - def __contains__(self, key): - return key in self._mutable_mapping - - def get(self, key, default=None): - return self._mutable_mapping.get(key, default) - - def values(self): - return self._mutable_mapping.values() - - def __iter__(self): - return iter(self._mutable_mapping) - - def __len__(self): - return len(self._mutable_mapping) - - def __repr__(self): - return f"<{self.__class__.__name__}: \n{self._mutable_mapping!r}\n at {hex(id(self))}>" - - def __eq__(self, other): - if isinstance(other, KVStore): - return self._mutable_mapping == other._mutable_mapping - else: - return NotImplemented - - -class MemoryStore(Store): - """Store class that uses a hierarchy of :class:`KVStore` objects, thus all data - will be held in main memory. - - Examples - -------- - This is the default class used when creating a group. E.g.:: - - >>> import zarr - >>> g = zarr.v2.group() - >>> type(g.store) - - - Note that the default class when creating an array is the built-in - :class:`KVStore` class, i.e.:: - - >>> z = zarr.v2.zeros(100) - >>> type(z.store) - - - Notes - ----- - Safe to write in multiple threads. - - """ - - def __init__(self, root=None, cls=dict, dimension_separator=None): - if root is None: - self.root = cls() - else: - self.root = root - self.cls = cls - self.write_mutex = Lock() - self._dimension_separator = dimension_separator - - def __getstate__(self): - return self.root, self.cls - - def __setstate__(self, state): - root, cls = state - self.__init__(root=root, cls=cls) - - def _get_parent(self, item: str): - parent = self.root - # split the item - segments = item.split("/") - # find the parent container - for k in segments[:-1]: - parent = parent[k] - if not isinstance(parent, self.cls): - raise KeyError(item) - return parent, segments[-1] - - def _require_parent(self, item): - parent = self.root - # split the item - segments = item.split("/") - # require the parent container - for k in segments[:-1]: - try: - parent = parent[k] - except KeyError: - parent[k] = self.cls() - parent = parent[k] - else: - if not isinstance(parent, self.cls): - raise KeyError(item) - return parent, segments[-1] - - def __getitem__(self, item: str): - parent, key = self._get_parent(item) - try: - value = parent[key] - except KeyError: - raise KeyError(item) - else: - if isinstance(value, self.cls): - raise KeyError(item) - else: - return value - - def __setitem__(self, item: str, value): - with self.write_mutex: - parent, key = self._require_parent(item) - value = ensure_bytes(value) - parent[key] = value - - def __delitem__(self, item: str): - with self.write_mutex: - parent, key = self._get_parent(item) - try: - del parent[key] - except KeyError: - raise KeyError(item) - - def __contains__(self, item: str): # type: ignore[override] - try: - parent, key = self._get_parent(item) - value = parent[key] - except KeyError: - return False - else: - return not isinstance(value, self.cls) - - def __eq__(self, other): - return isinstance(other, MemoryStore) and self.root == other.root and self.cls == other.cls - - def keys(self): - yield from _dict_store_keys(self.root, cls=self.cls) - - def __iter__(self): - return self.keys() - - def __len__(self) -> int: - return sum(1 for _ in self.keys()) - - def listdir(self, path: Path = None) -> List[str]: - path = normalize_storage_path(path) - if path: - try: - parent, key = self._get_parent(path) - value = parent[key] - except KeyError: - return [] - else: - value = self.root - if isinstance(value, self.cls): - return sorted(value.keys()) - else: - return [] - - def rename(self, src_path: Path, dst_path: Path): - src_path = normalize_storage_path(src_path) - dst_path = normalize_storage_path(dst_path) - - src_parent, src_key = self._get_parent(src_path) - dst_parent, dst_key = self._require_parent(dst_path) - - dst_parent[dst_key] = src_parent.pop(src_key) - - def rmdir(self, path: Path = None): - path = normalize_storage_path(path) - if path: - try: - parent, key = self._get_parent(path) - value = parent[key] - except KeyError: - return - else: - if isinstance(value, self.cls): - del parent[key] - else: - # clear out root - self.root = self.cls() - - def getsize(self, path: Path = None): - path = normalize_storage_path(path) - - # obtain value to return size of - value = None - if path: - try: - parent, key = self._get_parent(path) - value = parent[key] - except KeyError: - pass - else: - value = self.root - - # obtain size of value - if value is None: - return 0 - - elif isinstance(value, self.cls): - # total size for directory - size = 0 - for v in value.values(): - if not isinstance(v, self.cls): - size += buffer_size(v) - return size - - else: - return buffer_size(value) - - def clear(self): - with self.write_mutex: - self.root.clear() - - -class DictStore(MemoryStore): - def __init__(self, *args, **kwargs): - warnings.warn( - "DictStore has been renamed to MemoryStore in 2.4.0 and " - "will be removed in the future. Please use MemoryStore.", - DeprecationWarning, - stacklevel=2, - ) - super().__init__(*args, **kwargs) - - -class DirectoryStore(Store): - """Storage class using directories and files on a standard file system. - - Parameters - ---------- - path : string - Location of directory to use as the root of the storage hierarchy. - normalize_keys : bool, optional - If True, all store keys will be normalized to use lower case characters - (e.g. 'foo' and 'FOO' will be treated as equivalent). This can be - useful to avoid potential discrepancies between case-sensitive and - case-insensitive file system. Default value is False. - dimension_separator : {'.', '/'}, optional - Separator placed between the dimensions of a chunk. - - Examples - -------- - Store a single array:: - - >>> import zarr - >>> store = zarr.v2.DirectoryStore('data/array.zarr') - >>> z = zarr.v2.zeros((10, 10), chunks=(5, 5), store=store, overwrite=True) - >>> z[...] = 42 - - Each chunk of the array is stored as a separate file on the file system, - i.e.:: - - >>> import os - >>> sorted(os.listdir('data/array.zarr')) - ['.zarray', '0.0', '0.1', '1.0', '1.1'] - - Store a group:: - - >>> store = zarr.v2.DirectoryStore('data/group.zarr') - >>> root = zarr.v2.group(store=store, overwrite=True) - >>> foo = root.create_group('foo') - >>> bar = foo.zeros('bar', shape=(10, 10), chunks=(5, 5)) - >>> bar[...] = 42 - - When storing a group, levels in the group hierarchy will correspond to - directories on the file system, i.e.:: - - >>> sorted(os.listdir('data/group.zarr')) - ['.zgroup', 'foo'] - >>> sorted(os.listdir('data/group.zarr/foo')) - ['.zgroup', 'bar'] - >>> sorted(os.listdir('data/group.zarr/foo/bar')) - ['.zarray', '0.0', '0.1', '1.0', '1.1'] - - Notes - ----- - Atomic writes are used, which means that data are first written to a - temporary file, then moved into place when the write is successfully - completed. Files are only held open while they are being read or written and are - closed immediately afterwards, so there is no need to manually close any files. - - Safe to write in multiple threads or processes. - - """ - - def __init__(self, path, normalize_keys=False, dimension_separator=None): - # guard conditions - path = os.path.abspath(path) - if os.path.exists(path) and not os.path.isdir(path): - raise FSPathExistNotDir(path) - - self.path = path - self.normalize_keys = normalize_keys - self._dimension_separator = dimension_separator - - def _normalize_key(self, key): - return key.lower() if self.normalize_keys else key - - @staticmethod - def _fromfile(fn): - """Read data from a file - - Parameters - ---------- - fn : str - Filepath to open and read from. - - Notes - ----- - Subclasses should overload this method to specify any custom - file reading logic. - """ - with open(fn, "rb") as f: - return f.read() - - @staticmethod - def _tofile(a, fn): - """Write data to a file - - Parameters - ---------- - a : array-like - Data to write into the file. - fn : str - Filepath to open and write to. - - Notes - ----- - Subclasses should overload this method to specify any custom - file writing logic. - """ - with open(fn, mode="wb") as f: - f.write(a) - - def __getitem__(self, key): - key = self._normalize_key(key) - filepath = os.path.join(self.path, key) - if os.path.isfile(filepath): - return self._fromfile(filepath) - else: - raise KeyError(key) - - def __setitem__(self, key, value): - key = self._normalize_key(key) - - # coerce to flat, contiguous array (ideally without copying) - value = ensure_contiguous_ndarray_like(value) - - # destination path for key - file_path = os.path.join(self.path, key) - - # ensure there is no directory in the way - if os.path.isdir(file_path): - shutil.rmtree(file_path) - - # ensure containing directory exists - dir_path, file_name = os.path.split(file_path) - if os.path.isfile(dir_path): - raise KeyError(key) - if not os.path.exists(dir_path): - try: - os.makedirs(dir_path) - except OSError as e: - if e.errno != errno.EEXIST: - raise KeyError(key) - - # write to temporary file - # note we're not using tempfile.NamedTemporaryFile to avoid restrictive file permissions - temp_name = file_name + "." + uuid.uuid4().hex + ".partial" - temp_path = os.path.join(dir_path, temp_name) - try: - self._tofile(value, temp_path) - - # move temporary file into place; - # make several attempts at writing the temporary file to get past - # potential antivirus file locking issues - retry_call(os.replace, (temp_path, file_path), exceptions=(PermissionError,)) - - finally: - # clean up if temp file still exists for whatever reason - if os.path.exists(temp_path): # pragma: no cover - os.remove(temp_path) - - def __delitem__(self, key): - key = self._normalize_key(key) - path = os.path.join(self.path, key) - if os.path.isfile(path): - os.remove(path) - elif os.path.isdir(path): - # include support for deleting directories, even though strictly - # speaking these do not exist as keys in the store - shutil.rmtree(path) - else: - raise KeyError(key) - - def __contains__(self, key): - key = self._normalize_key(key) - file_path = os.path.join(self.path, key) - return os.path.isfile(file_path) - - def __eq__(self, other): - return isinstance(other, DirectoryStore) and self.path == other.path - - def keys(self): - if os.path.exists(self.path): - yield from self._keys_fast(self.path) - - @staticmethod - def _keys_fast(path, walker=os.walk): - for dirpath, _, filenames in walker(path): - dirpath = os.path.relpath(dirpath, path) - if dirpath == os.curdir: - for f in filenames: - yield f - else: - dirpath = dirpath.replace("\\", "/") - for f in filenames: - yield "/".join((dirpath, f)) - - def __iter__(self): - return self.keys() - - def __len__(self): - return sum(1 for _ in self.keys()) - - def dir_path(self, path=None): - store_path = normalize_storage_path(path) - dir_path = self.path - if store_path: - dir_path = os.path.join(dir_path, store_path) - return dir_path - - def listdir(self, path=None): - return ( - self._nested_listdir(path) - if self._dimension_separator == "/" - else self._flat_listdir(path) - ) - - def _flat_listdir(self, path=None): - dir_path = self.dir_path(path) - if os.path.isdir(dir_path): - return sorted(os.listdir(dir_path)) - else: - return [] - - def _nested_listdir(self, path=None): - children = self._flat_listdir(path=path) - if array_meta_key in children: - # special handling of directories containing an array to map nested chunk - # keys back to standard chunk keys - new_children = [] - root_path = self.dir_path(path) - for entry in children: - entry_path = os.path.join(root_path, entry) - if _prog_number.match(entry) and os.path.isdir(entry_path): - for dir_path, _, file_names in os.walk(entry_path): - for file_name in file_names: - file_path = os.path.join(dir_path, file_name) - rel_path = file_path.split(root_path + os.path.sep)[1] - new_children.append( - rel_path.replace(os.path.sep, self._dimension_separator or ".") - ) - else: - new_children.append(entry) - return sorted(new_children) - else: - return children - - def rename(self, src_path, dst_path): - store_src_path = normalize_storage_path(src_path) - store_dst_path = normalize_storage_path(dst_path) - - dir_path = self.path - - src_path = os.path.join(dir_path, store_src_path) - dst_path = os.path.join(dir_path, store_dst_path) - - os.renames(src_path, dst_path) - - def rmdir(self, path=None): - store_path = normalize_storage_path(path) - dir_path = self.path - if store_path: - dir_path = os.path.join(dir_path, store_path) - if os.path.isdir(dir_path): - shutil.rmtree(dir_path) - - def getsize(self, path=None): - store_path = normalize_storage_path(path) - fs_path = self.path - if store_path: - fs_path = os.path.join(fs_path, store_path) - if os.path.isfile(fs_path): - return os.path.getsize(fs_path) - elif os.path.isdir(fs_path): - size = 0 - for child in scandir(fs_path): - if child.is_file(): - size += child.stat().st_size - return size - else: - return 0 - - def clear(self): - shutil.rmtree(self.path) - - -def atexit_rmtree(path, isdir=os.path.isdir, rmtree=shutil.rmtree): # pragma: no cover - """Ensure directory removal at interpreter exit.""" - if isdir(path): - rmtree(path) - - -# noinspection PyShadowingNames -def atexit_rmglob( - path, - glob=glob.glob, - isdir=os.path.isdir, - isfile=os.path.isfile, - remove=os.remove, - rmtree=shutil.rmtree, -): # pragma: no cover - """Ensure removal of multiple files at interpreter exit.""" - for p in glob(path): - if isfile(p): - remove(p) - elif isdir(p): - rmtree(p) - - -class FSStore(Store): - """Wraps an fsspec.FSMap to give access to arbitrary filesystems - - Requires that ``fsspec`` is installed, as well as any additional - requirements for the protocol chosen. - - Parameters - ---------- - url : str - The destination to map. If no fs is provided, should include protocol - and path, like "s3://bucket/root". If an fs is provided, can be a path - within that filesystem, like "bucket/root" - normalize_keys : bool - key_separator : str - public API for accessing dimension_separator. Never `None` - See dimension_separator for more information. - mode : str - "w" for writable, "r" for read-only - exceptions : list of Exception subclasses - When accessing data, any of these exceptions will be treated - as a missing key - dimension_separator : {'.', '/'}, optional - Separator placed between the dimensions of a chunk. - fs : fsspec.spec.AbstractFileSystem, optional - An existing filesystem to use for the store. - check : bool, optional - If True, performs a touch at the root location, to check for write access. - Passed to `fsspec.mapping.FSMap` constructor. - create : bool, optional - If True, performs a mkdir at the rool location. - Passed to `fsspec.mapping.FSMap` constructor. - missing_exceptions : sequence of Exceptions, optional - Exceptions classes to associate with missing files. - Passed to `fsspec.mapping.FSMap` constructor. - storage_options : passed to the fsspec implementation. Cannot be used - together with fs. - """ - - _array_meta_key = array_meta_key - _group_meta_key = group_meta_key - _attrs_key = attrs_key - - def __init__( - self, - url, - normalize_keys=False, - key_separator=None, - mode="w", - exceptions=(KeyError, PermissionError, IOError), - dimension_separator=None, - fs=None, - check=False, - create=False, - missing_exceptions=None, - **storage_options, - ): - if not self._fsspec_installed(): # pragma: no cover - raise ImportError("`fsspec` is required to use zarr's FSStore") - import fsspec - - mapper_options = {"check": check, "create": create} - # https://github.com/zarr-developers/zarr-python/pull/911#discussion_r841926292 - # Some fsspec implementations don't accept missing_exceptions. - # This is a workaround to avoid passing it in the most common scenarios. - # Remove this and add missing_exceptions to mapper_options when fsspec is released. - if missing_exceptions is not None: - mapper_options["missing_exceptions"] = missing_exceptions # pragma: no cover - - if fs is None: - protocol, _ = fsspec.core.split_protocol(url) - # set auto_mkdir to True for local file system - if protocol in (None, "file") and not storage_options.get("auto_mkdir"): - storage_options["auto_mkdir"] = True - self.map = fsspec.get_mapper(url, **{**mapper_options, **storage_options}) - self.fs = self.map.fs # for direct operations - self.path = self.fs._strip_protocol(url) - else: - if storage_options: - raise ValueError("Cannot specify both fs and storage_options") - self.fs = fs - self.path = self.fs._strip_protocol(url) - self.map = self.fs.get_mapper(self.path, **mapper_options) - - self.normalize_keys = normalize_keys - self.mode = mode - self.exceptions = exceptions - # For backwards compatibility. Guaranteed to be non-None - if key_separator is not None: - dimension_separator = key_separator - - self.key_separator = dimension_separator - self._default_key_separator() - - # Pass attributes to array creation - self._dimension_separator = dimension_separator - - def _default_key_separator(self): - if self.key_separator is None: - self.key_separator = "." - - def _normalize_key(self, key): - key = normalize_storage_path(key).lstrip("/") - if key: - *bits, end = key.split("/") - - if end not in (self._array_meta_key, self._group_meta_key, self._attrs_key): - end = end.replace(".", self.key_separator) - key = "/".join(bits + [end]) - - return key.lower() if self.normalize_keys else key - - def getitems( - self, keys: Sequence[str], *, contexts: Mapping[str, Context] - ) -> Mapping[str, Any]: - keys_transformed = [self._normalize_key(key) for key in keys] - results = self.map.getitems(keys_transformed, on_error="omit") - # The function calling this method may not recognize the transformed keys - # So we send the values returned by self.map.getitems back into the original key space. - return {keys[keys_transformed.index(rk)]: rv for rk, rv in results.items()} - - def __getitem__(self, key): - key = self._normalize_key(key) - try: - return self.map[key] - except self.exceptions as e: - raise KeyError(key) from e - - def setitems(self, values): - if self.mode == "r": - raise ReadOnlyError - - # Normalize keys and make sure the values are bytes - values = { - self._normalize_key(key): ensure_contiguous_ndarray_or_bytes(val) - for key, val in values.items() - } - self.map.setitems(values) - - def __setitem__(self, key, value): - if self.mode == "r": - raise ReadOnlyError - key = self._normalize_key(key) - value = ensure_contiguous_ndarray_or_bytes(value) - path = self.dir_path(key) - try: - if self.fs.isdir(path): - self.fs.rm(path, recursive=True) - self.map[key] = value - self.fs.invalidate_cache(self.fs._parent(path)) - except self.exceptions as e: - raise KeyError(key) from e - - def __delitem__(self, key): - if self.mode == "r": - raise ReadOnlyError - key = self._normalize_key(key) - path = self.dir_path(key) - if self.fs.isdir(path): - self.fs.rm(path, recursive=True) - else: - del self.map[key] - - def delitems(self, keys): - if self.mode == "r": - raise ReadOnlyError - # only remove the keys that exist in the store - nkeys = [self._normalize_key(key) for key in keys if key in self] - # rm errors if you pass an empty collection - if len(nkeys) > 0: - self.map.delitems(nkeys) - - def __contains__(self, key): - key = self._normalize_key(key) - return key in self.map - - def __eq__(self, other): - return type(self) is type(other) and self.map == other.map and self.mode == other.mode - - def keys(self): - return iter(self.map) - - def __iter__(self): - return self.keys() - - def __len__(self): - return len(list(self.keys())) - - def dir_path(self, path=None): - store_path = normalize_storage_path(path) - return self.map._key_to_str(store_path) - - def listdir(self, path=None): - dir_path = self.dir_path(path) - try: - children = sorted( - p.rstrip("/").rsplit("/", 1)[-1] for p in self.fs.ls(dir_path, detail=False) - ) - if self.key_separator != "/": - return children - else: - if self._array_meta_key in children: - # special handling of directories containing an array to map nested chunk - # keys back to standard chunk keys - new_children = [] - root_path = self.dir_path(path) - for entry in children: - entry_path = os.path.join(root_path, entry) - if _prog_number.match(entry) and self.fs.isdir(entry_path): - for file_name in self.fs.find(entry_path): - file_path = os.path.join(dir_path, file_name) - rel_path = file_path.split(root_path)[1] - rel_path = rel_path.lstrip("/") - new_children.append(rel_path.replace("/", ".")) - else: - new_children.append(entry) - return sorted(new_children) - else: - return children - except OSError: - return [] - - def rmdir(self, path=None): - if self.mode == "r": - raise ReadOnlyError - store_path = self.dir_path(path) - if self.fs.isdir(store_path): - self.fs.rm(store_path, recursive=True) - - def getsize(self, path=None): - store_path = self.dir_path(path) - return self.fs.du(store_path, True, True) - - def clear(self): - if self.mode == "r": - raise ReadOnlyError - self.map.clear() - - @classmethod - def _fsspec_installed(cls): - """Returns true if fsspec is installed""" - import importlib.util - - return importlib.util.find_spec("fsspec") is not None - - -class TempStore(DirectoryStore): - """Directory store using a temporary directory for storage. - - Parameters - ---------- - suffix : string, optional - Suffix for the temporary directory name. - prefix : string, optional - Prefix for the temporary directory name. - dir : string, optional - Path to parent directory in which to create temporary directory. - normalize_keys : bool, optional - If True, all store keys will be normalized to use lower case characters - (e.g. 'foo' and 'FOO' will be treated as equivalent). This can be - useful to avoid potential discrepancies between case-sensitive and - case-insensitive file system. Default value is False. - dimension_separator : {'.', '/'}, optional - Separator placed between the dimensions of a chunk. - """ - - # noinspection PyShadowingBuiltins - def __init__( - self, suffix="", prefix="zarr", dir=None, normalize_keys=False, dimension_separator=None - ): - path = tempfile.mkdtemp(suffix=suffix, prefix=prefix, dir=dir) - atexit.register(atexit_rmtree, path) - super().__init__(path, normalize_keys=normalize_keys) - - -_prog_ckey = re.compile(r"^(\d+)(\.\d+)+$") -_prog_number = re.compile(r"^\d+$") - - -class NestedDirectoryStore(DirectoryStore): - """Storage class using directories and files on a standard file system, with - special handling for chunk keys so that chunk files for multidimensional - arrays are stored in a nested directory tree. - - Parameters - ---------- - path : string - Location of directory to use as the root of the storage hierarchy. - normalize_keys : bool, optional - If True, all store keys will be normalized to use lower case characters - (e.g. 'foo' and 'FOO' will be treated as equivalent). This can be - useful to avoid potential discrepancies between case-sensitive and - case-insensitive file system. Default value is False. - dimension_separator : {'/'}, optional - Separator placed between the dimensions of a chunk. - Only supports "/" unlike other implementations. - - Examples - -------- - Store a single array:: - - >>> import zarr - >>> store = zarr.v2.NestedDirectoryStore('data/array.zarr') - >>> z = zarr.v2.zeros((10, 10), chunks=(5, 5), store=store, overwrite=True) - >>> z[...] = 42 - - Each chunk of the array is stored as a separate file on the file system, - note the multiple directory levels used for the chunk files:: - - >>> import os - >>> sorted(os.listdir('data/array.zarr')) - ['.zarray', '0', '1'] - >>> sorted(os.listdir('data/array.zarr/0')) - ['0', '1'] - >>> sorted(os.listdir('data/array.zarr/1')) - ['0', '1'] - - Store a group:: - - >>> store = zarr.v2.NestedDirectoryStore('data/group.zarr') - >>> root = zarr.v2.group(store=store, overwrite=True) - >>> foo = root.create_group('foo') - >>> bar = foo.zeros('bar', shape=(10, 10), chunks=(5, 5)) - >>> bar[...] = 42 - - When storing a group, levels in the group hierarchy will correspond to - directories on the file system, i.e.:: - - >>> sorted(os.listdir('data/group.zarr')) - ['.zgroup', 'foo'] - >>> sorted(os.listdir('data/group.zarr/foo')) - ['.zgroup', 'bar'] - >>> sorted(os.listdir('data/group.zarr/foo/bar')) - ['.zarray', '0', '1'] - >>> sorted(os.listdir('data/group.zarr/foo/bar/0')) - ['0', '1'] - >>> sorted(os.listdir('data/group.zarr/foo/bar/1')) - ['0', '1'] - - Notes - ----- - The :class:`DirectoryStore` class stores all chunk files for an array - together in a single directory. On some file systems, the potentially large - number of files in a single directory can cause performance issues. The - :class:`NestedDirectoryStore` class provides an alternative where chunk - files for multidimensional arrays will be organised into a directory - hierarchy, thus reducing the number of files in any one directory. - - Safe to write in multiple threads or processes. - - """ - - def __init__(self, path, normalize_keys=False, dimension_separator="/"): - super().__init__(path, normalize_keys=normalize_keys) - if dimension_separator is None: - dimension_separator = "/" - elif dimension_separator != "/": - raise ValueError("NestedDirectoryStore only supports '/' as dimension_separator") - self._dimension_separator = dimension_separator - - def __eq__(self, other): - return isinstance(other, NestedDirectoryStore) and self.path == other.path - - -# noinspection PyPep8Naming -class ZipStore(Store): - """Storage class using a Zip file. - - Parameters - ---------- - path : string - Location of file. - compression : integer, optional - Compression method to use when writing to the archive. - allowZip64 : bool, optional - If True (the default) will create ZIP files that use the ZIP64 - extensions when the zipfile is larger than 2 GiB. If False - will raise an exception when the ZIP file would require ZIP64 - extensions. - mode : string, optional - One of 'r' to read an existing file, 'w' to truncate and write a new - file, 'a' to append to an existing file, or 'x' to exclusively create - and write a new file. - dimension_separator : {'.', '/'}, optional - Separator placed between the dimensions of a chunk. - - Examples - -------- - Store a single array:: - - >>> import zarr - >>> store = zarr.v2.ZipStore('data/array.zip', mode='w') - >>> z = zarr.v2.zeros((10, 10), chunks=(5, 5), store=store) - >>> z[...] = 42 - >>> store.close() # don't forget to call this when you're done - - Store a group:: - - >>> store = zarr.v2.ZipStore('data/group.zip', mode='w') - >>> root = zarr.v2.group(store=store) - >>> foo = root.create_group('foo') - >>> bar = foo.zeros('bar', shape=(10, 10), chunks=(5, 5)) - >>> bar[...] = 42 - >>> store.close() # don't forget to call this when you're done - - After modifying a ZipStore, the ``close()`` method must be called, otherwise - essential data will not be written to the underlying Zip file. The ZipStore - class also supports the context manager protocol, which ensures the ``close()`` - method is called on leaving the context, e.g.:: - - >>> with zarr.v2.ZipStore('data/array.zip', mode='w') as store: - ... z = zarr.v2.zeros((10, 10), chunks=(5, 5), store=store) - ... z[...] = 42 - ... # no need to call store.close() - - Notes - ----- - Each chunk of an array is stored as a separate entry in the Zip file. Note - that Zip files do not provide any way to remove or replace existing entries. - If an attempt is made to replace an entry, then a warning is generated by - the Python standard library about a duplicate Zip file entry. This can be - triggered if you attempt to write data to a Zarr array more than once, - e.g.:: - - >>> store = zarr.v2.ZipStore('data/example.zip', mode='w') - >>> z = zarr.v2.zeros(100, chunks=10, store=store) - >>> # first write OK - ... z[...] = 42 - >>> # second write generates warnings - ... z[...] = 42 # doctest: +SKIP - >>> store.close() - - This can also happen in a more subtle situation, where data are written only - once to a Zarr array, but the write operations are not aligned with chunk - boundaries, e.g.:: - - >>> store = zarr.v2.ZipStore('data/example.zip', mode='w') - >>> z = zarr.v2.zeros(100, chunks=10, store=store) - >>> z[5:15] = 42 - >>> # write overlaps chunk previously written, generates warnings - ... z[15:25] = 42 # doctest: +SKIP - - To avoid creating duplicate entries, only write data once, and align writes - with chunk boundaries. This alignment is done automatically if you call - ``z[...] = ...`` or create an array from existing data via :func:`zarr.v2.array`. - - Alternatively, use a :class:`DirectoryStore` when writing the data, then - manually Zip the directory and use the Zip file for subsequent reads. - Take note that the files in the Zip file must be relative to the root of the - Zarr archive. You may find it easier to create such a Zip file with ``7z``, e.g.:: - - 7z a -tzip archive.zarr.v2.zip archive.zarr/. - - Safe to write in multiple threads but not in multiple processes. - - """ - - _erasable = False - - def __init__( - self, - path, - compression=zipfile.ZIP_STORED, - allowZip64=True, - mode="a", - dimension_separator=None, - ): - # store properties - path = os.path.abspath(path) - self.path = path - self.compression = compression - self.allowZip64 = allowZip64 - self.mode = mode - self._dimension_separator = dimension_separator - - # Current understanding is that zipfile module in stdlib is not thread-safe, - # and so locking is required for both read and write. However, this has not - # been investigated in detail, perhaps no lock is needed if mode='r'. - self.mutex = RLock() - - # open zip file - self.zf = zipfile.ZipFile(path, mode=mode, compression=compression, allowZip64=allowZip64) - - def __getstate__(self): - self.flush() - return self.path, self.compression, self.allowZip64, self.mode - - def __setstate__(self, state): - path, compression, allowZip64, mode = state - # if initially opened with mode 'w' or 'x', re-open in mode 'a' so file doesn't - # get clobbered - if mode in "wx": - mode = "a" - self.__init__(path=path, compression=compression, allowZip64=allowZip64, mode=mode) - - def close(self): - """Closes the underlying zip file, ensuring all records are written.""" - with self.mutex: - self.zf.close() - - def flush(self): - """Closes the underlying zip file, ensuring all records are written, - then re-opens the file for further modifications.""" - if self.mode != "r": - with self.mutex: - self.zf.close() - # N.B., re-open with mode 'a' regardless of initial mode so we don't wipe - # what's been written - self.zf = zipfile.ZipFile( - self.path, mode="a", compression=self.compression, allowZip64=self.allowZip64 - ) - - def __enter__(self): - return self - - def __exit__(self, *args): - self.close() - - def __getitem__(self, key): - with self.mutex: - with self.zf.open(key) as f: # will raise KeyError - return f.read() - - def __setitem__(self, key, value): - if self.mode == "r": - raise ReadOnlyError - value = ensure_contiguous_ndarray_like(value).view("u1") - with self.mutex: - # writestr(key, value) writes with default permissions from - # zipfile (600) that are too restrictive, build ZipInfo for - # the key to work around limitation - keyinfo = zipfile.ZipInfo(filename=key, date_time=time.localtime(time.time())[:6]) - keyinfo.compress_type = self.compression - if keyinfo.filename[-1] == os.sep: - keyinfo.external_attr = 0o40775 << 16 # drwxrwxr-x - keyinfo.external_attr |= 0x10 # MS-DOS directory flag - else: - keyinfo.external_attr = 0o644 << 16 # ?rw-r--r-- - - self.zf.writestr(keyinfo, value) - - def __delitem__(self, key): - raise NotImplementedError - - def __eq__(self, other): - return ( - isinstance(other, ZipStore) - and self.path == other.path - and self.compression == other.compression - and self.allowZip64 == other.allowZip64 - ) - - def keylist(self): - with self.mutex: - return sorted(self.zf.namelist()) - - def keys(self): - yield from self.keylist() - - def __iter__(self): - return self.keys() - - def __len__(self): - return sum(1 for _ in self.keys()) - - def __contains__(self, key): - try: - with self.mutex: - self.zf.getinfo(key) - except KeyError: - return False - else: - return True - - def listdir(self, path=None): - path = normalize_storage_path(path) - return _listdir_from_keys(self, path) - - def getsize(self, path=None): - path = normalize_storage_path(path) - with self.mutex: - children = self.listdir(path) - if children: - size = 0 - for child in children: - if path: - name = path + "/" + child - else: - name = child - try: - info = self.zf.getinfo(name) - except KeyError: - pass - else: - size += info.compress_size - return size - elif path: - try: - info = self.zf.getinfo(path) - return info.compress_size - except KeyError: - return 0 - else: - return 0 - - def clear(self): - if self.mode == "r": - raise ReadOnlyError - with self.mutex: - self.close() - os.remove(self.path) - self.zf = zipfile.ZipFile( - self.path, mode=self.mode, compression=self.compression, allowZip64=self.allowZip64 - ) - - -def migrate_1to2(store): - """Migrate array metadata in `store` from Zarr format version 1 to - version 2. - - Parameters - ---------- - store : Store - Store to be migrated. - - Notes - ----- - Version 1 did not support hierarchies, so this migration function will - look for a single array in `store` and migrate the array metadata to - version 2. - - """ - - # migrate metadata - from zarr.v2 import meta_v1 - - meta = meta_v1.decode_metadata(store["meta"]) - del store["meta"] - - # add empty filters - meta["filters"] = None - - # migration compression metadata - compression = meta["compression"] - if compression is None or compression == "none": - compressor_config = None - else: - compression_opts = meta["compression_opts"] - codec_cls = codec_registry[compression] - if isinstance(compression_opts, dict): - compressor = codec_cls(**compression_opts) - else: - compressor = codec_cls(compression_opts) - compressor_config = compressor.get_config() - meta["compressor"] = compressor_config - del meta["compression"] - del meta["compression_opts"] - - # store migrated metadata - if hasattr(store, "_metadata_class"): - store[array_meta_key] = store._metadata_class.encode_array_metadata(meta) - else: - store[array_meta_key] = encode_array_metadata(meta) - - # migrate user attributes - store[attrs_key] = store["attrs"] - del store["attrs"] - - -# noinspection PyShadowingBuiltins -class DBMStore(Store): - """Storage class using a DBM-style database. - - Parameters - ---------- - path : string - Location of database file. - flag : string, optional - Flags for opening the database file. - mode : int - File mode used if a new file is created. - open : function, optional - Function to open the database file. If not provided, :func:`dbm.open` will be - used on Python 3, and :func:`anydbm.open` will be used on Python 2. - write_lock: bool, optional - Use a lock to prevent concurrent writes from multiple threads (True by default). - dimension_separator : {'.', '/'}, optional - Separator placed between the dimensions of a chunk.e - **open_kwargs - Keyword arguments to pass the `open` function. - - Examples - -------- - Store a single array:: - - >>> import zarr - >>> store = zarr.v2.DBMStore('data/array.db') - >>> z = zarr.v2.zeros((10, 10), chunks=(5, 5), store=store, overwrite=True) - >>> z[...] = 42 - >>> store.close() # don't forget to call this when you're done - - Store a group:: - - >>> store = zarr.v2.DBMStore('data/group.db') - >>> root = zarr.v2.group(store=store, overwrite=True) - >>> foo = root.create_group('foo') - >>> bar = foo.zeros('bar', shape=(10, 10), chunks=(5, 5)) - >>> bar[...] = 42 - >>> store.close() # don't forget to call this when you're done - - After modifying a DBMStore, the ``close()`` method must be called, otherwise - essential data may not be written to the underlying database file. The - DBMStore class also supports the context manager protocol, which ensures the - ``close()`` method is called on leaving the context, e.g.:: - - >>> with zarr.v2.DBMStore('data/array.db') as store: - ... z = zarr.v2.zeros((10, 10), chunks=(5, 5), store=store, overwrite=True) - ... z[...] = 42 - ... # no need to call store.close() - - A different database library can be used by passing a different function to - the `open` parameter. For example, if the `bsddb3 - `_ package is installed, a - Berkeley DB database can be used:: - - >>> import bsddb3 - >>> store = zarr.v2.DBMStore('data/array.bdb', open=bsddb3.btopen) - >>> z = zarr.v2.zeros((10, 10), chunks=(5, 5), store=store, overwrite=True) - >>> z[...] = 42 - >>> store.close() - - Notes - ----- - Please note that, by default, this class will use the Python standard - library `dbm.open` function to open the database file (or `anydbm.open` on - Python 2). There are up to three different implementations of DBM-style - databases available in any Python installation, and which one is used may - vary from one system to another. Database file formats are not compatible - between these different implementations. Also, some implementations are - more efficient than others. In particular, the "dumb" implementation will be - the fall-back on many systems, and has very poor performance for some usage - scenarios. If you want to ensure a specific implementation is used, pass the - corresponding open function, e.g., `dbm.gnu.open` to use the GNU DBM - library. - - Safe to write in multiple threads. May be safe to write in multiple processes, - depending on which DBM implementation is being used, although this has not been - tested. - - """ - - def __init__( - self, - path, - flag="c", - mode=0o666, - open=None, - write_lock=True, - dimension_separator=None, - **open_kwargs, - ): - if open is None: - import dbm - - open = dbm.open - path = os.path.abspath(path) - # noinspection PyArgumentList - self.db = open(path, flag, mode, **open_kwargs) - self.path = path - self.flag = flag - self.mode = mode - self.open = open - self.write_lock = write_lock - if write_lock: - # This may not be required as some dbm implementations manage their own - # locks, but err on the side of caution. - self.write_mutex = Lock() - else: - self.write_mutex = nolock - self.open_kwargs = open_kwargs - self._dimension_separator = dimension_separator - - def __getstate__(self): - try: - self.flush() # needed for ndbm - except Exception: - # flush may fail if db has already been closed - pass - return (self.path, self.flag, self.mode, self.open, self.write_lock, self.open_kwargs) - - def __setstate__(self, state): - path, flag, mode, open, write_lock, open_kws = state - if flag[0] == "n": - flag = "c" + flag[1:] # don't clobber an existing database - self.__init__(path=path, flag=flag, mode=mode, open=open, write_lock=write_lock, **open_kws) - - def close(self): - """Closes the underlying database file.""" - if hasattr(self.db, "close"): - with self.write_mutex: - self.db.close() - - def flush(self): - """Synchronizes data to the underlying database file.""" - if self.flag[0] != "r": - with self.write_mutex: - if hasattr(self.db, "sync"): - self.db.sync() - else: # pragma: no cover - # we don't cover this branch anymore as ndbm (oracle) is not packaged - # by conda-forge on non-mac OS: - # https://github.com/conda-forge/staged-recipes/issues/4476 - # fall-back, close and re-open, needed for ndbm - flag = self.flag - if flag[0] == "n": - flag = "c" + flag[1:] # don't clobber an existing database - self.db.close() - # noinspection PyArgumentList - self.db = self.open(self.path, flag, self.mode, **self.open_kwargs) - - def __enter__(self): - return self - - def __exit__(self, *args): - self.close() - - def __getitem__(self, key): - if isinstance(key, str): - key = key.encode("ascii") - return self.db[key] - - def __setitem__(self, key, value): - if isinstance(key, str): - key = key.encode("ascii") - value = ensure_bytes(value) - with self.write_mutex: - self.db[key] = value - - def __delitem__(self, key): - if isinstance(key, str): - key = key.encode("ascii") - with self.write_mutex: - del self.db[key] - - def __eq__(self, other): - return ( - isinstance(other, DBMStore) - and self.path == other.path - and - # allow flag and mode to differ - self.open == other.open - and self.open_kwargs == other.open_kwargs - ) - - def keys(self): - return (ensure_text(k, "ascii") for k in iter(self.db.keys())) - - def __iter__(self): - return self.keys() - - def __len__(self): - return sum(1 for _ in self.keys()) - - def __contains__(self, key): - if isinstance(key, str): - key = key.encode("ascii") - return key in self.db - - def rmdir(self, path: str = "") -> None: - path = normalize_storage_path(path) - _rmdir_from_keys(self, path) - - -class LMDBStore(Store): - """Storage class using LMDB. Requires the `lmdb `_ - package to be installed. - - - Parameters - ---------- - path : string - Location of database file. - buffers : bool, optional - If True (default) use support for buffers, which should increase performance by - reducing memory copies. - dimension_separator : {'.', '/'}, optional - Separator placed between the dimensions of a chunk. - **kwargs - Keyword arguments passed through to the `lmdb.open` function. - - Examples - -------- - Store a single array:: - - >>> import zarr - >>> store = zarr.v2.LMDBStore('data/array.mdb') - >>> z = zarr.v2.zeros((10, 10), chunks=(5, 5), store=store, overwrite=True) - >>> z[...] = 42 - >>> store.close() # don't forget to call this when you're done - - Store a group:: - - >>> store = zarr.v2.LMDBStore('data/group.mdb') - >>> root = zarr.v2.group(store=store, overwrite=True) - >>> foo = root.create_group('foo') - >>> bar = foo.zeros('bar', shape=(10, 10), chunks=(5, 5)) - >>> bar[...] = 42 - >>> store.close() # don't forget to call this when you're done - - After modifying a DBMStore, the ``close()`` method must be called, otherwise - essential data may not be written to the underlying database file. The - DBMStore class also supports the context manager protocol, which ensures the - ``close()`` method is called on leaving the context, e.g.:: - - >>> with zarr.v2.LMDBStore('data/array.mdb') as store: - ... z = zarr.v2.zeros((10, 10), chunks=(5, 5), store=store, overwrite=True) - ... z[...] = 42 - ... # no need to call store.close() - - Notes - ----- - By default writes are not immediately flushed to disk to increase performance. You - can ensure data are flushed to disk by calling the ``flush()`` or ``close()`` methods. - - Should be safe to write in multiple threads or processes due to the synchronization - support within LMDB, although writing from multiple processes has not been tested. - - """ - - def __init__(self, path, buffers=True, dimension_separator=None, **kwargs): - import lmdb - - # set default memory map size to something larger than the lmdb default, which is - # very likely to be too small for any moderate array (logic copied from zict) - map_size = 2**40 if sys.maxsize >= 2**32 else 2**28 - kwargs.setdefault("map_size", map_size) - - # don't initialize buffers to zero by default, shouldn't be necessary - kwargs.setdefault("meminit", False) - - # decide whether to use the writemap option based on the operating system's - # support for sparse files - writemap requires sparse file support otherwise - # the whole# `map_size` may be reserved up front on disk (logic copied from zict) - writemap = sys.platform.startswith("linux") - kwargs.setdefault("writemap", writemap) - - # decide options for when data are flushed to disk - choose to delay syncing - # data to filesystem, otherwise pay a large performance penalty (zict also does - # this) - kwargs.setdefault("metasync", False) - kwargs.setdefault("sync", False) - kwargs.setdefault("map_async", False) - - # set default option for number of cached transactions - max_spare_txns = multiprocessing.cpu_count() - kwargs.setdefault("max_spare_txns", max_spare_txns) - - # normalize path - path = os.path.abspath(path) - - # open database - self.db = lmdb.open(path, **kwargs) - - # store properties - self.buffers = buffers - self.path = path - self.kwargs = kwargs - self._dimension_separator = dimension_separator - - def __getstate__(self): - try: - self.flush() # just in case - except Exception: - # flush may fail if db has already been closed - pass - return self.path, self.buffers, self.kwargs - - def __setstate__(self, state): - path, buffers, kwargs = state - self.__init__(path=path, buffers=buffers, **kwargs) - - def close(self): - """Closes the underlying database.""" - self.db.close() - - def flush(self): - """Synchronizes data to the file system.""" - self.db.sync() - - def __enter__(self): - return self - - def __exit__(self, *args): - self.close() - - def __getitem__(self, key): - if isinstance(key, str): - key = key.encode("ascii") - # use the buffers option, should avoid a memory copy - with self.db.begin(buffers=self.buffers) as txn: - value = txn.get(key) - if value is None: - raise KeyError(key) - return value - - def __setitem__(self, key, value): - if isinstance(key, str): - key = key.encode("ascii") - with self.db.begin(write=True, buffers=self.buffers) as txn: - txn.put(key, value) - - def __delitem__(self, key): - if isinstance(key, str): - key = key.encode("ascii") - with self.db.begin(write=True) as txn: - if not txn.delete(key): - raise KeyError(key) - - def __contains__(self, key): - if isinstance(key, str): - key = key.encode("ascii") - with self.db.begin(buffers=self.buffers) as txn: - with txn.cursor() as cursor: - return cursor.set_key(key) - - def items(self): - with self.db.begin(buffers=self.buffers) as txn: - with txn.cursor() as cursor: - for k, v in cursor.iternext(keys=True, values=True): - yield ensure_text(k, "ascii"), v - - def keys(self): - with self.db.begin(buffers=self.buffers) as txn: - with txn.cursor() as cursor: - for k in cursor.iternext(keys=True, values=False): - yield ensure_text(k, "ascii") - - def values(self): - with self.db.begin(buffers=self.buffers) as txn: - with txn.cursor() as cursor: - yield from cursor.iternext(keys=False, values=True) - - def __iter__(self): - return self.keys() - - def __len__(self): - return self.db.stat()["entries"] - - -class LRUStoreCache(Store): - """Storage class that implements a least-recently-used (LRU) cache layer over - some other store. Intended primarily for use with stores that can be slow to - access, e.g., remote stores that require network communication to store and - retrieve data. - - Parameters - ---------- - store : Store - The store containing the actual data to be cached. - max_size : int - The maximum size that the cache may grow to, in number of bytes. Provide `None` - if you would like the cache to have unlimited size. - - Examples - -------- - The example below wraps an S3 store with an LRU cache:: - - >>> import s3fs - >>> import zarr - >>> s3 = s3fs.S3FileSystem(anon=True, client_kwargs=dict(region_name='eu-west-2')) - >>> store = s3fs.S3Map(root='zarr-demo/store', s3=s3, check=False) - >>> cache = zarr.v2.LRUStoreCache(store, max_size=2**28) - >>> root = zarr.v2.group(store=cache) # doctest: +REMOTE_DATA - >>> z = root['foo/bar/baz'] # doctest: +REMOTE_DATA - >>> from timeit import timeit - >>> # first data access is relatively slow, retrieved from store - ... timeit('print(z[:].tobytes())', number=1, globals=globals()) # doctest: +SKIP - b'Hello from the cloud!' - 0.1081731989979744 - >>> # second data access is faster, uses cache - ... timeit('print(z[:].tobytes())', number=1, globals=globals()) # doctest: +SKIP - b'Hello from the cloud!' - 0.0009490990014455747 - - """ - - def __init__(self, store: StoreLike, max_size: int): - self._store: BaseStore = BaseStore._ensure_store(store) - self._max_size = max_size - self._current_size = 0 - self._keys_cache = None - self._contains_cache: Dict[Any, Any] = {} - self._listdir_cache: Dict[Path, Any] = dict() - self._values_cache: Dict[Path, Any] = OrderedDict() - self._mutex = Lock() - self.hits = self.misses = 0 - - def __getstate__(self): - return ( - self._store, - self._max_size, - self._current_size, - self._keys_cache, - self._contains_cache, - self._listdir_cache, - self._values_cache, - self.hits, - self.misses, - ) - - def __setstate__(self, state): - ( - self._store, - self._max_size, - self._current_size, - self._keys_cache, - self._contains_cache, - self._listdir_cache, - self._values_cache, - self.hits, - self.misses, - ) = state - self._mutex = Lock() - - def __len__(self): - return len(self._keys()) - - def __iter__(self): - return self.keys() - - def __contains__(self, key): - with self._mutex: - if key not in self._contains_cache: - self._contains_cache[key] = key in self._store - return self._contains_cache[key] - - def clear(self): - self._store.clear() - self.invalidate() - - def keys(self): - with self._mutex: - return iter(self._keys()) - - def _keys(self): - if self._keys_cache is None: - self._keys_cache = list(self._store.keys()) - return self._keys_cache - - def listdir(self, path: Path = None): - with self._mutex: - try: - return self._listdir_cache[path] - except KeyError: - listing = listdir(self._store, path) - self._listdir_cache[path] = listing - return listing - - def getsize(self, path=None) -> int: - return getsize(self._store, path=path) - - def _pop_value(self): - # remove the first value from the cache, as this will be the least recently - # used value - _, v = self._values_cache.popitem(last=False) - return v - - def _accommodate_value(self, value_size): - if self._max_size is None: - return - # ensure there is enough space in the cache for a new value - while self._current_size + value_size > self._max_size: - v = self._pop_value() - self._current_size -= buffer_size(v) - - def _cache_value(self, key: Path, value): - # cache a value - value_size = buffer_size(value) - # check size of the value against max size, as if the value itself exceeds max - # size then we are never going to cache it - if self._max_size is None or value_size <= self._max_size: - self._accommodate_value(value_size) - self._values_cache[key] = value - self._current_size += value_size - - def invalidate(self): - """Completely clear the cache.""" - with self._mutex: - self._values_cache.clear() - self._invalidate_keys() - self._current_size = 0 - - def invalidate_values(self): - """Clear the values cache.""" - with self._mutex: - self._values_cache.clear() - - def invalidate_keys(self): - """Clear the keys cache.""" - with self._mutex: - self._invalidate_keys() - - def _invalidate_keys(self): - self._keys_cache = None - self._contains_cache.clear() - self._listdir_cache.clear() - - def _invalidate_value(self, key): - if key in self._values_cache: - value = self._values_cache.pop(key) - self._current_size -= buffer_size(value) - - def __getitem__(self, key): - try: - # first try to obtain the value from the cache - with self._mutex: - value = self._values_cache[key] - # cache hit if no KeyError is raised - self.hits += 1 - # treat the end as most recently used - self._values_cache.move_to_end(key) - - except KeyError: - # cache miss, retrieve value from the store - value = self._store[key] - with self._mutex: - self.misses += 1 - # need to check if key is not in the cache, as it may have been cached - # while we were retrieving the value from the store - if key not in self._values_cache: - self._cache_value(key, value) - - return value - - def __setitem__(self, key, value): - self._store[key] = value - with self._mutex: - self._invalidate_keys() - self._invalidate_value(key) - self._cache_value(key, value) - - def __delitem__(self, key): - del self._store[key] - with self._mutex: - self._invalidate_keys() - self._invalidate_value(key) - - -class SQLiteStore(Store): - """Storage class using SQLite. - - Parameters - ---------- - path : string - Location of database file. - dimension_separator : {'.', '/'}, optional - Separator placed between the dimensions of a chunk. - **kwargs - Keyword arguments passed through to the `sqlite3.connect` function. - - Examples - -------- - Store a single array:: - - >>> import zarr - >>> store = zarr.v2.SQLiteStore('data/array.sqldb') - >>> z = zarr.v2.zeros((10, 10), chunks=(5, 5), store=store, overwrite=True) - >>> z[...] = 42 - >>> store.close() # don't forget to call this when you're done - - Store a group:: - - >>> store = zarr.v2.SQLiteStore('data/group.sqldb') - >>> root = zarr.v2.group(store=store, overwrite=True) - >>> foo = root.create_group('foo') - >>> bar = foo.zeros('bar', shape=(10, 10), chunks=(5, 5)) - >>> bar[...] = 42 - >>> store.close() # don't forget to call this when you're done - """ - - def __init__(self, path, dimension_separator=None, **kwargs): - import sqlite3 - - self._dimension_separator = dimension_separator - - # normalize path - if path != ":memory:": - path = os.path.abspath(path) - - # store properties - self.path = path - self.kwargs = kwargs - - # allow threading if SQLite connections are thread-safe - # - # ref: https://www.sqlite.org/releaselog/3_3_1.html - # ref: https://github.com/python/cpython/issues/71377 - check_same_thread = True - if sqlite3.sqlite_version_info >= (3, 3, 1): - check_same_thread = False - - # keep a lock for serializing mutable operations - self.lock = Lock() - - # open database - self.db = sqlite3.connect( - self.path, - detect_types=0, - isolation_level=None, - check_same_thread=check_same_thread, - **self.kwargs, - ) - - # handle keys as `str`s - self.db.text_factory = str - - # get a cursor to read/write to the database - self.cursor = self.db.cursor() - - # initialize database with our table if missing - with self.lock: - self.cursor.execute("CREATE TABLE IF NOT EXISTS zarr(k TEXT PRIMARY KEY, v BLOB)") - - def __getstate__(self): - if self.path == ":memory:": - raise PicklingError("Cannot pickle in-memory SQLite databases") - return self.path, self.kwargs - - def __setstate__(self, state): - path, kwargs = state - self.__init__(path=path, **kwargs) - - def close(self): - """Closes the underlying database.""" - - # close cursor and db objects - self.cursor.close() - self.db.close() - - def __getitem__(self, key): - value = self.cursor.execute("SELECT v FROM zarr WHERE (k = ?)", (key,)) - for (v,) in value: - return v - raise KeyError(key) - - def __setitem__(self, key, value): - self.update({key: value}) - - def __delitem__(self, key): - with self.lock: - self.cursor.execute("DELETE FROM zarr WHERE (k = ?)", (key,)) - if self.cursor.rowcount < 1: - raise KeyError(key) - - def __contains__(self, key): - cs = self.cursor.execute("SELECT COUNT(*) FROM zarr WHERE (k = ?)", (key,)) - for (has,) in cs: - has = bool(has) - return has - - def items(self): - kvs = self.cursor.execute("SELECT k, v FROM zarr") - yield from kvs - - def keys(self): - ks = self.cursor.execute("SELECT k FROM zarr") - for (k,) in ks: - yield k - - def values(self): - vs = self.cursor.execute("SELECT v FROM zarr") - for (v,) in vs: - yield v - - def __iter__(self): - return self.keys() - - def __len__(self): - cs = self.cursor.execute("SELECT COUNT(*) FROM zarr") - for (c,) in cs: - return c - - def update(self, *args, **kwargs): - args += (kwargs,) - - kv_list = [] - for dct in args: - for k, v in dct.items(): - v = ensure_contiguous_ndarray_like(v) - - # Accumulate key-value pairs for storage - kv_list.append((k, v)) - - with self.lock: - self.cursor.executemany("REPLACE INTO zarr VALUES (?, ?)", kv_list) - - def listdir(self, path=None): - path = normalize_storage_path(path) - sep = "_" if path == "" else "/" - keys = self.cursor.execute( - """ - SELECT DISTINCT SUBSTR(m, 0, INSTR(m, "/")) AS l FROM ( - SELECT LTRIM(SUBSTR(k, LENGTH(?) + 1), "/") || "/" AS m - FROM zarr WHERE k LIKE (? || "{sep}%") - ) ORDER BY l ASC - """.format(sep=sep), - (path, path), - ) - keys = list(map(operator.itemgetter(0), keys)) - return keys - - def getsize(self, path=None): - path = normalize_storage_path(path) - size = self.cursor.execute( - """ - SELECT COALESCE(SUM(LENGTH(v)), 0) FROM zarr - WHERE k LIKE (? || "%") AND - 0 == INSTR(LTRIM(SUBSTR(k, LENGTH(?) + 1), "/"), "/") - """, - (path, path), - ) - for (s,) in size: - return s - - def rmdir(self, path=None): - path = normalize_storage_path(path) - if path: - with self.lock: - self.cursor.execute('DELETE FROM zarr WHERE k LIKE (? || "/%")', (path,)) - else: - self.clear() - - def clear(self): - with self.lock: - self.cursor.executescript( - """ - BEGIN TRANSACTION; - DROP TABLE zarr; - CREATE TABLE zarr(k TEXT PRIMARY KEY, v BLOB); - COMMIT TRANSACTION; - """ - ) - - -class MongoDBStore(Store): - """Storage class using MongoDB. - - .. note:: This is an experimental feature. - - Requires the `pymongo `_ - package to be installed. - - Parameters - ---------- - database : string - Name of database - collection : string - Name of collection - dimension_separator : {'.', '/'}, optional - Separator placed between the dimensions of a chunk. - **kwargs - Keyword arguments passed through to the `pymongo.MongoClient` function. - - Notes - ----- - The maximum chunksize in MongoDB documents is 16 MB. - - """ - - _key = "key" - _value = "value" - - def __init__( - self, - database="mongodb_zarr", - collection="zarr_collection", - dimension_separator=None, - **kwargs, - ): - import pymongo - - self._database = database - self._collection = collection - self._dimension_separator = dimension_separator - self._kwargs = kwargs - - self.client = pymongo.MongoClient(**self._kwargs) - self.db = self.client.get_database(self._database) - self.collection = self.db.get_collection(self._collection) - - def __getitem__(self, key): - doc = self.collection.find_one({self._key: key}) - - if doc is None: - raise KeyError(key) - else: - return doc[self._value] - - def __setitem__(self, key, value): - value = ensure_bytes(value) - self.collection.replace_one( - {self._key: key}, {self._key: key, self._value: value}, upsert=True - ) - - def __delitem__(self, key): - result = self.collection.delete_many({self._key: key}) - if not result.deleted_count == 1: - raise KeyError(key) - - def __iter__(self): - for f in self.collection.find({}): - yield f[self._key] - - def __len__(self): - return self.collection.count_documents({}) - - def __getstate__(self): - return self._database, self._collection, self._kwargs - - def __setstate__(self, state): - database, collection, kwargs = state - self.__init__(database=database, collection=collection, **kwargs) - - def close(self): - """Cleanup client resources and disconnect from MongoDB.""" - self.client.close() - - def clear(self): - """Remove all items from store.""" - self.collection.delete_many({}) - - -class RedisStore(Store): - """Storage class using Redis. - - .. note:: This is an experimental feature. - - Requires the `redis `_ - package to be installed. - - Parameters - ---------- - prefix : string - Name of prefix for Redis keys - dimension_separator : {'.', '/'}, optional - Separator placed between the dimensions of a chunk. - **kwargs - Keyword arguments passed through to the `redis.Redis` function. - - """ - - def __init__(self, prefix="zarr", dimension_separator=None, **kwargs): - import redis - - self._prefix = prefix - self._kwargs = kwargs - self._dimension_separator = dimension_separator - - self.client = redis.Redis(**kwargs) - - def _key(self, key): - return "{prefix}:{key}".format(prefix=self._prefix, key=key) - - def __getitem__(self, key): - return self.client[self._key(key)] - - def __setitem__(self, key, value): - value = ensure_bytes(value) - self.client[self._key(key)] = value - - def __delitem__(self, key): - count = self.client.delete(self._key(key)) - if not count: - raise KeyError(key) - - def keylist(self): - offset = len(self._key("")) # length of prefix - return [key[offset:].decode("utf-8") for key in self.client.keys(self._key("*"))] - - def keys(self): - yield from self.keylist() - - def __iter__(self): - yield from self.keys() - - def __len__(self): - return len(self.keylist()) - - def __getstate__(self): - return self._prefix, self._kwargs - - def __setstate__(self, state): - prefix, kwargs = state - self.__init__(prefix=prefix, **kwargs) - - def clear(self): - for key in self.keys(): - del self[key] - - -class ConsolidatedMetadataStore(Store): - """A layer over other storage, where the metadata has been consolidated into - a single key. - - The purpose of this class, is to be able to get all of the metadata for - a given array in a single read operation from the underlying storage. - See :func:`zarr.v2.convenience.consolidate_metadata` for how to create this - single metadata key. - - This class loads from the one key, and stores the data in a dict, so that - accessing the keys no longer requires operations on the backend store. - - This class is read-only, and attempts to change the array metadata will - fail, but changing the data is possible. If the backend storage is changed - directly, then the metadata stored here could become obsolete, and - :func:`zarr.v2.convenience.consolidate_metadata` should be called again and the class - re-invoked. The use case is for write once, read many times. - - .. versionadded:: 2.3 - - .. note:: This is an experimental feature. - - Parameters - ---------- - store: Store - Containing the zarr array. - metadata_key: str - The target in the store where all of the metadata are stored. We - assume JSON encoding. - - See Also - -------- - zarr.v2.convenience.consolidate_metadata, zarr.v2.convenience.open_consolidated - - """ - - def __init__(self, store: StoreLike, metadata_key=".zmetadata"): - self.store = Store._ensure_store(store) - - # retrieve consolidated metadata - meta = json_loads(self.store[metadata_key]) - - # check format of consolidated metadata - consolidated_format = meta.get("zarr_consolidated_format", None) - if consolidated_format != 1: - raise MetadataError( - "unsupported zarr consolidated metadata format: %s" % consolidated_format - ) - - # decode metadata - self.meta_store: Store = KVStore(meta["metadata"]) - - def __getitem__(self, key): - return self.meta_store[key] - - def __contains__(self, item): - return item in self.meta_store - - def __iter__(self): - return iter(self.meta_store) - - def __len__(self): - return len(self.meta_store) - - def __delitem__(self, key): - raise ReadOnlyError - - def __setitem__(self, key, value): - raise ReadOnlyError - - def getsize(self, path): - return getsize(self.meta_store, path) - - def listdir(self, path): - return listdir(self.meta_store, path) diff --git a/src/zarr/v2/sync.py b/src/zarr/v2/sync.py deleted file mode 100644 index 49684a51ee..0000000000 --- a/src/zarr/v2/sync.py +++ /dev/null @@ -1,48 +0,0 @@ -import os -from collections import defaultdict -from threading import Lock - -import fasteners - - -class ThreadSynchronizer: - """Provides synchronization using thread locks.""" - - def __init__(self): - self.mutex = Lock() - self.locks = defaultdict(Lock) - - def __getitem__(self, item): - with self.mutex: - return self.locks[item] - - def __getstate__(self): - return True - - def __setstate__(self, *args): - # reinitialize from scratch - self.__init__() - - -class ProcessSynchronizer: - """Provides synchronization using file locks via the - `fasteners `_ - package. - - Parameters - ---------- - path : string - Path to a directory on a file system that is shared by all processes. - N.B., this should be a *different* path to where you store the array. - - """ - - def __init__(self, path): - self.path = path - - def __getitem__(self, item): - path = os.path.join(self.path, item) - lock = fasteners.InterProcessLock(path) - return lock - - # pickling and unpickling should be handled automatically diff --git a/src/zarr/v2/util.py b/src/zarr/v2/util.py deleted file mode 100644 index 7e3bd788ec..0000000000 --- a/src/zarr/v2/util.py +++ /dev/null @@ -1,788 +0,0 @@ -import inspect -import json -import math -import numbers -from textwrap import TextWrapper -import mmap -import time -from typing import ( - Any, - Callable, - Dict, - Iterator, - Mapping, - Optional, - Tuple, - TypeVar, - Union, - Iterable, - cast, -) - -import numpy as np -import numpy.typing as npt -from asciitree import BoxStyle, LeftAligned -from asciitree.traversal import Traversal -from numcodecs.compat import ( - ensure_text, - ensure_ndarray_like, - ensure_bytes, - ensure_contiguous_ndarray_like, -) -from numcodecs.ndarray_like import NDArrayLike -from numcodecs.registry import codec_registry -from numcodecs.blosc import cbuffer_sizes, cbuffer_metainfo - -KeyType = TypeVar("KeyType") -ValueType = TypeVar("ValueType") - - -def flatten(arg: Iterable[Any]) -> Iterable[Any]: - for element in arg: - if isinstance(element, Iterable) and not isinstance(element, (str, bytes)): - yield from flatten(element) - else: - yield element - - -# codecs to use for object dtype convenience API -object_codecs = { - str.__name__: "vlen-utf8", - bytes.__name__: "vlen-bytes", - "array": "vlen-array", -} - - -class NumberEncoder(json.JSONEncoder): - def default(self, o): - # See json.JSONEncoder.default docstring for explanation - # This is necessary to encode numpy dtype - if isinstance(o, numbers.Integral): - return int(o) - if isinstance(o, numbers.Real): - return float(o) - return json.JSONEncoder.default(self, o) - - -def json_dumps(o: Any) -> bytes: - """Write JSON in a consistent, human-readable way.""" - return json.dumps( - o, indent=4, sort_keys=True, ensure_ascii=True, separators=(",", ": "), cls=NumberEncoder - ).encode("ascii") - - -def json_loads(s: Union[bytes, str]) -> Dict[str, Any]: - """Read JSON in a consistent way.""" - return json.loads(ensure_text(s, "utf-8")) - - -def normalize_shape(shape: Union[int, Tuple[int, ...], None]) -> Tuple[int, ...]: - """Convenience function to normalize the `shape` argument.""" - - if shape is None: - raise TypeError("shape is None") - - # handle 1D convenience form - if isinstance(shape, numbers.Integral): - shape = (int(shape),) - - # normalize - shape = cast(Tuple[int, ...], shape) - shape = tuple(int(s) for s in shape) - return shape - - -# code to guess chunk shape, adapted from h5py - -CHUNK_BASE = 256 * 1024 # Multiplier by which chunks are adjusted -CHUNK_MIN = 128 * 1024 # Soft lower limit (128k) -CHUNK_MAX = 64 * 1024 * 1024 # Hard upper limit - - -def guess_chunks(shape: Tuple[int, ...], typesize: int) -> Tuple[int, ...]: - """ - Guess an appropriate chunk layout for an array, given its shape and - the size of each element in bytes. Will allocate chunks only as large - as MAX_SIZE. Chunks are generally close to some power-of-2 fraction of - each axis, slightly favoring bigger values for the last index. - Undocumented and subject to change without warning. - """ - - ndims = len(shape) - # require chunks to have non-zero length for all dimensions - chunks = np.maximum(np.array(shape, dtype="=f8"), 1) - - # Determine the optimal chunk size in bytes using a PyTables expression. - # This is kept as a float. - dset_size = np.prod(chunks) * typesize - target_size = CHUNK_BASE * (2 ** np.log10(dset_size / (1024.0 * 1024))) - - if target_size > CHUNK_MAX: - target_size = CHUNK_MAX - elif target_size < CHUNK_MIN: - target_size = CHUNK_MIN - - idx = 0 - while True: - # Repeatedly loop over the axes, dividing them by 2. Stop when: - # 1a. We're smaller than the target chunk size, OR - # 1b. We're within 50% of the target chunk size, AND - # 2. The chunk is smaller than the maximum chunk size - - chunk_bytes = np.prod(chunks) * typesize - - if ( - chunk_bytes < target_size or abs(chunk_bytes - target_size) / target_size < 0.5 - ) and chunk_bytes < CHUNK_MAX: - break - - if np.prod(chunks) == 1: - break # Element size larger than CHUNK_MAX - - chunks[idx % ndims] = math.ceil(chunks[idx % ndims] / 2.0) - idx += 1 - - return tuple(int(x) for x in chunks) - - -def normalize_chunks(chunks: Any, shape: Tuple[int, ...], typesize: int) -> Tuple[int, ...]: - """Convenience function to normalize the `chunks` argument for an array - with the given `shape`.""" - - # N.B., expect shape already normalized - - # handle auto-chunking - if chunks is None or chunks is True: - return guess_chunks(shape, typesize) - - # handle no chunking - if chunks is False: - return shape - - # handle 1D convenience form - if isinstance(chunks, numbers.Integral): - chunks = tuple(int(chunks) for _ in shape) - - # handle bad dimensionality - if len(chunks) > len(shape): - raise ValueError("too many dimensions in chunks") - - # handle underspecified chunks - if len(chunks) < len(shape): - # assume chunks across remaining dimensions - chunks += shape[len(chunks) :] - - # handle None or -1 in chunks - if -1 in chunks or None in chunks: - chunks = tuple(s if c == -1 or c is None else int(c) for s, c in zip(shape, chunks)) - - chunks = tuple(int(c) for c in chunks) - return chunks - - -def normalize_dtype(dtype: Union[str, npt.DTypeLike], object_codec) -> Tuple[np.dtype[Any], Any]: - # convenience API for object arrays - if inspect.isclass(dtype): - dtype = dtype.__name__ - if isinstance(dtype, str): - # allow ':' to delimit class from codec arguments - tokens = dtype.split(":") - key = tokens[0] - if key in object_codecs: - dtype = np.dtype(object) - if object_codec is None: - codec_id = object_codecs[key] - if len(tokens) > 1: - args = tokens[1].split(",") - else: - args = [] - try: - object_codec = codec_registry[codec_id](*args) - except KeyError: # pragma: no cover - raise ValueError( - "codec %r for object type %r is not " - "available; please provide an " - "object_codec manually" % (codec_id, key) - ) - return dtype, object_codec - - dtype = np.dtype(dtype) - - # don't allow generic datetime64 or timedelta64, require units to be specified - if dtype == np.dtype("M8") or dtype == np.dtype("m8"): - raise ValueError( - "datetime64 and timedelta64 dtypes with generic units " - 'are not supported, please specify units (e.g., "M8[ns]")' - ) - - return dtype, object_codec - - -# noinspection PyTypeChecker -def is_total_slice(item, shape: Tuple[int]) -> bool: - """Determine whether `item` specifies a complete slice of array with the - given `shape`. Used to optimize __setitem__ operations on the Chunk - class.""" - - # N.B., assume shape is normalized - - if item == Ellipsis: - return True - if item == slice(None): - return True - if isinstance(item, slice): - item = (item,) - if isinstance(item, tuple): - return all( - ( - isinstance(it, slice) - and ((it == slice(None)) or ((it.stop - it.start == sh) and (it.step in [1, None]))) - ) - for it, sh in zip(item, shape) - ) - else: - raise TypeError("expected slice or tuple of slices, found %r" % item) - - -def normalize_resize_args(old_shape, *args): - # normalize new shape argument - if len(args) == 1: - new_shape = args[0] - else: - new_shape = args - if isinstance(new_shape, int): - new_shape = (new_shape,) - else: - new_shape = tuple(new_shape) - if len(new_shape) != len(old_shape): - raise ValueError("new shape must have same number of dimensions") - - # handle None in new_shape - new_shape = tuple(s if n is None else int(n) for s, n in zip(old_shape, new_shape)) - - return new_shape - - -def human_readable_size(size) -> str: - if size < 2**10: - return "%s" % size - elif size < 2**20: - return "%.1fK" % (size / float(2**10)) - elif size < 2**30: - return "%.1fM" % (size / float(2**20)) - elif size < 2**40: - return "%.1fG" % (size / float(2**30)) - elif size < 2**50: - return "%.1fT" % (size / float(2**40)) - else: - return "%.1fP" % (size / float(2**50)) - - -def normalize_order(order: str) -> str: - order = str(order).upper() - if order not in ["C", "F"]: - raise ValueError("order must be either 'C' or 'F', found: %r" % order) - return order - - -def normalize_dimension_separator(sep: Optional[str]) -> Optional[str]: - if sep in (".", "/", None): - return sep - else: - raise ValueError("dimension_separator must be either '.' or '/', found: %r" % sep) - - -def normalize_fill_value(fill_value, dtype: np.dtype[Any]): - if fill_value is None or dtype.hasobject: - # no fill value - pass - elif not isinstance(fill_value, np.void) and fill_value == 0: - # this should be compatible across numpy versions for any array type, including - # structured arrays - fill_value = np.zeros((), dtype=dtype)[()] - - elif dtype.kind == "U": - # special case unicode because of encoding issues on Windows if passed through numpy - # https://github.com/alimanfoo/zarr/pull/172#issuecomment-343782713 - - if not isinstance(fill_value, str): - raise ValueError( - "fill_value {!r} is not valid for dtype {}; must be a unicode string".format( - fill_value, dtype - ) - ) - - else: - try: - if isinstance(fill_value, bytes) and dtype.kind == "V": - # special case for numpy 1.14 compatibility - fill_value = np.array(fill_value, dtype=dtype.str).view(dtype)[()] - else: - fill_value = np.array(fill_value, dtype=dtype)[()] - - except Exception as e: - # re-raise with our own error message to be helpful - raise ValueError( - "fill_value {!r} is not valid for dtype {}; nested exception: {}".format( - fill_value, dtype, e - ) - ) - - return fill_value - - -def normalize_storage_path(path: Union[str, bytes, None]) -> str: - # handle bytes - if isinstance(path, bytes): - path = str(path, "ascii") - - # ensure str - if path is not None and not isinstance(path, str): - path = str(path) - - if path: - # convert backslash to forward slash - path = path.replace("\\", "/") - - # ensure no leading slash - while len(path) > 0 and path[0] == "/": - path = path[1:] - - # ensure no trailing slash - while len(path) > 0 and path[-1] == "/": - path = path[:-1] - - # collapse any repeated slashes - previous_char = None - collapsed = "" - for char in path: - if char == "/" and previous_char == "/": - pass - else: - collapsed += char - previous_char = char - path = collapsed - - # don't allow path segments with just '.' or '..' - segments = path.split("/") - if any(s in {".", ".."} for s in segments): - raise ValueError("path containing '.' or '..' segment not allowed") - - else: - path = "" - - return path - - -def buffer_size(v) -> int: - return ensure_ndarray_like(v).nbytes - - -def info_text_report(items: Dict[Any, Any]) -> str: - keys = [k for k, v in items] - max_key_len = max(len(k) for k in keys) - report = "" - for k, v in items: - wrapper = TextWrapper( - width=80, - initial_indent=k.ljust(max_key_len) + " : ", - subsequent_indent=" " * max_key_len + " : ", - ) - text = wrapper.fill(str(v)) - report += text + "\n" - return report - - -def info_html_report(items) -> str: - report = '' - report += "" - for k, v in items: - report += ( - "" - '' - '' - "" % (k, v) - ) - report += "" - report += "
%s%s
" - return report - - -class InfoReporter: - def __init__(self, obj): - self.obj = obj - - def __repr__(self): - items = self.obj.info_items() - return info_text_report(items) - - def _repr_html_(self): - items = self.obj.info_items() - return info_html_report(items) - - -class TreeNode: - def __init__(self, obj, depth=0, level=None): - self.obj = obj - self.depth = depth - self.level = level - - def get_children(self): - if hasattr(self.obj, "values") and (self.level is None or self.depth < self.level): - depth = self.depth + 1 - return [TreeNode(o, depth=depth, level=self.level) for o in self.obj.values()] - return [] - - def get_text(self): - name = self.obj.name.split("/")[-1] or "/" - if hasattr(self.obj, "shape"): - name += " {} {}".format(self.obj.shape, self.obj.dtype) - return name - - def get_type(self): - return type(self.obj).__name__ - - -class TreeTraversal(Traversal): # type: ignore[misc] - def get_children(self, node): - return node.get_children() - - def get_root(self, tree): - return tree - - def get_text(self, node): - return node.get_text() - - -tree_group_icon = "folder" -tree_array_icon = "table" - - -def tree_get_icon(stype: str) -> str: - if stype == "Array": - return tree_array_icon - elif stype == "Group": - return tree_group_icon - else: - raise ValueError("Unknown type: %s" % stype) - - -def tree_widget_sublist(node, root=False, expand=False): - import ipytree - - result = ipytree.Node() - result.icon = tree_get_icon(node.get_type()) - if root or (expand is True) or (isinstance(expand, int) and node.depth < expand): - result.opened = True - else: - result.opened = False - result.name = node.get_text() - result.nodes = [tree_widget_sublist(c, expand=expand) for c in node.get_children()] - result.disabled = True - - return result - - -def tree_widget(group, expand, level): - try: - import ipytree - except ImportError as error: - raise ImportError( - "{}: Run `pip install zarr[jupyter]` or `conda install ipytree`" - "to get the required ipytree dependency for displaying the tree " - "widget. If using jupyterlab<3, you also need to run " - "`jupyter labextension install ipytree`".format(error) - ) - - result = ipytree.Tree() - root = TreeNode(group, level=level) - result.add_node(tree_widget_sublist(root, root=True, expand=expand)) - - return result - - -class TreeViewer: - def __init__(self, group, expand=False, level=None): - self.group = group - self.expand = expand - self.level = level - - self.text_kwargs = dict(horiz_len=2, label_space=1, indent=1) - - self.bytes_kwargs = dict( - UP_AND_RIGHT="+", HORIZONTAL="-", VERTICAL="|", VERTICAL_AND_RIGHT="+" - ) - - self.unicode_kwargs = dict( - UP_AND_RIGHT="\u2514", - HORIZONTAL="\u2500", - VERTICAL="\u2502", - VERTICAL_AND_RIGHT="\u251c", - ) - - def __bytes__(self): - drawer = LeftAligned( - traverse=TreeTraversal(), draw=BoxStyle(gfx=self.bytes_kwargs, **self.text_kwargs) - ) - root = TreeNode(self.group, level=self.level) - result = drawer(root) - - # Unicode characters slip in on Python 3. - # So we need to straighten that out first. - result = result.encode() - - return result - - def __unicode__(self): - drawer = LeftAligned( - traverse=TreeTraversal(), draw=BoxStyle(gfx=self.unicode_kwargs, **self.text_kwargs) - ) - root = TreeNode(self.group, level=self.level) - return drawer(root) - - def __repr__(self): - return self.__unicode__() - - def _repr_mimebundle_(self, **kwargs): - tree = tree_widget(self.group, expand=self.expand, level=self.level) - return tree._repr_mimebundle_(**kwargs) - - -def check_array_shape(param, array, shape): - if not hasattr(array, "shape"): - raise TypeError( - "parameter {!r}: expected an array-like object, got {!r}".format(param, type(array)) - ) - if array.shape != shape: - raise ValueError( - "parameter {!r}: expected array with shape {!r}, got {!r}".format( - param, shape, array.shape - ) - ) - - -def is_valid_python_name(name): - from keyword import iskeyword - - return name.isidentifier() and not iskeyword(name) - - -class NoLock: - """A lock that doesn't lock.""" - - def __enter__(self): - pass - - def __exit__(self, *args): - pass - - -nolock = NoLock() - - -class PartialReadBuffer: - def __init__(self, store_key, chunk_store): - self.chunk_store = chunk_store - # is it fsstore or an actual fsspec map object - assert hasattr(self.chunk_store, "map") - self.map = self.chunk_store.map - self.fs = self.chunk_store.fs - self.store_key = store_key - self.buff = None - self.nblocks = None - self.start_points = None - self.n_per_block = None - self.start_points_max = None - self.read_blocks = set() - - _key_path = self.map._key_to_str(store_key) - _key_path = _key_path.split("/") - _chunk_path = [self.chunk_store._normalize_key(_key_path[-1])] - _key_path = "/".join(_key_path[:-1] + _chunk_path) - self.key_path = _key_path - - def prepare_chunk(self): - assert self.buff is None - header = self.fs.read_block(self.key_path, 0, 16) - nbytes, self.cbytes, blocksize = cbuffer_sizes(header) - typesize, _shuffle, _memcpyd = cbuffer_metainfo(header) - self.buff = mmap.mmap(-1, self.cbytes) - self.buff[0:16] = header - self.nblocks = nbytes / blocksize - self.nblocks = ( - int(self.nblocks) if self.nblocks == int(self.nblocks) else int(self.nblocks + 1) - ) - if self.nblocks == 1: - self.buff = self.read_full() - return - start_points_buffer = self.fs.read_block(self.key_path, 16, int(self.nblocks * 4)) - self.start_points = np.frombuffer(start_points_buffer, count=self.nblocks, dtype=np.int32) - self.start_points_max = self.start_points.max() - self.buff[16 : (16 + (self.nblocks * 4))] = start_points_buffer - self.n_per_block = blocksize / typesize - - def read_part(self, start, nitems): - assert self.buff is not None - if self.nblocks == 1: - return - start_block = int(start / self.n_per_block) - wanted_decompressed = 0 - while wanted_decompressed < nitems: - if start_block not in self.read_blocks: - start_byte = self.start_points[start_block] - if start_byte == self.start_points_max: - stop_byte = self.cbytes - else: - stop_byte = self.start_points[self.start_points > start_byte].min() - length = stop_byte - start_byte - data_buff = self.fs.read_block(self.key_path, start_byte, length) - self.buff[start_byte:stop_byte] = data_buff - self.read_blocks.add(start_block) - if wanted_decompressed == 0: - wanted_decompressed += ((start_block + 1) * self.n_per_block) - start - else: - wanted_decompressed += self.n_per_block - start_block += 1 - - def read_full(self): - return self.chunk_store[self.store_key] - - -class UncompressedPartialReadBufferV3: - def __init__(self, store_key, chunk_store, itemsize): - assert chunk_store.supports_efficient_get_partial_values - self.chunk_store = chunk_store - self.store_key = store_key - self.itemsize = itemsize - - def prepare_chunk(self): - pass - - def read_part(self, start, nitems): - return self.chunk_store.get_partial_values( - [(self.store_key, (start * self.itemsize, nitems * self.itemsize))] - )[0] - - def read_full(self): - return self.chunk_store[self.store_key] - - -def retry_call( - callabl: Callable[..., Any], - args=None, - kwargs=None, - exceptions: Tuple[Any, ...] = (), - retries: int = 10, - wait: float = 0.1, -) -> Any: - """ - Make several attempts to invoke the callable. If one of the given exceptions - is raised, wait the given period of time and retry up to the given number of - retries. - """ - - if args is None: - args = () - if kwargs is None: - kwargs = {} - - for attempt in range(1, retries + 1): - try: - return callabl(*args, **kwargs) - except exceptions: - if attempt < retries: - time.sleep(wait) - else: - raise - - -def all_equal(value: Any, array: Any): - """ - Test if all the elements of an array are equivalent to a value. - If `value` is None, then this function does not do any comparison and - returns False. - """ - - if value is None: - return False - if not value: - # if `value` is falsey, then just 1 truthy value in `array` - # is sufficient to return False. We assume here that np.any is - # optimized to return on the first truthy value in `array`. - try: - return not np.any(array) - except (TypeError, ValueError): # pragma: no cover - pass - if np.issubdtype(array.dtype, np.object_): - # we have to flatten the result of np.equal to handle outputs like - # [np.array([True,True]), True, True] - return all(flatten(np.equal(value, array, dtype=array.dtype))) - else: - # Numpy errors if you call np.isnan on custom dtypes, so ensure - # we are working with floats before calling isnan - if np.issubdtype(array.dtype, np.floating) and np.isnan(value): - return np.all(np.isnan(array)) - else: - # using == raises warnings from numpy deprecated pattern, but - # using np.equal() raises type errors for structured dtypes... - return np.all(value == array) - - -def ensure_contiguous_ndarray_or_bytes(buf) -> Union[NDArrayLike, bytes]: - """Convenience function to coerce `buf` to ndarray-like array or bytes. - - First check if `buf` can be zero-copy converted to a contiguous array. - If not, `buf` will be copied to a newly allocated `bytes` object. - - Parameters - ---------- - buf : ndarray-like, array-like, or bytes-like - A numpy array like object such as numpy.ndarray, cupy.ndarray, or - any object exporting a buffer interface. - - Returns - ------- - arr : NDArrayLike or bytes - A ndarray-like or bytes object - """ - - try: - return ensure_contiguous_ndarray_like(buf) - except TypeError: - # An error is raised if `buf` couldn't be zero-copy converted - return ensure_bytes(buf) - - -class ConstantMap(Mapping[KeyType, ValueType]): - """A read-only map that maps all keys to the same constant value - - Useful if you want to call `getitems()` with the same context for all keys. - - Parameters - ---------- - keys - The keys of the map. Will be copied to a frozenset if it isn't already. - constant - The constant that all keys are mapping to. - """ - - def __init__(self, keys: Iterable[KeyType], constant: ValueType) -> None: - self._keys = keys if isinstance(keys, frozenset) else frozenset(keys) - self._constant = constant - - def __getitem__(self, key: KeyType) -> ValueType: - if key not in self._keys: - raise KeyError(repr(key)) - return self._constant - - def __iter__(self) -> Iterator[KeyType]: - return iter(self._keys) - - def __len__(self) -> int: - return len(self._keys) - - def __contains__(self, key: object) -> bool: - return key in self._keys - - def __repr__(self) -> str: - return repr(dict(self.items())) diff --git a/tests/v2/__init__.py b/tests/v2/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/v2/conftest.py b/tests/v2/conftest.py deleted file mode 100644 index 6680e4066b..0000000000 --- a/tests/v2/conftest.py +++ /dev/null @@ -1,7 +0,0 @@ -import pytest -import pathlib - - -@pytest.fixture(params=[str, pathlib.Path]) -def path_type(request): - return request.param diff --git a/tests/v2/fixture/.zgroup b/tests/v2/fixture/.zgroup deleted file mode 100644 index 3b7daf227c..0000000000 --- a/tests/v2/fixture/.zgroup +++ /dev/null @@ -1,3 +0,0 @@ -{ - "zarr_format": 2 -} \ No newline at end of file diff --git a/tests/v2/fixture/dimension_separator/flat/.zarray b/tests/v2/fixture/dimension_separator/flat/.zarray deleted file mode 100644 index f265bb0674..0000000000 --- a/tests/v2/fixture/dimension_separator/flat/.zarray +++ /dev/null @@ -1,23 +0,0 @@ -{ - "chunks": [ - 2, - 2 - ], - "compressor": { - "blocksize": 0, - "clevel": 5, - "cname": "lz4", - "id": "blosc", - "shuffle": 1 - }, - "dimension_separator": ".", - "dtype": "MC+6cQE@6%&_`l#-T_m6KOcR8m$^Ra4i{)Y8_`)zddH zG%_|ZH8Z!cw6eCbwX=6{baHlab#wRd^z!!c_45x13RUz zF>}`JIdkXDU$Ah|;w4L$Enl&6)#^2C*R9{Mant54TeofBv2)k%J$v`RUzF>}`JIdkXDU$Ah|;w4L$Enl&6)#^2C*R9{Mant54TeofB zv2)k%J$v`+{(>E|QGBzQN=`{lOV7y6%FfBn%P%M_DlRE4E3c@ms;;T6t8Zv*YHn$5Ywzgn>h9_7>z^=j z(&Q;qr%j(RbJpxRbLY)puyE1hB}NRWEt>3V5)8;K(w{73CbJy-Yd-v@> zaPZLKBS()NKXLNZ=`&}~oxgDL(&Z~xuU)@!^VaPo;%T iz5np>)8{W=zkUDl^Vjb`fB*ew81+9*;vWD^rVa$L$DH;6 diff --git a/tests/v2/fixture/test_format_compatibility/array_0/compressor_1/1 b/tests/v2/fixture/test_format_compatibility/array_0/compressor_1/1 deleted file mode 100644 index 4a92f77cede08276ecabe701f17af40e362bc6ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 285 zcmV+&0pk960gH%?ijIkmi%&>QN=`{lOV7y6%FfBn%P%M_DlRE4E3c@ms;;T6t8Zv* zYHn$5Ywzgn>h9_7>z^=j(&Q;qr%j(RbJpxRbLY)puyE1hB}NRWEt>3V5 z)8;K(w{73CbJy-Yd-v@>aPZLKBS()NKXLNZ=`&}~oxgDL(&Z~xuU)@!^VaPo;%Tz5np>)8{W=zkUDl^Vjb`fB*ewU}R!uVP#|I;N;@w;pO8O z5EK#?5fu}ckd%^^k(HBIP*hS@QB_md(A3h_(bdy8Ff=kYF*P%{u(Yzav9+^zaCCBZ jadmU|@bvQb@%8f$2n-4i2@MO67}@@3pcMcBHWU7;>t&vN diff --git a/tests/v2/fixture/test_format_compatibility/array_0/compressor_2/.zarray b/tests/v2/fixture/test_format_compatibility/array_0/compressor_2/.zarray deleted file mode 100644 index 2b3043fb76..0000000000 --- a/tests/v2/fixture/test_format_compatibility/array_0/compressor_2/.zarray +++ /dev/null @@ -1,17 +0,0 @@ -{ - "chunks": [ - 600 - ], - "compressor": { - "id": "bz2", - "level": 1 - }, - "dtype": "|i1", - "fill_value": 0, - "filters": null, - "order": "F", - "shape": [ - 1111 - ], - "zarr_format": 2 -} \ No newline at end of file diff --git a/tests/v2/fixture/test_format_compatibility/array_0/compressor_2/0 b/tests/v2/fixture/test_format_compatibility/array_0/compressor_2/0 deleted file mode 100644 index ae9adf56dfa9c9deb5a76e8319d282cc4115098e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 525 zcmZ>Y$}lu^j8qGb{NHNyh=GBz{y!nGfw7>0Yt^dE1_t~f{MD)+hm;GvMh1=x7dvxK z_Lbl;F=FOOF*zVC*x=ZpaKhxWvjAH|215qp0z)Y_HD|{OmmMxMFbO3v%wXWeXS)0o z^B9*#6)!#4GrPot-ozf+Xt0NK>65+Un|{Q5f2OyF|oNk=xW>DD^4c`dism91;L zv+itP$KCa0=eq7yZ+5TeKJ{hq`tDbM_HW?fW;wW_N1NyHMjmgGqZ@m&WsYy+=~g+p zsb{s$>CHT+P0nuad2Mrk3op0J#Vx(sK9{%hdWT%y+M6A7eH(9g%FS)Pt8;E|=RI9= zcYE*an)^HWxLY3X=+o|bypzv+%F~^F*>j%n;_F`Wa#!E#HLrK`o!;_xci-zh@AvR? zANjbaU;E7Gz5L!+zV7YMzVm$_fA^E0`}$YE`Msb2^q0T;`(OXjd(u^~Q1L%k>Jb2s CwC)Q4 diff --git a/tests/v2/fixture/test_format_compatibility/array_0/compressor_2/1 b/tests/v2/fixture/test_format_compatibility/array_0/compressor_2/1 deleted file mode 100644 index edec761d33967bfb0f15a5b8d6f533d614d1ba65..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 500 zcmZ>Y$}lu^j8qGb%wCyrk%1xVKS8j8F@ZsA)v^W#2Bwq)K*FGhf!Top2sj%+Bn?wcBYqH8qr=Y2-nMqO8RbMtG%~Z==lr>xJ<)Nav>Y0zK=BvMA z{#BpF)v|C!|HpqTRvGmz(bNi>woG$X(YzH}T9cNoQV6wLwK}Z%>ebfNsIaR?OXI?? zv9=~fTyve87J038X;##=qg(T$ud^O4in;E3wJi2}>(i>Z>qo!-*FXK6;lKtTX^ul1 z&R7c^+2|83actw6YK0SKh$# zZR^?Ugd5v@XJ_2nc6N8co$bE2EADMS`@7-64nOIRM?210Pk6G^FM7tao#(0-yx8S8 zd&R3==XP&+v)k|Xj(5Ay{XXzvkH7SZPy4^md|o9tedX(Fx$8UM*T{>X{9G#^e)D^s V{PdSBf4g@o&C&2My|N*r0RTvL=cE7t diff --git a/tests/v2/fixture/test_format_compatibility/array_0/compressor_3/.zarray b/tests/v2/fixture/test_format_compatibility/array_0/compressor_3/.zarray deleted file mode 100644 index a5c1d0b86a..0000000000 --- a/tests/v2/fixture/test_format_compatibility/array_0/compressor_3/.zarray +++ /dev/null @@ -1,20 +0,0 @@ -{ - "chunks": [ - 600 - ], - "compressor": { - "blocksize": 0, - "clevel": 1, - "cname": "zstd", - "id": "blosc", - "shuffle": 0 - }, - "dtype": "|i1", - "fill_value": 0, - "filters": null, - "order": "F", - "shape": [ - 1111 - ], - "zarr_format": 2 -} \ No newline at end of file diff --git a/tests/v2/fixture/test_format_compatibility/array_0/compressor_3/0 b/tests/v2/fixture/test_format_compatibility/array_0/compressor_3/0 deleted file mode 100644 index ffaee5493bac4c138c00e1841cae757d68b59ec0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 300 zcmV+{0n`2h0gwS$0ssJ50ssIk0RR9L0000K0RR9fwJ-f(SOHZC00a;K0RjUA1qKHQ z2?`4g4Gs?w5fT#=6&4p585$cL9UdPbAtECrB_<~*DJm;0EiNxGF)}kWH8wXmIXXK$ zJw87`K|(`BMMg(RNlHshO-@fxQBqS>RaRG6Sz23MU0z>cVPa!sWoBn+X=-b1ZEkOH zadLBXb#`}nd3t+%eSUv{fr5jCg@%WSiHeJijgF6yk&=^?m6n&7nVOrNot~edp`xRt zrKYE-sj922t*)=Iv9hzYwYImoxw^Z&y}rM|!NSAD#m2|T$;!*j&Cbuz(bCh@)z;V8 y+1lIO-QM5e;o{@u<>u$;>FVq3?e6dJ@$&QZ_4fDp`TG0({r>*}00313>Y4)cX@)TX diff --git a/tests/v2/fixture/test_format_compatibility/array_0/compressor_3/1 b/tests/v2/fixture/test_format_compatibility/array_0/compressor_3/1 deleted file mode 100644 index 6ddcd64e7282440c30988f063fe6a05d9ed9863b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 303 zcmV+~0nq*e0gwS$0ssJ50ssIn0RR9L0000N0RR9fwJ-f(SOIMa02B~dSz23MU0z>c zVPa!sWoBn+X=-b1ZEkOHadLBXb#`}nd3t+%eSUv{fr5jCg@%WSiHeJijgF6yk&=^? zm6n&7nVOrNot~edp`xRtrKYE-sj922t*)=Iv9hzYwYImoxw^Z&y}rM|!NSAD#m2|T z$;!*j&Cbuz(bCh@)z;V8+1lIO-QM5e;o{@u<>u$;>FVq3?e6dJ@$&QZ_4fDp`TG0( z{r>*|0RjUA1qKHQ2?`4g4Gs?w5fT#=6&4p585$cL9UdPbAtECrB_<~*DJm;0EiNxG zF)}kWH8wXmIXXK$Jw87`K|(`BMMg(RNlHshO-@fxQBqS>RaRF30sw_i0sw!2T2q*P BhXeos diff --git a/tests/v2/fixture/test_format_compatibility/array_0/compressor_4/.zarray b/tests/v2/fixture/test_format_compatibility/array_0/compressor_4/.zarray deleted file mode 100644 index 6d725e7208..0000000000 --- a/tests/v2/fixture/test_format_compatibility/array_0/compressor_4/.zarray +++ /dev/null @@ -1,20 +0,0 @@ -{ - "chunks": [ - 600 - ], - "compressor": { - "blocksize": 0, - "clevel": 1, - "cname": "zstd", - "id": "blosc", - "shuffle": 1 - }, - "dtype": "|i1", - "fill_value": 0, - "filters": null, - "order": "F", - "shape": [ - 1111 - ], - "zarr_format": 2 -} \ No newline at end of file diff --git a/tests/v2/fixture/test_format_compatibility/array_0/compressor_4/0 b/tests/v2/fixture/test_format_compatibility/array_0/compressor_4/0 deleted file mode 100644 index f54919df59f41ef78172b16bf1c0de116997ffb5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 300 zcmV+{0n`2h0g(Y%0ssJ50ssIk0RR9L0000K0RR9fwJ-f(SOHZC00a;K0RjUA1qKHQ z2?`4g4Gs?w5fT#=6&4p585$cL9UdPbAtECrB_<~*DJm;0EiNxGF)}kWH8wXmIXXK$ zJw87`K|(`BMMg(RNlHshO-@fxQBqS>RaRG6Sz23MU0z>cVPa!sWoBn+X=-b1ZEkOH zadLBXb#`}nd3t+%eSUv{fr5jCg@%WSiHeJijgF6yk&=^?m6n&7nVOrNot~edp`xRt zrKYE-sj922t*)=Iv9hzYwYImoxw^Z&y}rM|!NSAD#m2|T$;!*j&Cbuz(bCh@)z;V8 y+1lIO-QM5e;o{@u<>u$;>FVq3?e6dJ@$&QZ_4fDp`TG0({r>*}00313>Y4)dlZG+? diff --git a/tests/v2/fixture/test_format_compatibility/array_0/compressor_4/1 b/tests/v2/fixture/test_format_compatibility/array_0/compressor_4/1 deleted file mode 100644 index 9c4e54e9bbef65e016903a2bfb2ff44d87b25834..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 303 zcmV+~0nq*e0g(Y%0ssJ50ssIn0RR9L0000N0RR9fwJ-f(SOIMa02B~dSz23MU0z>c zVPa!sWoBn+X=-b1ZEkOHadLBXb#`}nd3t+%eSUv{fr5jCg@%WSiHeJijgF6yk&=^? zm6n&7nVOrNot~edp`xRtrKYE-sj922t*)=Iv9hzYwYImoxw^Z&y}rM|!NSAD#m2|T z$;!*j&Cbuz(bCh@)z;V8+1lIO-QM5e;o{@u<>u$;>FVq3?e6dJ@$&QZ_4fDp`TG0( z{r>*|0RjUA1qKHQ2?`4g4Gs?w5fT#=6&4p585$cL9UdPbAtECrB_<~*DJm;0EiNxG zF)}kWH8wXmIXXK$Jw87`K|(`BMMg(RNlHshO-@fxQBqS>RaRF30sw_i0sw!2T2q;- BhXnut diff --git a/tests/v2/fixture/test_format_compatibility/array_0/compressor_5/.zarray b/tests/v2/fixture/test_format_compatibility/array_0/compressor_5/.zarray deleted file mode 100644 index e218eb4f93..0000000000 --- a/tests/v2/fixture/test_format_compatibility/array_0/compressor_5/.zarray +++ /dev/null @@ -1,20 +0,0 @@ -{ - "chunks": [ - 600 - ], - "compressor": { - "blocksize": 0, - "clevel": 1, - "cname": "zstd", - "id": "blosc", - "shuffle": 2 - }, - "dtype": "|i1", - "fill_value": 0, - "filters": null, - "order": "F", - "shape": [ - 1111 - ], - "zarr_format": 2 -} \ No newline at end of file diff --git a/tests/v2/fixture/test_format_compatibility/array_0/compressor_5/0 b/tests/v2/fixture/test_format_compatibility/array_0/compressor_5/0 deleted file mode 100644 index b72d1fa45bbb4594c693161fef240f1e3419cc53..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 121 zcmZQ#oWdBv#J~W;l?)6FB0!u7#2Q=m|0YB*?qy<7VOq87%m;@53=IE)7({{rPyhrW zqT&n-4mcchNb++JM}E!Fs)jp#c;-l;X?t#|NjgO|LcJi zDqvucV`w<$ki^HMC+6cQE@6%&_`l#-T_m6KOcR8m$^ zRa4i{)Y8_`)zddHG%_|ZH8Z!cw6eCbwX=6{baHlab#wRd^z!!c_45x13RUzF>}`JIdkXDU$Ah|;w4L$Enl&6)#^2C*R9{Mant54TeofBv2)k% zJ$v`@beFH-yV-r&| ya|=r=Ya3fTdk04+XBSsDcMnf5Zy#Sj|A4@t;E>R;@Dc8RU_yWZP6mqrFbe=kC-QWF#jADM>|Y(vX&Pq$dLz$wX$dkd00k*T zVTw?cViczYB`HN|%21Yal&1m}sYGR}P?c&_rv^2tMQ!R(mwMEv0S#$HW17&EW;CY- zEont-+R&DEw5J0d=|pF`@DN>jm`8Y&Zgi&ykMTH9(32<=Q&33JR^C5QM|}##xRy~jAsH9d5M>q#AK#0m1#_81~YkuSDD3ZUgLG< zFqe7EX8{X&gEv{kVwSL!w|JXnEN2BPS;cDBu$FbK=N;Z<0~>jd_xXTLY-S4|@)29v z#>Z^u6Fy}JJK4o!$G3dP zc`oohKky?Lxx{6z@Do4t3s?D-Yh33Bzj2dW+~#-gaF=`h!Jquaeg5Vj9`G-yVFDAH z$iyZwsmV-k3R9ZO)TS}5=}d11Gn&cFW-+VT%x(^In#jpRap6~mCo80UcKlCHFy3LQ>?k9fg z4tKiC-G1ih?s2bQxX=9_@JkPR$ip7-s9*WD$2{%{PkPF4Jnb3Jdd_eC&huXIdw=jp zFM7$#UhyY?_7|`EtJl2l4S(~dx4iA|-tn&Y{KG%}%lrQAKR)o^|MMFW2_j)6io}s5 zl18#f9w{Paq>9v$CelW_NFNy@V`PfVktMQ5w#XhiB4^}^+>s~pM!v`&1)^XSio#JO zibk<09wnk=l#0?(Cdx*+C?6G~VpNLCQ6;KIwWuC7qGr^J+EFL!M!l#X4WeN*ipJ3- fnntr|9xb9}w2IczCfY{3XdfM-V|0qn(Ix%^h3XHq diff --git a/tests/v2/fixture/test_format_compatibility/array_1/compressor_0/1 b/tests/v2/fixture/test_format_compatibility/array_1/compressor_0/1 deleted file mode 100644 index 7dd7d7404e17b12ad66cc591f7b729c816a18581..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1200 zcmdVH1A-U;00Y5mHnnZrwr$(CZQHhO+qP}nHs8^iYO1aps;OG4tvaf!daADmYN$qP ztR`xzW@@e$YN=Lgtu|__c51H<>ZnfYtS;)RZtAWc>ZxAptv>3je(J9Q8mK`UtRWhz zVH&Ox8mUnltuY#_aT>1)ny5*dtSOqRX_~GXnyFcutvQ;jd77^UTBt=@tR-5iWm>Kk zTB%i9tu+%AI;m4S ztus2Sb2_gJx~NOKtSh>zYr3u*x~W^btvkA_d%CX&dZtv7nB zcY3c6`lwI(tS|biZ~Cqu`l(;~tv~v!00JSv1SSYU2}W>25Ry=YCJbQ-M|dI-kw`=) z3Q>thbYc*bSi~j{afwHK5|EHYBqj+-Nk(!~kdjoSCJkvxM|v`lkxXPJ3t7oVc5;xD zT;wJXdC5n93Q&+j6s8D8DMoQhP?A!VrVM2%M|mnxkxEpi3RS5_b!t$PTGXZvb*V>v z8qknNG^PnnX-0Ee(2`cPrVVXrM|(QZkxq1`3tj0(cY4s1Ui799ed$Mk1~8C83}y&J z8OCr%Fp^P>W(;E)$9N_%kx5Ku3R9WJbY?J-EM^HyS;lf!u##1* zW({ju$9gufkxgu73tQR7c6P9nUF>ELd)dc+4seh|9Oei|ImU5LaFSD;<_u>!$9XPr zkxN|W3Rk(tb#8EzTioUjce%%X9`KMyJmv{cdB$^I@RC=&<_&Lo$9q2TkxzW)3t#!h zcYg4bU;O3|e+h6P%h(R=AP(wa4(<>R=}->sFb?Z*4(|w#=tz$2D30oAj_w$a=~#~K zIF9Rhj_(9c=tNHJBu?sNPVN*==~PbbG*0VuPVWrP=uFP+EY9j|&h8w}>0Hk3JkINU p&hG*)=t3^+A};D;F76U8=~6E3GA`?KF7FDi=t{2aDh~MX`~yF<1Z4mK diff --git a/tests/v2/fixture/test_format_compatibility/array_1/compressor_1/.zarray b/tests/v2/fixture/test_format_compatibility/array_1/compressor_1/.zarray deleted file mode 100644 index e4e48baf94..0000000000 --- a/tests/v2/fixture/test_format_compatibility/array_1/compressor_1/.zarray +++ /dev/null @@ -1,17 +0,0 @@ -{ - "chunks": [ - 600 - ], - "compressor": { - "id": "zlib", - "level": 1 - }, - "dtype": "+=(3WiI9o}UF8+niS`G8Gq zW(yzk5nI{D$86^lK4k|x*~M-?<8$_~moM1Ieh%;@2RX!Hj&PK(_?lxJ=L9D?#W$Sh z3}-pVw|vKWF7Q1+@FN$w#AUAV6F>6{SNWA|T;~SAag$rz=6CLJmwWudpZvvr{^lPZ z@Gq!g0u!3Z#3nJR$xLnvQ<}=urZKJQOm7A=n#s&&F{|0kZVq#r%iQKMuldYx0Sj8l z!WOZp#Vl?KOIpg(ma(kmEN=xXTFJ^*v8vUqZVhW%%i7kluJx>M0~^}N#x}93&1`N9 zTiVLjwy~}4Y;OlU+R4s#@gckVu#fnt-Ry1;AMoNh1~>Yi@B4w9-0T)V^dq;r z&5zygCw}SQ77s~y{I1z qqG2?O#?d62Mzd%hEuv+#iq_F4+D5x*A047&bc)W=CH@12>JPL*h}41r diff --git a/tests/v2/fixture/test_format_compatibility/array_1/compressor_1/1 b/tests/v2/fixture/test_format_compatibility/array_1/compressor_1/1 deleted file mode 100644 index 193648d77f656fd559439ae77084dc4b3c9226c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 770 zcmYkqSxnOa002-F)SVsT1r-$)t9aCTq11zLwTz>T+m%bMDn)eMtQ;oB9}=^4n?Edm znx%L7P!ktFBr#ivX2C=gnI`_2W$P|J{LsY@e|>p#+rF%2KhDp}%3d2SeKq#&^?yr$ zd|IJ+8RR*Fmk?f{d1WWXqLc_xJW0v>DXxQ(N-1H6QUI03RS{PC2daEn<$6`=mMSc% z$~KU#0L7yq-w(>WL9Q8;E`Y)~sA#>c#VeM0`4O*N;N@&ysmv=lyvh!Ots%q{1V2p3 z`v|U`kggEIG@%&7Y-3nF7v{&p@}V%-6P9j>#?$>1GC9_e_s|b`G7UtRCXC518h#MR0>bFZ;BOBj_Zt;qG|pGmed5^T>t^pYrK@ zV(2Wr;pzS5GcCrjM?1|-g@M#m6$I?4+1>npt>7K7c0Xqy~?$=)q@=i7+!p(8kd zYs=NN5YhMj3Cu2SdHViFOpmnoq7PqzL=m9g0aCd@%nBsUK->Y$}lu^j8qGbd{UyW&%nS?|DO=Jz-*y#j-T;K15hyK zkWtYwv2o$SM@&jiNll9mJ!V#RPHtX&_z8=OOMR15P*PFTq(z$!U3&DHSyrhTff0z zqv0l_&Gj>HNkz@NEge1kj!ev)yRxx!@5#l@yDuL<|A9inf`^KU3m+*ZEqbh+y!eSq z%95w5sY{=!r7e4|p1%BrM#hSlnwcwKX=SZ?t)0F4jZV&*x4OA&-|6M8d#|6r{)0im zhL47Y8$THpZTf6ny!nes$(FCCrCYz5m2LZOUcUW@Ma7PvmX$kySyk=&ZC$Y$}lu^j8qGbeC;Fuoq;j3{y!mbfN6)~90m`T18xjJz-S=s1ELvN4lo%SfLRcN zqq9S&0TcYR#MtEQuy3QVvQPWgO@=VCeBR zNtnTuk-*>TufON7{eKn{8yBCDn3SB7nwI|g7d^dwUylD;Vq14o zQq$5iK4;6GBWJGMdGhA_uKN2Qeq0E+7U%mhOThrhFSoY`gm5{59;gQiXLVI*u zdnaUDdj~vXxgC9nOT5VFMaIML<<0h; zzd=%_6wLt50M7tM5|WQ}WFr~5NJSiAOYI5s5fNAqEkMKlEV_dALIz<`9QBv|$ZtI71o65QZ;wVGCKfLKUVE zg(oy&2}w9Y5rz?x9AXfE*ux#>@P;*48Bch=)1B>P=Q`DyPIR8roaH3vIK>%G zaDLO9-Q?yrwV6$9UelV@q~rZGl%*u)C`B1cP=3;ro#f;uHJM3FUec14 zq~s(e1^1fQyViBCaeZrB*P7O|mUXOQ{c2aYn$@dTb*fQ)YEze*)T0)4s6qW{Pj{Nr zo7QxuF@0%ESDMn3mUN^c{b)xwn$e3^bfOV`XhRp8(1R9qpaK17Klho>d)9NF@qA}H z*O|_9mUEop{AM?|nayifbDGh7W;2(W%wraFn8EyIFL#;CTh?-xv3zAKSDDIFmU5J# z{A4FLnaN95a*~mJWFr@u$U_!#kb(STANQEYJJxZIaeQMN*O{9+fkn8hnr zaf(p{epCFU_(k!9;`hYQiC+^xCVor&l=vm_L*jSD&xl_UKO%lZ{Dk-g@dM)b!_S9b z4?iA$JN$I`&~@B`rY=jZ3QpYL1W`NsFX?Oktr&s*N{hWESO z-EMZTTixkK_qokoZgP)X+~EfIx4qqMZf{%L*~a#@tzB(uPg~m2hW4|a-E3wrTiMA* T_OXp!Y+?^v*ue(&uYCmow^qet diff --git a/tests/v2/fixture/test_format_compatibility/array_1/compressor_3/1 b/tests/v2/fixture/test_format_compatibility/array_1/compressor_3/1 deleted file mode 100644 index ab304f14c3fa18fbcd8d7e80ff385a89a62f882e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 798 zcmV+(1L6Dv0gwW)1ONcA1ONaY0{{RN000060{{RhwJ-f(umj~60QSGU4iNBc8~^|S z0LpEd;{6tUeL{8j9P0U10yhKZ^ zL`s}QNsL5Dd_+fVL`GahMNC9QJVZk*L_!=yK@3Dd{6jzNLq6O?JDFH_r z%1?H3lbO6^B_|olM>cYii9BQ>2N}pe_HmDSyki~b7{@oZagAv_V;RR7#xHhpi&?y4 z6{i@*CpK}3Njzc^hZw{k_Hc(eykQMz7{eE~aD^#6VF^bV!Vh+EgBiSF1t%E62R3kl z2|Qo{2N=MA_ve21F(2{q9__In>2V(AF&^RZ9o?}V*>N4!F&)wI9L=#D$#ERTF&x41 z8@;g`xp5n{F&nY*8m+M!sc{;m1xFj&&vtgRnZ0afCmY$vHg>UzJ#1kI8`!`0b+387 zYhC9W*SEHHt!X`LS;rdIuXc5-S-omiryA9#Hg%~HglQDJZ3S68O&eya+kThWi4kJ%U8B?m8m>sDFJDRnZhh z(GxY%5+%_Q710m{(GT^|4&~4d)zA#Z&8qmCy)<&_$(7(2q^=sex#`nGLU2l5NTi)@8_q*NQZg#I*-RVa6xy@Z}a*tcw;Rg4& cz1?kYZ(G~h#`d+XU2SSlTUr4C|6LSF0|e7mtpET3 diff --git a/tests/v2/fixture/test_format_compatibility/array_1/compressor_4/.zarray b/tests/v2/fixture/test_format_compatibility/array_1/compressor_4/.zarray deleted file mode 100644 index 86c7d4c156..0000000000 --- a/tests/v2/fixture/test_format_compatibility/array_1/compressor_4/.zarray +++ /dev/null @@ -1,20 +0,0 @@ -{ - "chunks": [ - 600 - ], - "compressor": { - "blocksize": 0, - "clevel": 1, - "cname": "zstd", - "id": "blosc", - "shuffle": 1 - }, - "dtype": "RaRG6Sz23MU0z>cVPa!sWoBn+X=-b1ZEkOH zadLBXb#`}nd3t+%eSUv{fr5jCg@%WSiHeJijgF6yk&=^?m6n&7nVOrNot~edp`xRt zrKYE-sj922t*)=Iv9hzYwYImoxw^Z&y}rM|!NSAD#m2|T$;!*j&Cbuz(bCh@)z;V8 z+1lIO-QM5e;o{@u<>u$;>FVq3?e6dJ@$&QZ_4fDp`TG0({r>*|0RjX71Sa7F>i9Ut J0#psCYYMVGi*x`0 diff --git a/tests/v2/fixture/test_format_compatibility/array_1/compressor_4/1 b/tests/v2/fixture/test_format_compatibility/array_1/compressor_4/1 deleted file mode 100644 index 00dd93a5e2aa0538de48f2da6195292736571133..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 318 zcmV-E0m1$P0g(c*1ONcA1ONa&0RR9L0000c0RR9fwJ-f(umj}?08|iISz23MU0z>c zVPa!sWoBn+X=-b1ZEkOHadLBXb#`}nd3t+%eSUv{fr5jCg@%WSiHeJijgF6yk&=^? zm6n&7nVOrNot~edp`xRtrKYE-sj922t*)=Iv9hzYwYImoxw^Z&y}rM|!NSAD#m2|T z$;!*j&Cbuz(bCh@)z;V8+1lIO-QM5e;o{@u<>u$;>FVq3?e6dJ@$&QZ_4fDp`TG0( z{r>*|0RjUA1qKHQ2?`4g4Gs?w5fT#=6&4p585$cL9UdPbAtECrB_<~*DJm;0EiNxG zF)}kWH8wXmIXXK$Jw87`K|(`BMMg(RNlHshO-@fxQBqS>RaRF30s{m91^@*n8896m QP>cj9DSWyB_yg3M049#FnlOr`2U}Q;eS1l zLIprUki>s=g#u>H6@2*)t2@R`7R%I1M$=(v_ z!~F}+b;5*Lml*!H~-sYydx(vhAFWF!-r$wF4Lk)0gmBp12K zLtgTcp8_OMkU|uu2t_GIaY|5W(;F_il-UJcqZ@+&oYroOlAtt@jNf^B2$^h zbY}1pFEf)_yuz!@W)5?i$7{UKd={{fMZCeAEM^HyS;lf!@D^|L4)3y(Rjg(WYgxy7 z-s62fU;`WZkdOG7PxzG2*u-W&XA4`|#ut3aSA5NOcCeFO?B*N3=(^}THj&-ePeH+-&MmDyI zO>Jg#TiDW8wziFJZD)Hs*wIdQwu@c8&u(`2ejl)hJ$=xJeAr(0wvUhasE_%$eeGv| zpKyQ!9pqq#IMiVd_en=M(ov3fjAMPuryb{bC-{udI?+i^c8bsWyf65oQ=R5?XZVsY zJJVUd;;YVfj&q&oYrgJ$7r4+xzTulLc8N<}=5kl~mT&ux@4C`eu6B)UUFUk=^L;;X zgB$(OkNntA{M66fQUiOMtz2MyV(rWuk19i}Fz+Dn_NK995!fREz3SBWgyis2z2p tZq$qV(I6T|qi7sWqG>dX=FuWrMyqHYZK7?oi}ukWI!34H99^Po{0DyM5(fYP diff --git a/tests/v2/fixture/test_format_compatibility/array_1/compressor_6/1 b/tests/v2/fixture/test_format_compatibility/array_1/compressor_6/1 deleted file mode 100644 index 198d631f844ddcef1218f450c2aa08b36bb5282f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1071 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 Sm$#4?wJ)o1Bq>oDk}@Pi977XPA<9@ZAt?># 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 nPg;$WU diff --git a/tests/v2/fixture/test_format_compatibility/array_10/compressor_1/.zarray b/tests/v2/fixture/test_format_compatibility/array_10/compressor_1/.zarray deleted file mode 100644 index 6c4c6a48e5..0000000000 --- a/tests/v2/fixture/test_format_compatibility/array_10/compressor_1/.zarray +++ /dev/null @@ -1,17 +0,0 @@ -{ - "chunks": [ - 2000 - ], - "compressor": { - "id": "zlib", - "level": 1 - }, - "dtype": "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 diff --git a/tests/v2/fixture/test_format_compatibility/array_10/compressor_1/1 b/tests/v2/fixture/test_format_compatibility/array_10/compressor_1/1 deleted file mode 100644 index ee596083fb83fdab88d69f8caf6dc46b917e1af2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6176 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 diff --git a/tests/v2/fixture/test_format_compatibility/array_10/compressor_2/.zarray b/tests/v2/fixture/test_format_compatibility/array_10/compressor_2/.zarray deleted file mode 100644 index 93d7fdf873..0000000000 --- a/tests/v2/fixture/test_format_compatibility/array_10/compressor_2/.zarray +++ /dev/null @@ -1,17 +0,0 @@ -{ - "chunks": [ - 2000 - ], - "compressor": { - "id": "bz2", - "level": 1 - }, - "dtype": "TL8NO01|6QG@70wAB)< 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 fXpXzs;g z2u1pLoB6^@Hu$*uq-#xI7!ul*Hp%sYLWVqlrFHu_{dSD()|ECSkSnimDy$j?;#c~j zVLg?BkL$tdm@A>h1#E6z3m;ASki`<5RF9JJJ)3%GVff<9W`Gd21mAmEkm^-(7T2@?+pgP z3&R4Jv_?z?N^Nc;RXPVeJgbJ%Yz(>6Uav@+fHRU#!F($UIcxlpC&)X=s3WaxBIN@9 zbjVG}c|-N~);XxK3gV#3FH(4KTxY~{Cn4i(GF{PbM#JW;E9ho6es5p9ZSs&f5iLDW zVAqfajmOYI)IrParBSroVX*3l*4e9tjYSF#&gK(T>Llx&p+>A*&F~H}vO*!Sq+61y zIAPpm)YnrPkVT!FCj_9_a@}f7+5tnkn$}n%uB-q%9;h6$PjfB=n$1S3VTcsJBs$d{ zmRB{@*OgcK8lQI^#Te-9PNg1q#%n>^#fBNfqx53jA%bqJf+s70hGa)~|S}NH zmOwNf^SegvG^z!u?U{{EZZX7Sq}F7cA{vnrw)*n+=JJP<7kr#bX>8_V*t>QE?R{;L z9^Il56+(DwQkZliz;|!NY(8j6xi%1T&t(eDYuZG9*QNYY*!$(lNiJn>Lh*sdez~8c zYvYSYxW|5NU+aFMzUc18T+NVd{>Tb6-M0*@4?KPQ5;pMFt=ktbi=1o5-P|9n*Q&^v zDl2RBDC~bofaP`DIXWhM`EpD%75_(Ti?pn!d_o${n<}cDbyzLCaNcB8u%_kuOPTJB zdDE6z*D@LA%4^tXEyQGJ4(1S`xz-f(^b>UVLcb=?m+()0wwFrk*tySY)!%g7*chgq zH#VO1xPR)}9EE}@wu`pPM(v&)?{Wzt_mu1kyab4^vf}g8SXXh;y(bL}D>Zcfie;%N z*+>#cRemaaTXu>2P3vCb{_nI;_umaX_)BuNVnCEUXlimXlB|G(i1if|GCP;M#@E8-^Lbi&v#-$^6bq1FR$0DNk%P^TtOX9}UCU~o* zkB=IK_Jqs1*_d8MeG0<$>u#Mp%DB2h596bTva{bP;bXW%LmRpu3p;A~Qwz_opGBeU zUn4K}S|8*#6X{_Onb&OM*w%J`c*PeFq&4?lKXle2%3U^##|+H@hJpfWigns2`_wNK zEYvpAE5j|YMAY{4>;(3(b~*4A#MReJ%lz>1-R5jY$=hAkG3O6XpFebD^?cCX5pl=v zF?Px8$gf%LG2L?NS|0noR!;-q6Td(DbR5#pm{QvM`At8%JWdbLZw924oOTMPoAT35 zgzpBV&5pZcqFN$wIGm4aUR3Y#dpdsoON(h~>AcGhvd%&2Uq7wAyZ*6DDP?*7tFGMU z2^rhnYyL2tv;811TgAi6#Lc|vsX~aQMm~Mpy~>l1yIgcsd$O$rN5kdQUV&Bv8Z(PG z|IGWlu^Z=NvJ=h9+>;W`N=xG|$aR-~8GN6h^Ql%tb07QVzUvF}_MiRlzhg^2b)D%y zJfcc~@f0|sNM<%BcQ1$@TkRyG(_Nh9&p}I%j*kyLvka%#g;{1dMEuY|=PlnTP zxFnY6=BA~kmDb^$4T7sYNJCS=Sro?1UoqLLg^nG0^U*P$+QncM&B4^H&XL_V+;n3N=z6xcV1}K z>{zLLqScE%6v4(Vu=oOH_=)6oJtB{mp%^5>J9uZRuMlSx7Rm9Iq}M>bn=dsVti*xz zQXsYCJpQ1CENz`SYL+Pb>->^Zr)C-(^on3sk9813zlheEl!>~ViJ^C@Ag9-pYmKxd gA7}}ch;iQi70=6R4tlB&K6mmE50g_6MVQ8a0a+e5D*ylh diff --git a/tests/v2/fixture/test_format_compatibility/array_10/compressor_3/.zarray b/tests/v2/fixture/test_format_compatibility/array_10/compressor_3/.zarray deleted file mode 100644 index de26b1c01a..0000000000 --- a/tests/v2/fixture/test_format_compatibility/array_10/compressor_3/.zarray +++ /dev/null @@ -1,20 +0,0 @@ -{ - "chunks": [ - 2000 - ], - "compressor": { - "blocksize": 0, - "clevel": 1, - "cname": "zstd", - "id": "blosc", - "shuffle": 0 - }, - "dtype": "2@e>!n85+yz(r36eCfs3{ZT3x13rZw|935Hxt{ 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 YL%c=WY#aE%Q|ZL%adL-sTvZ|Xe+uRt)Bpeg diff --git a/tests/v2/fixture/test_format_compatibility/array_10/compressor_4/.zarray b/tests/v2/fixture/test_format_compatibility/array_10/compressor_4/.zarray deleted file mode 100644 index e7a01eff8e..0000000000 --- a/tests/v2/fixture/test_format_compatibility/array_10/compressor_4/.zarray +++ /dev/null @@ -1,20 +0,0 @@ -{ - "chunks": [ - 2000 - ], - "compressor": { - "blocksize": 0, - "clevel": 1, - "cname": "zstd", - "id": "blosc", - "shuffle": 1 - }, - "dtype": "248_F6jml-unyU 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 diff --git a/tests/v2/fixture/test_format_compatibility/array_10/compressor_5/.zarray b/tests/v2/fixture/test_format_compatibility/array_10/compressor_5/.zarray deleted file mode 100644 index ea781710a4..0000000000 --- a/tests/v2/fixture/test_format_compatibility/array_10/compressor_5/.zarray +++ /dev/null @@ -1,20 +0,0 @@ -{ - "chunks": [ - 2000 - ], - "compressor": { - "blocksize": 0, - "clevel": 1, - "cname": "zstd", - "id": "blosc", - "shuffle": 2 - }, - "dtype": "e@ocWQ7xAFD)v%4jsZ0FHWt>9GJqmo1cR6 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 GJ1%?BX31y( diff --git a/tests/v2/fixture/test_format_compatibility/array_10/compressor_6/.zarray b/tests/v2/fixture/test_format_compatibility/array_10/compressor_6/.zarray deleted file mode 100644 index a9fe1db2eb..0000000000 --- a/tests/v2/fixture/test_format_compatibility/array_10/compressor_6/.zarray +++ /dev/null @@ -1,20 +0,0 @@ -{ - "chunks": [ - 2000 - ], - "compressor": { - "blocksize": 0, - "clevel": 1, - "cname": "lz4", - "id": "blosc", - "shuffle": 0 - }, - "dtype": "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 diff --git a/tests/v2/fixture/test_format_compatibility/array_10/compressor_6/1 b/tests/v2/fixture/test_format_compatibility/array_10/compressor_6/1 deleted file mode 100644 index 4f8a05c565e50ed765ce68acae09b093bf2eaa49..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10775 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 diff --git a/tests/v2/fixture/test_format_compatibility/array_11/.zgroup b/tests/v2/fixture/test_format_compatibility/array_11/.zgroup deleted file mode 100644 index 3b7daf227c..0000000000 --- a/tests/v2/fixture/test_format_compatibility/array_11/.zgroup +++ /dev/null @@ -1,3 +0,0 @@ -{ - "zarr_format": 2 -} \ No newline at end of file diff --git a/tests/v2/fixture/test_format_compatibility/array_11/compressor_0/.zarray b/tests/v2/fixture/test_format_compatibility/array_11/compressor_0/.zarray deleted file mode 100644 index 4c38043a12..0000000000 --- a/tests/v2/fixture/test_format_compatibility/array_11/compressor_0/.zarray +++ /dev/null @@ -1,14 +0,0 @@ -{ - "chunks": [ - 3000 - ], - "compressor": null, - "dtype": "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 TE-%sAbC0+h{(ngZ^)2;(8do}Y diff --git a/tests/v2/fixture/test_format_compatibility/array_11/compressor_0/1 b/tests/v2/fixture/test_format_compatibility/array_11/compressor_0/1 deleted file mode 100644 index b03ff4972b80891e1780513845f05f1f070172cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6000 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 diff --git a/tests/v2/fixture/test_format_compatibility/array_11/compressor_1/1 b/tests/v2/fixture/test_format_compatibility/array_11/compressor_1/1 deleted file mode 100644 index dba38807a798cb7a57099233c92477607cf151d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2758 zcmV;%3OV(70qxf5cTMLW2XI`PM5CG*Lo^~mBFV`a_CEW0_MA{5C~`}q(kn&Gl-xE- zVoZw~RudQ3oR~snIOimL-p_u{2~k7LV(y|<#C(+&#pQZ^|Au^DeP4gpZ~fNyeLdL! z3#zOhVx$>P;W`~6WkX%*oE%1Zlzjabdk-uPWa^^vLc2}hkNwf=rHleWwQ>3|avndP zBDfN5Q0ef!81;dJz=m|DdOpsPUehT`!1c-ncJ9_chGX!go~yVx^{jppGLGl)QQYBx#>R>< zT*rfe`c@r{t|r%e!vUXAfVOE(`Zc`3s;jBt25apN1D)xsu`R`7BHunndrt|0ZDogV zL7AZ)uWTEb2qba3_KG}BM{$fq`O}Dx>?`Df-c9yn}{kFj1iWE_~?%1ULM@utKfj!na@*jb0qJfUEX2Es+a`=nJ z&Xgm8fpP<$EZC)mzOK}RtpP2_n&|k@T?F?EV*;Zp>)98csz5(0m8>GZgrX5g6r1tH z>3+oq%b&w5yx15m90N{$zLYDhHQW355pBp%;dt0dxQPIjwB`!=xJ2Gm!Ey8b zW$q}m4NNi9dD46s7-86?PuURb59DDMTZXTr@0qiOG0n^(6U;f*UG@|eE)1kQSP8Zd z(H0!1USTE?MV6+n^&Y@7%&0B0j#-o0uF63+o$ta9(B1}Gk;&qUeb4-p^k01wQFLAZ z>NKN^FxOT{m3Ug%w&Hj+mX(~@%3&%LJQsjNL{mIJgXc^hZco_E6wc zDPI$+k<}{z=Adi%32Ib^+UMR=%nMXG+su7E~ZLf0{QQg^XRerrqSR)GA# zp&qyBY^x>ByAk_?{(^~gd?>6<>C1bm-C`r?Zw8fjrMJ?HQ8vnx)4(ve%wAlX6T3ll@HZz^MdTSfK!3dS2;|2(= zQ8n#F&(Pq$MZNde?PAjcmEPgpbn%pITK%{O^f~pRX6c1;dvduJqDI3vYGXq}^ATv9ND>u|L^A z3U|^%lqRbS`+=2jl_fPWkzzT>;4h;LG1nig6@VFdx|xZdOI~hx>{Rto-M3T*eF6`z zOmb?>alNJai5kNtv2*uyWUas`dOivhQqfssF&GGA)!)g7RJwbf@;)f#nviC^>_a2U zKaTw-Fk1+})%xRuDIl471u$C;75{D>e?~51M!RqfCxDMt5nW{Ru>{ z=_mSGWQ!=lc!{&k>1ZIFKyP<+uzCySN+TH2ugbX^6zh;1iTk1iP6M4%vJ+46 zv;2Fle#|pGvZjJ<1*)U%>OALmkgh)^N3c<~S&5jM3WAvSh|#XAjOp|*J#d7j@BscE zT&O$(C)`s|#jcAc3w1rYdY<=4av87GUT3SRN^Y9G7H+6`RhNazK{T*g?OZ`%_P+BF zgT?zgYl-YsYPl8e+JaP*(sBG7a+PvP4$>T~T=!VA@i8Eb3lF?NJpjQMBSM+sK4m3> z&+C4no|*9~1>>mBrFF~+yvmtjWHO83QMjI8!nXA})}I1d_T4ZquuM3eumBG?Qv+FO zjki6&*8R0>CE8@X)XVfzdAc-H9;av6gXQsfa@;&m1~#?BU^RMyT=Hwc3m?PJ*wtjd zPQm=DV0W=Omr^{FT)9NfWsR%sjN0jjx(S(>~1ZLY<{+?9srj z@}bInIEcLj6_+EvK-QcWxjn{L%K6HcY;V^!CWb{I746~6xUj_K7LUV;ZSQu%Nc_FZ zIZHS{)lKQ>E+g*49_78+jr5SD9iA*k)`M_6JkCgi^~x&eL-n}Y4c(~P;@j-rOboAi z4-aDJR7E6B<_9pnDShL!9UfPq`x5^|+(Ud4*k%es0G@Gw<~x8CeuHrV64uR{C(M6q zZ^^mQnN|c=VV$xU&SM+c0=!-y24;fAVt=!e{gW)h<24s?2KNbs`g;>~>NCxw)mX#S zLH+=Hh~FDns?Jr``&zMiYFX8vDN&vOVM59kWxc@A{%&Iz;7Gz&BfGz&BfGz&BfGz&BfGz&Bf M{GY)80OquB39D9bHUIzs diff --git a/tests/v2/fixture/test_format_compatibility/array_11/compressor_2/.zarray b/tests/v2/fixture/test_format_compatibility/array_11/compressor_2/.zarray deleted file mode 100644 index bb53d586e8..0000000000 --- a/tests/v2/fixture/test_format_compatibility/array_11/compressor_2/.zarray +++ /dev/null @@ -1,17 +0,0 @@ -{ - "chunks": [ - 3000 - ], - "compressor": { - "id": "bz2", - "level": 1 - }, - "dtype": "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 S1i##lR3#(|fQNj)kNgKvyQuO2 diff --git a/tests/v2/fixture/test_format_compatibility/array_11/compressor_3/.zarray b/tests/v2/fixture/test_format_compatibility/array_11/compressor_3/.zarray deleted file mode 100644 index 774e0578b6..0000000000 --- a/tests/v2/fixture/test_format_compatibility/array_11/compressor_3/.zarray +++ /dev/null @@ -1,20 +0,0 @@ -{ - "chunks": [ - 3000 - ], - "compressor": { - "blocksize": 0, - "clevel": 1, - "cname": "zstd", - "id": "blosc", - "shuffle": 0 - }, - "dtype": "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 D&HbRO diff --git a/tests/v2/fixture/test_format_compatibility/array_11/compressor_3/1 b/tests/v2/fixture/test_format_compatibility/array_11/compressor_3/1 deleted file mode 100644 index d2c7a046ddd66aa7654f7ae11dfd9b6686760159..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2768 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 diff --git a/tests/v2/fixture/test_format_compatibility/array_11/compressor_4/.zarray b/tests/v2/fixture/test_format_compatibility/array_11/compressor_4/.zarray deleted file mode 100644 index f105f83461..0000000000 --- a/tests/v2/fixture/test_format_compatibility/array_11/compressor_4/.zarray +++ /dev/null @@ -1,20 +0,0 @@ -{ - "chunks": [ - 3000 - ], - "compressor": { - "blocksize": 0, - "clevel": 1, - "cname": "zstd", - "id": "blosc", - "shuffle": 1 - }, - "dtype": "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 Dn1;ah diff --git a/tests/v2/fixture/test_format_compatibility/array_11/compressor_4/1 b/tests/v2/fixture/test_format_compatibility/array_11/compressor_4/1 deleted file mode 100644 index 6740339fbb7ef3372f35062e1e7edf19e7d4ddef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2778 zcmV<03MKUd0g(c57XSco7XSd-3IG5U0002O3IG5owJ-f(a2CZ?0E)GsDoY>|(`^p3 zbibMQx@IcyGDEMhC!Hi$c6YZK0?>beXL8#615Z}fV`z_!M>{F_mM>cD@jU*vySsa5 z3axZQAon@-D`@b6#6O!IlKTSm0yP3aWZWcvNF_l4;>#@c0GKTgw&Z|e%+s}F3=N2E z3(XdsQQq5`0Vno?O30XEW1^8p%1J1M3X39Sh^rA<8W0I;7>{SjoTxg=Qs)(b4pNW^ zBhv0UWo~$~#q59~cSU7|wFmGR=>)Js5?TTU&x8s;B6P%twzx`AD5$1T-Jv@M`Ctr* zG+J={@Zzn+5vy(&hgBnKGj z8Nst4zT9j%J2LgdoC0NcZF=z=8O5A5W{O&9Yon4x&e#ekz8V$1EVfMqdOFxR#Emd1 zRKAhc34MShaAd+AIBn8^_Vn;V$jVX>8UxDQFvc@D5j&_V&ZY{=IGyF-b+-ddoe+$= z8}Nmmoi$=Qw!FbpCWqDpP710Amdt?{ED=2P;2rsmD9aNA1}@Jd#O3Ry;q=7UmiFVs z6$>OAGu*&)WD0;EtWIEQL4l)%wJkVHl6mb$nkFX)Kq1hANDLeepuBYW@Q|p96mX&FR6D zG2_bG0XBY;H=qy##F5vc%M>$WDD)`G`?8>n6){&hG-2b$r8bzkb3AIxVHdga=ypiD z@Ck8-fiSe+uLm$uU1I8?BPp&l9%f>Wl90$5GB-ey2`lm}&_a_IM;5p`)1xtbcDy!1 ze5r#3Lj(~pIL zgBTDpbu9v`h7(1^Xv{70x>8_~f#yGvqCg2Cw2|ph!v^0N)}FN6T=Or3xa}b@R{|Z zf_lQ^gsInpMWp1xP@;jZv>ydROkgg+{0%=&_A*;=3 zXwD5?Xee$-aqLrup?_gDuxVgfAgJ2{#|Rl-4*`abc6ciTK**NLR6csc>55qp_vOJB+GGmAfb@|W!63zSvBZTPp)nPbSlG}905Wo# z_J+_Ha+d+wjra!}(AjxL!UPSlJq-1bz{TD&!vO}gRfES4x3@h_kZ_P>Lam!A+V zvx$ygFhcc)hz+3?k&g~)oaml;&#eG6tT{O-wIJd`)lf;*in_`+oeJeXdfv%O>+9{$ zXQb;{?xs@rt={qckx00nR`EpDxYE(8khN_$zLA}Uk#~yx{rK+kY{K*`qw5X3t1+qG zEirkI`nKt<(R)qnI)A3M{E78xtF`HoFwN zb`E=wqHs2MPnN0;rnE{#Z!|@cuRqv*Q?zp+?dKLa(?~jo1I}+T{j8iZEzuS@^mXct zoNrI7H~0PZrBBh54Ysxasp|0bCD6xoZIM}{Jgus`XV+$@%DL0P*ni*CAvJ#M;O@5T zT9;Jwk?!bhmp|o2A8h#<3swJr#q-zJba(zpBeA$!#M%z=p zZPTuW$K##)TYc-K&Dz~2BG)+hx3};9x4oMs(r#axx3%%J^;2n=buZ03SybhcZ6^lr z`N?^F+YcwHYWAt$Xqhx`Ue9wdrur>?oJ%B~&fm1_l5ZY8_Bf6+fBsIw zp2I(i?k};t2|+~XNox+;^e=e6$s^z5Oyamvf5mep00O+DCY zc~$W{J!LqI(f8B6UsZj}*%-M-ahR|7i>5`PY^p1f-z&&%aL(IU>eSBvfM^`NqtN5b z!e_rt)rR>n{dO)JtF?>XWqssZFS~iE*p!FvOMHb{kWVNUm0msD)g;?z_x{~Jr>?lo z>&cq59glA%bgty>E&128a@mJ*7P&>yIJW6tF`&-?Z0oNVBU_$MjA)nxbZ6>pGo*-K~G; z%K+-{w>XD>^`CNE#1S-LE7D22(HvQl&EuPceiIWNn?xYz&FX;BJQ;d)7o&s8Y@PnZ9WT3OH4 zbg0V3^(us&hF$G3TvC0#dPL_`pRRKqL~W0xg}0}ZPpKb#%itVvU57H=@5@2tkI#2* zSUhdwT&GR7w;cAK&%fC8VIU2^FiqEfNMwyZX@96T*@0ZiQDu8iYvE7b&Mw>M9lwdO zXSq+VoPF=q8IDaS+V;cyRA(%_W8uS_bAK1t;q_GQ*eN3&-EAtK$m7m_H4D;~XP@#j z%1$+DE|ihDTKCf%$wpD})&G^|!Y3ZLcM$E~{`IMHVk@le)~VYbqDk~6a_>v3Gzr77 z5uQD3?4Ntp&;G+{Z<^)WzSC7s<}GcM;iRwocg_Eh+ZoR_g=y|Y8~rL}(mlOPyF|{k zow7E|1PgoK=6b(GrrQM)X;X%cNELZ=p3ZFhkuy96b$`)+p3Y?(r?LFn1^cbe9|)`R zeG*eBZp|8fuU(JQD+(;5d;KjSm6UiYC#qj zSa#a=(C#;$iHEK0|H@!Kp;Ax<$o`Vb9{DV`z=Jth5(OW zY9&y_I*wVE!|E9VPPRU9cMQaSddx24ApEq_(f({$?Cs}om-spjBNLC5oWimoqj4Ps zTzgcdBRi3?hM@%Q^-U*4U*5rbBI$?e@w#PlvqR-vr)i7yTp;e${Y5b5_*dWJZ{>dB z{A+0C<19z=F&~+IiiooBFZA@C$5O`Rb#tZDBbG!j^$%PlP=I;l3eeQ={y^7R*)9{ zlyV>y19$3|k)2oc5nSbT^c8vAP(IVM)>(HuWo?Jc-z-7!(rX$Ohq&ytP#fv;VDe5O zZJ3myT%u8I`Kv(FxOMBT|HB@vct-NdDuU4&8%oPiX`MtgA{6zuL z`G%J>aKbA4>(M9t51)Q4c(mTsWVkrHcgwP?0@+<7k%7F>R;DCWg3BM>!f)MB*e7_I zepoyI{+RJE5b3^ERDLJMJzA)0uW+QwIHG#hf>`^$x{!9Ps`p ze!2L9mKPgF)T{QlnwB_NF3gJ~ChIhfHMK?U0xT8H)wcv|@wKsMwXKS&=yEFaVq0#B zMN%6cji1Li5hetQlgr}bJ1*_dIIMc$0`x~B{ z>@~`4?R-3S+vxTCL>-L>PFHH?zqz$`TJ9Z5>sUhY8N#NW~E5ytmMAvUNuSEuI=y>G~6${AH~}1>-e9^rwaH_N*~E# z!aFl%*QAp)p_hp?oW=FM&7vsmuHXB|24@*asu}^_L8D28zE*+qJ)hS&V*$DPOCc-yc>+JQ6fj z>`0lIvkVS6s-22DnB&}l3+t&+85xZ|gBY;2CHwG6dw3?j9(Og8p?W}3@sYEvwC6SP zI4p$s`kM0bMJ%wOa(_s3`zu*FxAB1<5pTzddv3P`Ku_z67KfftZuQYz!*R?^GcU;Y zkj=|K1cdI`z0L6S&=eOi3?K%!?uX%z%uc0pmXb!J?DNuDme?-te@*E2zdzNJspR~5 zH|uLt2V-(?!)STdNS%i54HR~M@lC-7J~Sm;s4_1k)K)ca#7csP-aGJyF9vWi`dxz9!ce`&W<1Vo-wz!TaRpcR-rZKO*w}j zKXysmNT$ zJP}^5oNaYTs}islH*PHv@`G+fJb)3?Z+zT}_C)_IkS>3pd0&XsfpL+xaO0{UD9xFa z3ot5(nznEgmpb|>iE{rS3)`eUkt}jBLBI6PuVIu*N5O?=EkmV?9B&GbzEARd;azg4 z(S=68EueMe%?(>0AazNOntQ<_o=1vo25oL8uGPj^hP7Ppdzu7IPVMo^{o|YLAXK@f zY`E3-V?m-$H#pS+&Fe<&StelG_;xk8#3Z4@w@wevxI zf;!@FvZ`_e!axk(l-hB1vhS;RnQOy*vnY}1r=R`jrl$4H8hfk?tTYf{7DS8;3ZQ?is6`Z0gcl&`5sU;B21H1 zL*f^hj!dq>>4fp7vl4y3#{?zUtwYSIvqf+QS!} zI%pw1*=@!w3zC((|oDKvI#W94*P zs`Z!cvjj`?da?4&v!&1;>>nuRh!E%4%|B9(?yS z%Oom!Xk2c`0yQ!!y@>yEl|5S2Tr4P>xlQckj zGlcc>1?>dr#^xnh967k~cV%qLny%6`U!M`MM-lym5Yau0KjBVjfRdI4+=8_(7D z4y$PWdKzy8gw4_GO>gMv&FMY!?ah}i=R)-Dhg8VxoFCmN_B6A;Fdg3S5x+?``$mdm ztd!zoDx&|}vyU8jqpqW%peOy;81_shRW}TiaeBn)$^+lNL9usFV6U+XW)jJaBFG@_ zRT)25o1$3A9V6X$HdkdU%D0}+sPWo&Pg(SoMA4CLBTxR`wd?OF^z)7w$kE$2w{Wmi zl3U|D9Sx5yH=;?r4C3cIiW7Dz={OQ3jL$TI&Rghyddb5?@g{4#Z8)_hcPGx3gsjtE zrK&S;5*NLGaU#3A((olj0|nG3r#vaRVlKIzcf1O{eMva%H7@Veh~p>Asow6=?E9?K z#$KD+BGvJ5!ql9RQ6j)XcCMvtDRwG>0wiu##>==HBOBp-_ud<^d#`WFxlRX~cl+byjXA<&3rFNQa;W$LJvW74fTX_suK zVfciQtt(-*7`YWJZ_d;aW+sABV=JF%?({iuR3pjMjC;dk;ie{U^NFQ|_f}OsA)b5a zbmuvLa6_ip)y*%tEsXh9(V`g}>Jm2tuD92L_ zCy%AAX>#q#j9rPoK}=Z8JW3 zi;?XEmBzlzaWE?(I|pCv9GRdW~3Q4)1Aj@YXQMdeunfY_YG!;V?(AeediN3Xa^ywVnM}(So)~dtahf_k3-km7S!_Ty7^tg zEB`Z z#xBL>RsL??v^}pUwEY^Kg28U^edu3|xI1~QG&FOtEPjK~;A1i}NSAjvG{%W%Wh_vX zOY%y-OmLUMzLG7E#~!ykC{Awdt$N#4FNK=@ub_?3v0C9D#@V2qn{r&9Y97tr@x^=< z))|@aYa^K4J>R7l(MZ<$|Mcn%6wxo?!l{JFB|RHGo-!A4(H&hhK`rGA9>g{``nmxA zz)c*0h>!smN^rl69v|`?OrIIUt$bV3y{In-ydsf{+mj1}fUO^*8#&a+jy_hcD5#t; z7?-sC3=u;y`=k^wV-XGyC|q^plKbjB7L%d zas3=0>k%HDG4>*>k633>fN$puej8Ntzv^;cm@x62_5qt@g#5u5+;xw+p_1_;%X zeavBZz>ZF|$wz~lKbT^nHT?dZCsy_~jnT?QTSTETYCif$Pq6Qi6JVtbVDZcAz#a;V z127mI7O4tEV*oq`Kmb5A05|}kc@F#r4uK&6IK=k>0C+BlO8{IV4*~4?AO-|t0bmt{ z0<2e%z&eu3ZX)!dB&;eTYXo}gN=EDxX+)ZGe7!2sUa{WW0a{0ND|1JTjmINXpAfW-o>hX?=@ zy)uOZ%Lf1i62yY70+6_9ET{;N2axAreM7!0(cm7y$~tiL4+oZy0;0hi1%lTBEZEb3 z1N=ll4-!-l0uW%J8|)HL2FAei0M5aNp&UR#01g2j$iE`~aUfB600aV{;OKwy!w_iT zp9P`-UodkBoS_iW|917pf~lZ3@CAY~(V$8!=*A=daf1_IkPiH7#|?wf(}9)@5(5Dr zItT*+ga`^DfDGNrPXTW|*4uQsv!A^@#);c-2Z|N+iR%xcuT0 z8T}lJ$JB22-Q!;yKCaVU>9GSpiDbmH0hs8xx1vv^VM{(INWG^QMOmC z48IgA?&z(aW|kE;ua!xbx^Gdk$*Ouakn^qj*2Lw)Be?;4T%`b7J1YD!%j()p`)1L!1gv&Nnd@W!6fYN2L*q@ILvx66>89B6syf{U@D0#3iQQv45X CZJbpA diff --git a/tests/v2/fixture/test_format_compatibility/array_11/compressor_5/1 b/tests/v2/fixture/test_format_compatibility/array_11/compressor_5/1 deleted file mode 100644 index ad82b6bf841c5301a8a264fe1b2d7186e68ad48e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2934 zcmY*bc{r3^AASs8yDVcJ%S3sNO0wilibB?eWS1?X?1XGHwk#KAO(wEL*|K|)Wri%F zWS4cQ?914P88grKc>nvJ>-_F>?sH$~+`sdj>s;r+=+a@{0swFt0c-#O2LRw9R7=-i zrrLY+7g+(C71am$t1*Z8?dLl_af}tPua6h?GrD*>{(TsAW0)Aol0s=vQ~1kA{(`#D zO=Z>~ey#^YxhS}q7(9-$JAdodwdB{HU32FsFi|Mm;Ham>z8NbQa)d&qKop;*0wx%4d_rQL0~~+>pkR!bY?W^EQI1 zHFI~BY=Pu~J&Q{IOeXJ{9903eqy&6{)&ZQ%`=tWQJ|`+MbG%T?War-Kwf&CoXH&e; zDr?nGCb<^BgNdvbZ4~GjQF^iOo7Hs50Ezs3?F>0Z+Dt&XVDVLB-`^!j{@7+kkPoA} z+Wr2sh-!G{TMhsyF3x-Xfb$$LiS5Upq=o3Lr7p{tf(r+d$S3~KdsL`EN8&@(2O~Vm zx2DT#ziUQt`CKwrX>(buuZ~v1zB0J8ZfWo=C#!DPjVVg)lF@pzx0@g9?__^-dzlB~ z7due~rHL3T%`t;29;}_s+Bex$_iU}2ZvY@GWl)w-nj*{7P*oe+=kZ)-qom*{AccFc zYfSEgOI~SG@YCt(BSa$N9zpttcmLr_iP8W3?NIYgnBqf$2R#idY}Iq($J5uZrY|~l$}gkF zDhux~yIG_XpVzz)s}v%|ihbPQVs6T*-hCG-jM0>s zAWVNxj%Jk;bdM?m@HX%QRFZ;t_OIn~PIr%>V1*s|p+6!0qS|)CnyabllbH@O|G3k& z-cUgsFQ!`snlP9-aXM5jSj}Kjf@CIZq0IpOpBH%7#6G22M_0AOOz>pVm8W`_dziyh zjs#9pX3#3Gj@#v*uob&{N_5$P|07rI;1LXgzar`~U_aB7<}GwaHathIw9zz;<9ExJ zFT7+6S3Ptuq3vE)p3^sXuX|A(jN{yAgO@*_th3M`Ik*(Cs}%Cw3X68R6 zvv8zfmJL}<%)sB89sue(!hEF=i79Oe`A*yKqc`k5yK`%!K4uD*O7bPLy;YJJ*s7oS z<#rBnsO=sT&|d7(!uaV`b3WKkNS4iZa%gEEEa}y-58{_@J?eARh#R_=tR%oEi6T<& z*BqpmX(^gjC^9|0sTBG>l+UC5k3c|4ZYS$?9VeNHsXYhg$14dd0%`0o)VVu2LoCF5 zL&o&L04u#A7W5MyIb?!$UGMJx$ftzoCNl%+V#Kq7A)^^Nynd^H0YGBm9uOaNnZd?5 z%Bc61>u77buT>pW_jg8vC^Kxd(7tKq_0^6yyqg7bS;!W9BZ4Nsg-o}x#qu9LBgx4g z5mY6CTJEFrA`P}O7I15PL6$PS!_3A~4U-aLRuIEWrPHN~BpLTc>5CuM#Invr*f=kc zR#3-BL0y^rH5#QR(FWGx0wSA2sq8%^`8P2F`EgfKPAZN)Hcu=S_~z(O>Pd~m@ypID z5B`0#MZK;pW%+gOEeX)Dik=CLXOGZ7pSPLTzCY|DMECP#J2e4MmyxKO#mVGXA$La! zaf$BPy%2%mImbPWjj-$aOa$kLbDZpEI(&M9Ifd!J`i+8==Lclp`!H)yh!TVt#c#y< zAKg~Hjh99SgPwS&{|J)NIWLo=L2KY zc~@fp&WDjc_$Zr(u9MiLJ;G+=LP4I$fe5{rqRSej2xqq{Y9*;?w(%nl)5ddc)Z4P z2ZUCjMB-Gcq6Gdn8_wzQ8`J9%fHevWP~|NU2wnTTp-=ZDWP4t4Nd^<>k?nBiaU z6_eqFsoY!3sI~pTT$f;qV@dpY`(|i!H9D96Vog3QR714-&EgpV0Q*`?S7$-GH(vcy z8+CTdTXTetdOk{saXCLmIa3u8CMvGhJ?Y#UNDH(RZ?>DfQ9-IE_Opd;E!pm_lhKxT zt)#}lMK87E?)vF5GF8WOir8e1#!+IL515FB%$DFg(o*x?<2>3fc-E0>N1R(>BZYvX zUrWp>9%x*3aL9pK2>Qm-I~1kFTZq){n20(Eo~O)Ub!uytHENWdwBxhE75BoaW7 z0^(HrK^l<)61Ipq5c;-gU_Iyog5e=lh*4ZYG)M`fR8l6W2OyC~T&JCOKoX7&f@Bg% zpnyao5d_zXR1k-zQK^(b>hUIlL<<9Phu{w|kh&K}0V&6AXb^Xd%MGMLW<<0&2(}!M zh*Vq~m4F7ROwiv3p*d&4RxpGJjzOdngnV!~C|fls|I@?>r+{mNfC%|-QNSDE=4sbc zC@7FZA%LV)PC15v29xNieI0FVwbh^R9l0q9WDaWO#S0Xit)To7O& ziJ1-ts6!gWAdQO)qYgDsxw@M3JC2X%WiA}ZI2&LcbED+1>^ch-ECyEPzA@hPF6r0nB<;N$yD(3W5u}pIgYBi8qwbLDXVd z@7$zqa+U@lOy(5EIDNT7H229D*}JFsC7W{_PE2z(d08J5s-K^!6w`+G=WirHpNesR zLEj)I-OH$m=Zc$mq`73wV~`)q8y!3uGbB4CnoFnb^B=vX8{%4M@u-G#iQM0&8~OK< ozli|JRtmm9u90tjImWXUUP?7t8Q0IB`j31*_Q#)BD=6SBvXTdvYE23<%tSx+48ZQ-vM*|)V zZfQ4L0T`Uf&GbZ4BaJNeef*G_gV*a_O~H4>kyYM<`nf0;xAYW?9}-ol0fPNBJ(W%l zzw{prLTMCzTsc|V4*cQYf~F`d#fNNhLY(sxNC$=>UCEI$$Wm0_tTt=>k6<(RN~Nb{ ziM{~mNvE32TcmwVUEwviiHt}}2OZ@TNaQN*Ys&(3jdKKdLV8ztPR(`C0)Lj?Aio>) z#il5dw8l$Ng;3vLU_R~<<0{s_1w_jOr?{!&Br+)V_)(uZ4XnXcum#!4ceEaHsUV#T z@^1^<3{x7XdrF3?zp_JM|Kvxc2#n`Tm8E)5z6jN`9|Q&XF5R3Ky^-)bI1N3aE_?-9 z&5x3r@B=^-4-p5_tK|mnHts4t*PIUb^6eZu0*kn6bD4c1n8dB-C(58iK*vBCn9sfA zKOEK3%m*33Z9Wp_Gi}LbQbT8I{oQ-SO{|R@CY=)Aus<1HdQByD)ZNy5+*Nz5@YKo& z^YBSuzL-j7(36Dg%v^Q2|FkpFcrNay&Lp1%zOt+Sa=n0lK#f2?e?N2p-M1zNFIXSz zze5_dByDj=?Ew5l`jWh*WzfyVD6J$^;rm!xMy+?Hk)vdHH`VkAr*OuhwV}b9MGl5X za_i;gAfNwMn=RY{6YM7WPyazaQ*!87m3dZ+2U!XS82C%*SA99{pcA=xUhp)?vmL|JnZ;}ZciEVSrlON-24_+c zOs@1fzs5(1j_k@l;msir`W5Au#EO~nmH;mLg&cG5Fn!=B^&FfTWEKd1 zUo(AV3m8oI;S}SV@weCmj3J%P$KFZ!YaC$~>bpq}IYqTDKE`ghTE_M8JXN;~D}}#= z5ssDIHa?19Kq@WP8m5G~TryE?0IPK$UK1MS`dO}5@=-_@mRMKGx4~a+jmb;}@fU>% zX}|kZ;bEXkSim;tN*w7%3}L+|?0M)Eli;bbj&OVl!~Uj~`r_@El_+ab@Te*9^FlG; zCi8$99`2>?0^MN-X@{~(e!vevx02bAHpA?P>`1&Yyn(6lPoOK^Hx6}zTWj~HtK)Lw z9r+cT;X91_P`!+D$2si{Uf(F8VXM0sKzMp|0B-NHc;(;5uI!koXOH&@;R|hi@6; zgP)d0%4?7)>EuIcs~$|wQRAdBpgp^jKomm?!_6eOI#qoo?bZ=?1Xq!z^h>oR48wEY z(J2YgQn}Kr{--^rq6Wa38VNtAkCi;I=F)GIPec-wg{J6}a`furJV;YptF>_{iT~{Ute5 zIVDXEkB852J=eqJ0=dF%!|hx3cjn+m{Lx@vvQT+xbO$@!S6#8TTXq8+1Rs?(rFecF z`58Qvi@-~AD^$!!;cZkmISn<@rc(~H70&E_LAk-LN*kR@aEUb5`qDfOS1M0n<0F%( ze&A|KA#4fO;#JmVupW%UA8I2Ff&N?mmX8Nt2^Ut50MGbi(ir`Tl?t}grcxH{l-3nh z;<+gZeC6rIN14^`R|gdQKJg0&T+8WrDHqR|dcfhH@3~B)d3d4lpIYOK6PkiMAV?Hx zAN)M5lN#q0V}I!sy25`?k1%(~$At^fG@~e38SsiDf zbs;fTgs1SyDRaJcG4WLa9jXw=$l1{@C5LX zIgIS$C)`q8;H}pCq@{5+j<4Be(#TQ>%Z2U!{-mYZS!V1yToRlo1e9|oOJAvV;uH9` z^n>$Xu$7Cp{*(76?&7k|rrZ=@xxbO}xD*T6O}N~3gWy0{_#qquKk#j$-h|7;x$5vh zbLS;=txGd|6>0&FIO=hm*U7qIorW184^EaVz)ET+{W;aiab1m2`jctl zToCCP*EyTnizCPyWua6`(#n(J2Jp9q2upQK%QLr_x%4IQ8ZNRPG55I`cN+h#;8dzT zS=t>ax$l}2Lx*iKTL%T7h<%KX$;Z_=vTP-4+G=|`fmF! zVG|sR8>81Kl^Ye}tvT-V=p*M-vtepEO0zzY3~AS`=yRC3g;1q7tQ#{+AelO*E5V4o@;e=+*W$DA)=JdEP{sp`E z=FmR?N?0vi(q=HaQaPuq7XrDcEd zqk1Nh2jr=FS(+zY^!C=b!Yr`c{S*8WB=vo_}uCgVHVj6HrV&T zD{!?r5b}zfTq;`6UI`55=i++coGn$Z^sLZ-)ouk(a6=>>bzsK${^6R){-D=-!WZF9 zzI5Rj^|^NkJYT+rZ>F@6$1}rWAFu<|4W0tCU?;~0uAlIN8>VK1f6LavJb2vw5G6@# zNd;R@Es_D6ZOknzlG_+5<%g2Ci!I0*A)fes4TLbc;J6MpvnNm+xeq@p^rx@^4s=F> z$#ApPu$HG=N-KqXDxJF9*INzKS4cx8k{;tOA&Fd5erad}(^S;BtkMy5p5vHhcUGzU z(Q{8d=^OF|eMNd@{xA8Ft-aZXnGYi6m%)g%&fy?^$UK#j0$Xq`eLHa!Hr(ym+9bw_ z0j<+lwPU-9cO4!NPjPv{f*!+^v))VcX-{KjvNy+y!YzCd-GYPVtRCxalaL0^2a@10 z>r!zpH05mfBT_)7DJ$_)r=S*4tw1mGOsqudo<*`$rW$+5f5Pd*Q^hHsMEn;?aBc!C zjJ%$oN~^3@uA%TBo0o6xYC`HkRq9P6A%a>cjMBQ{PQp@8peK%!6>EvE&-~P3OQ%{}eQVsIWvDNq`6AI>I4eoEmMWd-oAsOUNi>%5}i^@@zELE|)J= zhNS*&pH|d6d3t$!im+p(yR^-h3-oX-?1FQTOb+*xUI#nS-CWJ3IU&1k3+U_JXtb(j9s#E5?(APIUa{Uyix;DnyGj+G%m3)u z5LduGw;rimtgGC|Xn-{apSG_L_{k;9NInSTlurI?<~zY>k5fl=pP_EB#sfio;{MvB zknw86G)K`zSG0A(j@{j*TX2-Nf?U7{LNiL6$Ub_$GzetFHuxmEVRh|!q1@oUNnXne zAk(>0uHjnZ9l;f0&Sm!($wRro1|1STKm{vZ4Ec$6Q$Sa8fpEbSzIM_*v_z!L2N+`*mp6@dHne{d?z8ym%~=56o}-N)ZiXv+P5i8h=zcLd+4ce42t|Zfy6V6nZfL7AqMyA*nzgByw z|E5L&KsEN6;xzoIlf6c;EogLM7<~aI>eo}vEtBc}NL-0NL zk2nEbwQBfnk|-x~B5A@u!3Am@GZSwi`RoGs3vh(LB6i?itX*7a<~UPJo2TUHiQHP; z7mDbf5V0>?uTdX&ERg>pAMxwhR>ln}gKGt6hDNw>Q7lOzkA=&jM)Vv16INCH)FYIB z^fFs%k7lMB7;b^nl{~3f-AG1oA4*Nl-`oe8uHc4rT5MBx6SSc+!FiBL#tN7G)9kJ7 z>r>jv^UdEZ66y!+ShSBWYis-{-c}X$zsawi6{svhl&#*2AA2W+l z2*aDzQAE=PLkT_QDAr-<*n?#q9eN62vZ4Kxl#7~4R7GZgPp;=xjQkSQZy zc#4AsU3SX>SS)YWviZ$$y19>u<$IfdDiz>7H%vPX&$_=iHx*wudr5=#D2_F0A^1l2 zXbXe=(t7#}&CjWo{wn%i-waPaH<-MzO~(HV4lr2HDzt=8QTLHy&MCFbahE?NMdC!G z&>3m|%+C>j;p0G>93b!ERB<_&gdQUfO|zoS|A?xLpdK=BK}-MB_R1_3yGao!TYCYj z%2e);QbGQa&EZyYv)~lHhtSNfk;U%I;>DiX$~b2?u60=O48;{&=lA^2eV}x#yp#zT z8TKt@BMG95;`2GXlSbWzH_&ygvpo;SFmK!~JOli@a4Gkf+LsxC>QRN*1P~77Th=yf z!$_stfysf+d6qXx%LH6Mca~7W&xMB^Z8-X~fg?7lkAn}>B2(76cz zE@=tgst{@*(=0K@EcL7lwzNIq+MxkSx#$p+s|=>>WFMYqmZ9$De=6_7O1qZONV2D` z10Ma3{#YPZQ*}?UguBN*MSHbab|StEs<;A28nZyJht6qrxIE`^?G3-){{?!BYEHtI zu&Wnptq|iX7=-R=&6uU;d9xi8t20I=SBNJnNyabKaA|gkvHdJqwVWkPUC>4Rh3=E; zF#Zxg71og}G|#sb&zD@s^J<=G*12D>2!p<*QLw@nuxc0t}g?BYUJtVYuZPB`d&(TFx zRGtTc6d&GgG-M0H(eP^!?KwzIF}%ipdm)wQJ|X6$WU7(9>hWWcJ=nmw5RRbZ+$*d$ zNk>pJx6D7wyk;E06QpG6rE(9f^$Ti7(pS`MxII2yh_`=|@CQ4BuUoQ3Z{f_gjKHI? zVhst|K|AN!auH}zW>!(6<()78(?)=H?yUk5z6YKS`ZxPvkB# zt^`9W;ut+fpATE|FX1^&1n)S{f*e65tG)Nd--ymMENT!sVhiFD;_ZS$lTe2=if?Z0 zmL3{2;5O2pS}46XV#Aq$m0yO&u^!i8yvckZ8jiQ97jP=R7fuhBOR+{<>pAm`^TEw3 zPu2M9fGW>b>pn13zm(4Ml}S;;5Y34TgmT=CzuBWGybX?}3$=FT{iSJ+4(vwV!;Ny^ zLdEg~<|&@;>BCIn-&3-1mSdRwM7eh8vQ(i&aSg=uz*A}NG0YvEM08=ohP z4K2q5K?rp%J0YD>o4|(tsqSvBW`eIImhV@%d!Nd{@ml&LOrtY--s z5B@NRuyvJ4l zG-{LUF;;pUos3VB$`j0|T<@?6;z93(Gn~kMT2^S9 k;$G=0IcQGs)kFL3SHR-%FnOugj(f~i^Z!dSsBfwN14!jOLI3~& diff --git a/tests/v2/fixture/test_format_compatibility/array_11/compressor_6/1 b/tests/v2/fixture/test_format_compatibility/array_11/compressor_6/1 deleted file mode 100644 index 7f243db58c9385b5e7f6df326e903b3ddbc0674d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2973 zcmYk8cT`ko8phGp0E%m{qZmX`L1372@7(XZ9W7wyc zyF)*S{nqNOi~_;6ar$s_9zUKUxKeHKjuHDdCWlcUn>JX4v#j^U&e}!T!0!w!VI8oT zH_$2Wz7E)}`xEGt{@H&1pymY9ICrQNDt_wfr%ocjSDq!^K)E~istZw*FdFranqkxt z5tdGO6jkFo8>zg*e<~K#cE%eRo2wO=Eq65cuU;2LHO6#U$ zr*e(HWDm1BPWU;bNVUBI9|ZVaYq6g8!N_eoelx&OLa85mR#@c0QiJLv`uT$uj2()T}=%)L~Cyt z=xjfYZ6THr1@ht^mTtL#lY5jRpKK2Kwb%AP?UT+FwdHaifp6Fq>3mdTruEjXO^7EofLjYHegFF zBqk*HuDDFKpNL9GkR%YH7x4 zu-7_I{xy(8G|&;&EV!D(+XK}ZtI;U>ATrc%mRp0jtu4>QwXF1n7N zqDEzy-LCI|P>Up%B|qU$n$yKPE!+CJrXV$*IS)Re;|z-I6~PL>R<~2iQGwk{oI)a% z@A@-)g$=d!L3@obVVL80{uLEM7csZcJ>nVqR*Hh%(R}d6`a7GUM{Z4yJ{`HadoYTNR7<>J38gCO%HZu*s@}_JTRqxqqch^J5IOFv zt{t$^vw`oNQU-^)-h#>KkmIV}37pZtk{9@WsG59cq_Dfp6snt%;Eu(jde{hOa;e8? zagEK`%yi-Et!?xMBTR~p8z{6w)wCBqM??A*_c>6vn@tN;dPi{6#nZBB_2(Ya=ha7= zr5DNV$>mz88Vw&?trQ7Au^seBF{xHp|5^Dm?}J|dIO{`YzF1`DnUOr_bhBsmCDjAC z-QI<)(;bhJ_FQGg!hYo?{$&3s+*u1%nyjwupREF`EUAHs6w5&-e+6ZVdHxWs5X`{S z%`Egn@^T|$r>ckRzN9kglXysFl2c<&=q=0-)EF*_ox8UaYXwHp^HI2viq08}!5|o` z{!Bii(%tivwqOU>gf!z-Um8*VaqJg?*-8g`AmLOdqQ&H0Glf`czhIBjJBTKkFVTe4 zuqnsHSmRaYFf|@o?uxi{rjRC?EHfHr!Y(L6K5va}o^&p=7E(=2h#{Cq^?%4OjM;h^ zlTdw^n4&$_))Jw1NR!+O{T7{x`ikksGB^fZ_a^hJjH%+}ZKs$#_ee^zO4MWQQhAQu zfro1g*=WmxUdNi+0_mDMOPS5wR~Vsz*s2|4-hk~m22Jpc#x`*s-MFhzKA~OnKgL^x zq5KwYAkz-K1+^CIH`xb%?7b;}qK1$&^il4w>@IPYwXk24ySp^T3g#w~;Z|$v6koP~ z#l}JWA@d%8jL9{}=q_!vKY<7_{X~CjJktcr^g41x(qk?f{ow7TTk!({rJt7#W)^pw zn&!=~odD8ZoA5et)2Og2@%@@`v@d|92Tb5JGQty)=UwhZJnzxuGG3{@!B$h1+%$PD z+)(klE*q7DXkfG2xq`v${TCnxi}!cY64|NLa;t-D3sOx=$MJ8-Rmx>KSaYy)-4n^i z$AECIL*OOq2?)Lf5y}krX)6(YT=xU@+>BQ#7)N#4QOBIbtDKoe7PAN*gX{SvY#X0r z{b`VG-vjdl%Y-ut3-AatHIR+gc-!-9-JiQwqD{sty-eRBPnTxOh&&!oj+^Jn z#HMxxtVS=9Oa2}3!u#+eb~Ra`Q?Q^a#9d;}r4-L3S67oND>;&BIJTQetQ;ICEAS<3mT?n)y&+P9^1ec;`RD)FcT~m2bh)YuVgVE zuepe`xNjiL--oDEpKBhi#u}~;_6OL*{Jy|ab*{4B*OJXw%c_1&iSmRCt-vg;Ue7CC zitZVQl^Ws$;h;L6D#ASb6P`fZs&vmOiLl-vAKkO}L#Z28Xv;DlijQEpJyI)Wh8w>b z-|<)b^)Sx$JW0O<4NQ==kuiNz(j~SOZYQ7k&zGkeUigT)AdS{({=9Ysl^b)kTgD+e zH_+bfbii=d^EQ4m-sR8jJ3&8;dc}@+DaIQu!dTzTvLjJ1eS&g-`!u49xDM2Vfzl6w5 diff --git a/tests/v2/fixture/test_format_compatibility/array_12/.zgroup b/tests/v2/fixture/test_format_compatibility/array_12/.zgroup deleted file mode 100644 index 3b7daf227c..0000000000 --- a/tests/v2/fixture/test_format_compatibility/array_12/.zgroup +++ /dev/null @@ -1,3 +0,0 @@ -{ - "zarr_format": 2 -} \ No newline at end of file diff --git a/tests/v2/fixture/test_format_compatibility/array_12/compressor_0/.zarray b/tests/v2/fixture/test_format_compatibility/array_12/compressor_0/.zarray deleted file mode 100644 index 3be17b750d..0000000000 --- a/tests/v2/fixture/test_format_compatibility/array_12/compressor_0/.zarray +++ /dev/null @@ -1,14 +0,0 @@ -{ - "chunks": [ - 3000 - ], - "compressor": null, - "dtype": "VbEsYfRa!U3yp1$Kx0nHD>pgi+ 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 P|0d_o&nqFzXD9v-)zKK& diff --git a/tests/v2/fixture/test_format_compatibility/array_12/compressor_0/1 b/tests/v2/fixture/test_format_compatibility/array_12/compressor_0/1 deleted file mode 100644 index 70aee3ffd9f43641ad17facaa984800e405e1353..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12000 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 diff --git a/tests/v2/fixture/test_format_compatibility/array_12/compressor_1/.zarray b/tests/v2/fixture/test_format_compatibility/array_12/compressor_1/.zarray deleted file mode 100644 index 5202ce93db..0000000000 --- a/tests/v2/fixture/test_format_compatibility/array_12/compressor_1/.zarray +++ /dev/null @@ -1,17 +0,0 @@ -{ - "chunks": [ - 3000 - ], - "compressor": { - "id": "zlib", - "level": 1 - }, - "dtype": "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 JP>>Eat-5sf*%tr+ diff --git a/tests/v2/fixture/test_format_compatibility/array_12/compressor_2/1 b/tests/v2/fixture/test_format_compatibility/array_12/compressor_2/1 deleted file mode 100644 index cb808fcc9a68dc87d3383ed03a394fac249bebb4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6120 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 diff --git a/tests/v2/fixture/test_format_compatibility/array_12/compressor_3/1 b/tests/v2/fixture/test_format_compatibility/array_12/compressor_3/1 deleted file mode 100644 index 817b8c5e66543d9f0f7b6974805fc43ae79bf813..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5435 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 diff --git a/tests/v2/fixture/test_format_compatibility/array_12/compressor_4/.zarray b/tests/v2/fixture/test_format_compatibility/array_12/compressor_4/.zarray deleted file mode 100644 index df1f247463..0000000000 --- a/tests/v2/fixture/test_format_compatibility/array_12/compressor_4/.zarray +++ /dev/null @@ -1,20 +0,0 @@ -{ - "chunks": [ - 3000 - ], - "compressor": { - "blocksize": 0, - "clevel": 1, - "cname": "zstd", - "id": "blosc", - "shuffle": 1 - }, - "dtype": "jfG@h3&ey8nbBuYZ#?5p@zrlUZLq|zl=7c3-YO}z{S z#)awb;M&2sKuLU9C^tH`h3bjsMJ9u9=9>^iv%%C{JQ58K+uj$ciU+~!icH+{!h(7u zQPYZP2G<bw*%+rg*IYLFIM zr>sd27fr@khIT;=A}ZP~8?gf=m+CLU08_07K~Bs?`b9or9h{KBjCGL-%mg6XmY}R9 zG67JmxQ0(u9u~0N=;9!U2vCh8I-FcwLh%yF8_|U%RFY4?A`1Z$7>LeRrqY5`unX#~ zr9z}h5FzamVXX(ycaK1@p-6KgjK)z$k+d(-n$|DCSfRU=VVysqcvo~`JVTfu04z`r z#~_L&+_WX;f*0VVB)=3G1mN&8Yhw%}lhP4eBFJ=ts05e-m_sjI45@j%Tp^*5iwn#w zXKE$H5d#hgt6rD1UV^aB0-yzf+IX^Ds8nX|y!!F+%^KnHjZx;L56WKH6(HuEO=BOA zj=vvAoJ9cvniy^62!=6ZN;K_~1stM?K{Q$^uGE!nPFhHzdOGC`XDRvETOjk)wo8QJ zr4cl^vz}p3WGyT>2m%+SDD-eUE@&u(Z1EdGNfXY<3JZ=VRGFL&9o2c-fTd@uah$*Y~d&o|p*O&hR6i22-RKZq1B9vxMMlHy9E9Oh@cA+Rp3B!G$v9r&9{vm}F;yBANWI}?D-sEkpw0EKZN zu#lm}#i!>3hys(pmWWml2q`&0sev`53$QBJhKyiy(o~8S)e{brP*Xl_3WS=TDwB8w zA>byd+5qCfgfV9nPF&q0Q9#2&ha!M1E@GfK`YR$f2#ln1g##uvvP3!h`Yi(>gjpFP zQ%H5TsD#M)3Ec99dF#c*)C`#|&xpAPiBYyNPato~W<=_0F}_4NSU5CYa7p4BykX}t zt3-&~FPxBhM--?~=x`Fn1;mV}g09@4NiJAJu=rr$V+v9(m#hj{jD*E%5rn=bmAHIO z8*>~-Xh8yqk;_#h6{4m$x;VVWa6sx5=Eh{&+2jDmAunHdPqKQO#0-dez^r@m%80tU zJ4sX?IiUlF&1dl12rX=!<0ZUCFuoiZ^9-mxo-t)i0(2DnVMXR#6VoM0f@cHO6v3Ps zzT-Ow9L9~zZ_0!|G6ncn!w{53+BSh=iftfFg8-s{4Ld8lUt}>EJ)BAix^$6<3!*fW zk1S_OR|K@mGjwz9Q<)e<5VySJqQoc+44o*^FfMcCZOo?0P@CKvj&2Mq7j(1u%)Go& z(H>H9qJxEFz_EveCA3>MU5r@W;8~L?WEjHmC&7v9ThpTCK%h$LT&Je<2dez6|c5%ftoTkKrfG=62rvA)+Pnth5fv( z0*T2XtH7y6k=00{hc8Ftq$WbN2GLFe!f=%UeGc4+(U{XRBeqjPuM8FC!y$@|CZvWUWJeN|CQY(dgoWR> z7PM}_>eQ-?V4`X*tRlvQEHhPjXC9=;>8=9WQo)q^xgz)T1Hsc~ql!7l(M2gkGBm>A zlII0R3oRB(9bzOASUiQCq-OMHh?j9 z1w&GU0#~xV!PzK}PbsWeZFhLkuy&h7HVF#F&r+n~hp`Zom@uy*P+=p8PPh>2FmfsF zNkhom6hsvas~TBGhhhvo2Qpa4w8gGb2|W}d6{0wq>ycOd2RLI!Dnkl}QX%u)&6IBrrxv(~#EI z%Ar)W#m5RNQbYXW&DVtmvoE&V9H7x+*tImc*%q2QRTSf}3?Pt=oTh1wYxogIkXhlF z-X{)*i4-`$Ov%vu)FtxEfTA4pm0+T-KwuX@B~`@Q45lEBnc{fWUWuK?pb16l!3koD4kFJmUNYpsn*rjA67CR9vn`63 z7PwBtB8Jrt7&FZQ3gOo{R^&Aesw|%aIWumIQi&RvFsNiAb>>xp(ZcPO&!mAzDG*9D zX)G~Y6w6IY+gnBP0E6t6rTUtb7L+EVONU)3y#!{Eu7Dzs!4Tla){(d$un$pOm0&(j>?UFH=7cPWioaX!(FZl5Vz-kyrQ#S}@!Hsc< z-)mchHP6}_Q8G+o5IdGS9*lg*Fw;b{(uJ{;(QkFEb`F-pZsSlFAZQ~*`&z$V{A^f@ zo6n*OJgW_m~uSx zLW-SgxjtU{7paXY~ zOs=b1=mK#D>{)iOdUQ+S>I6WO)HW$d2tm_Wh3V2IcKKs%v)4ltNVuUcxfPN_bZp#s zp$%h$K@jNXE^LPQSEsXubkGpNX4o7~%vz%ML$K$J^2tyu8Jj*BcAv2jlvwuas@6HT z4a?QcEaI}lKmsMP8VIVJWM6C%`Rzt1z-bO;u!k4n1i>!Uh=ptBh)$)hFk%K)BiqP4 z@FJ8L3WKmgm}-?R9C!diLBUy~fysyDlR^sL79&(!_yNH_J0pfVWjv=I-24Rp(sWb? z(F1}a?W>*@6dWYOx-dvmiGpyul7!Zy%$`etqZcSh6~{QwxUAaCBsKc1G&6Pd)pa?= zI)RZTHVDTK2n84^9%N*40|NwV$4_ETS8UG*;tI#aS_>bx6hMK9Ixy*o9{I`$t?Rmi z>q|4{@yKEcwlUeRDuPWoDU&xAY(BmLf$jYo>D}Q?^9Aw@?+T(Ud(M>U^^Prv+uI)( zG7eQeMosKhX`w}TsQ}`K>I=i{ky1%eTHvZLSV$_6H*c#hq=MvMPL4N8R}QdJh7!zj zu)@U3BzKFHFfoxp5W|sSumD&mAOvsdO)-MC@T9;I8l0k>li?4ZCWEV&iI`=gAjNKU zFeG#1;WKqq!LSTV%m`pkRsgHr9Hr8X3PNynz0gBr7*!-lq8S`VW^tPEjSlt*mc2ur zeZ1o>s}cKTv22MCFGFzX4oEVLg~2{ge0kgrGkV>-bdW{{DBEQa$q(_igyL!f0%*d9 zXFX84CV&C8`i9*=jWXcrCF-JXl!AS})y2WV1>_~|Nfj-~*3 zj!^>{+*&49O@&O)6%dNx06&88DwB=y^4w*x!%#)W)B_}Ee4D32B4iR{GkHaT9b24% z>J9uNp-E3blC@7SHeg$XkvMru${b6x8UA4Abq;56S-_DVDwJGPHlByB$6!laol}-D zBbqwvB#b#jy>N54^~1_gPuPnHo~Kx?0svQgv$;*MCIm0VXk59%Ai`#k)h+v*R^h{v zGdxkgSD>s!X@c5Fr1WyjVS_C_GIwKAL*kYmo4DMF4YBxRYKDc(N&yn6%9iBzeDatW zMDZy$<0pmp02U2jRUBTZl%{dJ@S62U#IJ^xsoFFc4g%84rvNX4B5;oMF5L{4%UIkuPri{cwX>?U&vg9rPJ}$`Qfh0_Z83Soi3koF~)aVVFJUi3e zaxA^GLke@9Wo(q_Y9NT=1GG9@h@tPp(gq5iID1BP4}KgIfAkI!qw=AI%Ow)*)EJiu zC4_27-7w{%`~u+24NwTsMv^Nm00^W;;i37(3y=ua$I1jPO(;b$c`~rWMYIJxja{ia za$AHo8T_lDfD+BOBf&JSP3A}?jCINBZIN|>>JfIh$i!w>x=6pjJ3xwi0gTAbS8OhX^#iieF`3*v6D(riPMz?1a5n{U_l0+(oplf`~dG=u_Tkb;Xw;7 zI7w9`Er$h$ddd5u#a$yL3yTE) zMiQb_TzqdduGo;iDz$v5zCvZaguhJj8SyI+_ai5ngL@)($d-~(PO3MyXh4*dl2wN- z4q}f&OhBr*@Q^YIL1gT9a`bKQNv8qCGCO`>JV(O>NP*hSk2vlEu*;!!g zsD|vNAaLNw6L3_*|s!c#1hT4-b` zIQQ+a0ChZMva9xa!S4hJGZ$2HacvNynUaE4lY@-{hk$ICu7G7^K6-99k`(FketiME zc5EWJLP*;Mbs*JfnVGZ0(1`@B-p!%i7&D5FW*(#~_7BXCjE)lg$Xlf&PMhEYOqC}r zEG(jUNK`>V5(;Eigzf{>N!JRYHiCddlMJ<(G^IV*F&d-+TlAv5Q5Cc`x~dq8)I;sV zRUR% z?ghUbiGh=`B* zxU6E<+&YBP7(cgcd}{ zBV}cz`0-vQ`Eq9RfH4EwqDX?!HQB`@(*(hSPycBp4ntctpN5#f(KtlCkAj=v_+wuwSgTi zq+n!^ikGMc5xxoxG@)zI^ebiA$eLML=55DN+67UwBNA#Wv^o}$5onbpX`4Dkh@~!s z*Tx$fM{?M`Xe96;&&-;*0GcXj4y0;?P}1YfSAgBp8eUH~1|!WZ9X6;j%l5ammO+Ej znFdvU>8W$_*TzB%GCO0COsWFmwV6dRGdWUQ+Gh(prbet58!W!EM42G7#m2{vqMju? z(;f_8MmQ*a61n4vpa;snR~0mD?rL>__z(&j73T$)#>o^lUSJ@bKODbux{Od;z{kKA z4e9Og0w_N*x1#AMCxQf{2Q}27#vHsDD}wyh!)laKxl~u>>`1_m8(YfcLaL3B8q-6A zYE5lwZWODVD=iZhSgIyIOMtcrzce^R&?E+d*%t`7EHhZRytX#2GaQD8jvOrwZg@|3 zi?E(b#O;ca@xkG6n&h{0kWUNfrj;r}5-L=%u;Yc95g}t(H;Jm4mKL$iNaH#ohDc`RA85ctD9!SCZ0P-4MGCil!`?fg-QXu$76K6`a{k+*TzqAUsrNLc$Uh zdjho3EG0mzJ9hdMi=hP5-=dW{F?36$P^W?$Y8***UXmGZ;S+vkDcDG5TzRUs?LDAS z;D_oBiYh8T5H4BJDZmug^}`g|Ga@T;h>oa}L06-O>JMDjs4_S9YOv@upmV~AWE`0)MMc&XuE8=9R=xOEsR3;kenCo;lvXAVGd4zrbwUfe1UAwL>Nu+fG83zT zmSYSZF5VXATiDp^VQBg-QT$m)Tr9HXR95fU#YdtDXm(vTZO0+yXjaxoR6F_K4$ z2*4Ymly1EVTEp^y1H)9YK_>yPXHmlzl1I!ghIt0e9E?S}{)I_{>BC82o)qtgH?mXF zS=tpouzV;P`o2P-s)+0cFjb(g?&5@0++C(Zz0Js$s-Foke_ySa=|(QRas7xIi%X#KGy&QdJ&ndcmeV0!F6U*MBpRv z@2d}oGHjeKE;R(N7)1g$fwmYPhlRX}FJy@fcCOE@DO}MH-3E?lWvJB8+aCJEr zLyHlnPSXO7qgur=OIW|`5jx=_Kz&f?xGFSxi-bXqCRu=d$Z|u9DE1#U!N2Em-F^~mS;_S{Mlh*Hsow~>$Q!=0=rc$a7u{)z->`YAFc&84_ zbV&uCPHQWZxv(}TB_dF?m1V6WK|+V5P!NOa6)hZ3B4v4Q6;%afVe(l@;p%|ODWHS5 z7%E&)P<^|xIsIc%LJ)9NDa;EIPX@QiE-kBC+#@phHq>@5uMR@0WgM0r>xR(^d^eL! zUd$OF~mwsv0TJ!0{c;n-VXv2#p_NOJQoXvW?U=a5wK;X$N}7GDKC!2}0LZXqBt zLyL<+2!uAl0o^($+Pt8x+v^vkmB60Tn8tL35RMoN11L7mY-Do~o_QtPVqxeL^~X^I zJ}poV7&sfl(wwP{(mu(X%ux*EUViJX1`0p9cJD&x`7D|7;!iu!`fz-Rzc3=%~sJWLmlXgyd=vjcYp5EF*E z0T>jpqu~TXi!Ev_&aPZA`XCZyNSGx+*`R9-10AFitu_I*;5?^8mQx+!4hxG_yufsz zqH9k_wMHEQb0=l+-T>)O2C9Qlr*lxOusrA?^_v}p%n@8fJzVh!D@28=@3AuL1J3E; zk((6(E5aNz*f1PAuux+xOSmjB=`_4vu|+oMe#!BVnOchaDh@9W4#CCV8V6VWV@w zhgPMi<3~{|G}FjXWM+`busOddgk`>qcqI-+pQc!l(ivUl;xe`xYdNEb-r(1ZN0N$D z0Ow1R?$`B|(uL$&l^mr-o+i^&G1SBoTZwL_g&8AX4pNXEkDIMTP}rO{LAIUY67_hu z^Q5N`hr*7XBuZo^xm-Xi6SbgNP{Gqn=L7+Ni8rM_vO2aR%q)nF0JcJ%9Z(YVnu|fJ z<9k*YE7-QB2pMG~@jC=bff_*ro<96uibzcSS~9Fs0OU5rCo~061qwraj|6P`{V8FJ z_}h?RPB7FDGtmwR-3$e}v5=h(JU~w3qR?{J=V+nJiW=?}8^1#bTv19e5d|+Nl%bOL zNLG>3D*!I`?!|4A5Mp9t0(ptylOMrIluVLb2B`sEE`4^5CFpuxq2*Wai>_tYQIjVU zBPJsN@L9wYgX=a`YRcdP0a>*^2Q?YXBE9I4mDw(Vju^@EwVLI^35K^w1AzI9IK>Fl zhVLs-E7BJ4!BS3N0>!$%N)QXim=53?9k}xGvjW-F;ERcN-7!`pI6ET;v9KyoC=IU^ zip#-l?+znYP?@a;Zb3yKWOlwX@MKLh9LovB3kq{XiR(eqOJaj6S0Pqt$fD$lE$@aZ zkeXML6zpyAvD38pMCbvoqM6D~FI*WkmYhK|`^f=hbPMfKs^%2SjZ`#1E}LUY7-5S9 zYX|&d(FHF|`VCbp%W@k90hP-#<0FnGbee+TXtCo*UzZt}$SPWVOId~~M`qZlb}@ZK z0p_I2PlUt_=egoB!84E$tUA|;{p9+A~3fjK>3Maz#W6CW6?Wo;g%g~Or- z-se1?8hxl>En)o}0VROZcksyWhb<@rIhxc6775c5`Dt<(a|^vY!65F&UfeH(!NQ(A zA7x2yJwuSP)$bD0S_r>q!T@CcFvSw}DLB&Us|=wRWtHM{iVrNUa$YcBpIF{5O0wO^ zfXRnBHA_{dlyzeWq=7I`x9ke1%?ZuwNRkcn8BaMouLZgxT)PbUx@4K@%-VP@E@p_q z;)+aa76W)?9?C!p*E3=Xkx`^#fg62+Sv5scZi)MbR)`rIv!-Z9U)QD#DR87jS1B-z z?WKgO{9J|QS;KX-1^Eu zSEZBE5zsXY!kA4q_JG3`oEal@Dr!yf26kMy8S4`~3gWQHQ6qx{SEVVAI6aHh6uH%b zl%OHSAc4G2oZ}P~32CK4r2!oPc$Nf&b`*qbeE8tN^jl(y=IH0%6)BfT08-pc6_RO~ z#;^*QHw{?%9u!p(bu^Pklv4j2fFQMdv#bVeda+z%BUj)bBL9D%@{+1ha^atb`Ui`~e8p!d3vCwG6~TAQZvs!w8Pg>8H)!0v}1C0AaH?2VQoW0?45B zOdP(snmK^YurnZvlO+}jEqEpzc_X(=u`D>Pa0-J|Xe|XrVQW-XoZ*O*N`grhO*R0o zVrjHUhMh)8i05WNFtMh2m&kMaRK#}b%$rjO1XP1Cc!b$NiTiXAgYwepG*I?MDTAu4 zPP{lS8Di(=KNC2o1DxhGHV8)Wf$b==dfG*)Vl+eBGB^klPjs=U)EV$-YD0;Lq5=Y4 zI!rohF`J~nn$@mNKoSBNuU24u2AEaYVTe>vRzz|HlnmSYqN6|< zVsn_;X9}36q9rgDb z!_kbI1`xbFK-5q?k&fImkpsR=)syb1Fewr9M4%Ph zRKN<6za6JyEv+CEkcedMDlMQE<`m{(>3XVQMaDBZ0vw$YOVoT0eE21h3tcn)EzBWE zDd-}oG}A}yl&leb51tIe93W9s3#eJuA!<*9&w>a#bX=R(I-%IP-m;L<_vpqVG7FRu zOG!w04v#{QBIcGQPnHzS0-Mm_F~AVV=ENvsL z!tGKqz_9als7NR5V`^{K?_~kX2BL3qU1|o$XbEbyS)R~RdSZox53@#^3%GBVYh4JB zcv5E;yEsHMCp31lArMoxGj+rjxVA=Ln87RtSN=AE7)}5K#DrJK?S&nAVvdNIF(b?%W84bY^LGotf!)JW2UjuTMW!;=$+X%G!_v?v_9VV4Hf$v*6W zq(u}6U@2REkVaOpeu*rW@iAe5BLvG8(Hb35nqZ`-c=e>D63umwQ%{D1E_zO?cw|-* zUgjw#&h`*!t?L2?FZ6{V=7%g|q_`9%EX<<(8BP+NQgN zkU@rx41r^Zx62Cv7?Mp#uIY~p557NwJ}`Q1{-AT*%@V>jt$uk`?c_=%ehBaTfBWoO z|JJ@gynQ^@Klc6pueFco{aT*)YoBL-z5nGsZ`b|Hr?-_I?-Z z_xD+A*W1VMFTZQ8_51AaKMsTWcKv(1{&&5@`~UTq>-qKk`(4)V;{9U1=RMx;Kku!# zhuvkcy?y_Fe|e8*->-kXzw~|&Yxn&w{|;|?|GU=yA9kPrx4eJWvTObC{~y-wKl_Wt z@9%nV*ZW<+YkBMK^L}?N`+nKCe%aq2Ec|-b{#$STde5%+e&71t|Mv#l@3U|H|9bDe z9c=#n@4w}L-{QTs*8k(Z#pCz)z4yC#%UZu{*ZZ~0?mvI{_s_5QZ>`;5`~JQCf4`Pp z_TR2w{{MG(|MCB8{rlZryx)KS{@2>I-Y@>~)?2&3|NIZOKkU9;yMEvE{_XF3*5R$U z@A2NR{r|W7`}W^^`}TL=TAp3MexKi8{M)ztmbZ3!tbe?;1FO?pn()|JMJtUyrxHc6Ytq-+%vp z|KI!l-Tlkm|NX;1-tO=3^6c-gKkR$VVf?E2mJ`~3P{Z};C?miLEUi+ykR z?LND9-(c_l{@eHLTD#Bx`v0}-f9ItpB(B*8W?&|K2};Saw-{5AW^oF1z?qW&iSj->!YXZ~Z&$ z^7ysu|M&OT@A~K8uiwMJ<*l{Wd++dm`StJZ|Gsy4)<6FL+5PpGZ+ZLw@7LS4&%XU- zf9-YWtul=>w`?Wla{r$)8@Bc54eJuXHJ^Ze>-v7T{cK7$$b-~F}w`}qB} z{QvHD|6~7mch~xT|M-8u>#_V`vj6t?{r>OzUG|rM|9jZ|H`sWK z_w28K|GWS1-&%IPwfp?;TlSy#|9Jj??ef2V{r~U3eY^PG$6ITc-Tmi1{@4Fu-@a$p zul?WkZ+H1$`_J#&^~@d1Ywg?L{e8Rt?|1G0KKA#2|KGBUeQ(*r z{<3d(E&rDH|GN(V@1JMwvj2Yl|L#BU{Vw*|-T%endAo1lzPAqhSkGI#`+v>;VcE63 zwbt(L`)9H1|GoeJwVt*6c6afMKOFx5m)(E7-QQii>)H1Xe|W!#W$nLTzyG^$mtXAv z+kcP$-QD%r_t)|6hD)V;UA`}@3K|G(bu z?yq%NetB=-;_WYM-!E(bFED@q|9{{A?*6`gZ{NSSlbB!xi&}xOggYn7#&F>tx->zs0ye(-yY09pC*krO1M&RZg(WGVv;T~;829~7u8|FD4+}(VLGTW zD7rMLK+I4ZgmR73Op-weP7oV_4yv6vd+=mQM2r)DiKptkQb7I+#?zsK*#1O&cX+qX-xiBlQ?DAR$bkMWF^)j|UtHdSu~(wpZH;ODi5C z+(_~0At+VSs$`@x!8mv=F+$ZQCCx#mj^S0YYR>FW4SL zCl70$m-G#Xpb(e!o7OaP*fF)~=ayew_DvZC9EYR!fK>1nu15QOD9%*0;dwlQo6d>J z&Ku;`qV(ZXilFLAoUOBzukfA24Umv)eGM^g&sUKC1=Q9+h?VP)0@ca?yE4)6Jd&Hh UT?4*EbSI$4ZUoZ(Ew9V}0h#z%wg3PC diff --git a/tests/v2/fixture/test_format_compatibility/array_12/compressor_4/1 b/tests/v2/fixture/test_format_compatibility/array_12/compressor_4/1 deleted file mode 100644 index f1721ae903b7f29cbf894a3552762aaf58e0161c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5380 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 diff --git a/tests/v2/fixture/test_format_compatibility/array_13/.zgroup b/tests/v2/fixture/test_format_compatibility/array_13/.zgroup deleted file mode 100644 index 3b7daf227c..0000000000 --- a/tests/v2/fixture/test_format_compatibility/array_13/.zgroup +++ /dev/null @@ -1,3 +0,0 @@ -{ - "zarr_format": 2 -} \ No newline at end of file diff --git a/tests/v2/fixture/test_format_compatibility/array_13/compressor_0/.zarray b/tests/v2/fixture/test_format_compatibility/array_13/compressor_0/.zarray deleted file mode 100644 index 7170ee6829..0000000000 --- a/tests/v2/fixture/test_format_compatibility/array_13/compressor_0/.zarray +++ /dev/null @@ -1,14 +0,0 @@ -{ - "chunks": [ - 3000 - ], - "compressor": null, - "dtype": "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 N64(;h68L`#{14*XEKUFb diff --git a/tests/v2/fixture/test_format_compatibility/array_13/compressor_1/.zarray b/tests/v2/fixture/test_format_compatibility/array_13/compressor_1/.zarray deleted file mode 100644 index 02cd2d7491..0000000000 --- a/tests/v2/fixture/test_format_compatibility/array_13/compressor_1/.zarray +++ /dev/null @@ -1,17 +0,0 @@ -{ - "chunks": [ - 3000 - ], - "compressor": { - "id": "zlib", - "level": 1 - }, - "dtype": "Lh!kc^ZOszZ`dA|pj9kx0KvsEiaMrBrs5tn9M) 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