a1o commited on
Commit
7f681ae
·
verified ·
1 Parent(s): 1e5b576

Upload folder using huggingface_hub (part 19)

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .venv/lib/python3.11/site-packages/zarr-3.1.6.dist-info/INSTALLER +1 -0
  2. .venv/lib/python3.11/site-packages/zarr-3.1.6.dist-info/METADATA +171 -0
  3. .venv/lib/python3.11/site-packages/zarr-3.1.6.dist-info/RECORD +103 -0
  4. .venv/lib/python3.11/site-packages/zarr-3.1.6.dist-info/REQUESTED +0 -0
  5. .venv/lib/python3.11/site-packages/zarr-3.1.6.dist-info/WHEEL +4 -0
  6. .venv/lib/python3.11/site-packages/zarr-3.1.6.dist-info/entry_points.txt +5 -0
  7. .venv/lib/python3.11/site-packages/zarr-3.1.6.dist-info/licenses/LICENSE.txt +21 -0
  8. .venv/lib/python3.11/site-packages/zarr/core/attributes.py +57 -0
  9. .venv/lib/python3.11/site-packages/zarr/core/buffer/__init__.py +21 -0
  10. .venv/lib/python3.11/site-packages/zarr/core/buffer/__pycache__/__init__.cpython-311.pyc +0 -0
  11. .venv/lib/python3.11/site-packages/zarr/core/buffer/__pycache__/core.cpython-311.pyc +0 -0
  12. .venv/lib/python3.11/site-packages/zarr/core/buffer/__pycache__/cpu.cpython-311.pyc +0 -0
  13. .venv/lib/python3.11/site-packages/zarr/core/buffer/__pycache__/gpu.cpython-311.pyc +0 -0
  14. .venv/lib/python3.11/site-packages/zarr/core/buffer/core.py +586 -0
  15. .venv/lib/python3.11/site-packages/zarr/core/buffer/cpu.py +238 -0
  16. .venv/lib/python3.11/site-packages/zarr/core/buffer/gpu.py +237 -0
  17. .venv/lib/python3.11/site-packages/zarr/core/chunk_grids.py +306 -0
  18. .venv/lib/python3.11/site-packages/zarr/core/chunk_key_encodings.py +129 -0
  19. .venv/lib/python3.11/site-packages/zarr/core/codec_pipeline.py +562 -0
  20. .venv/lib/python3.11/site-packages/zarr/core/common.py +254 -0
  21. .venv/lib/python3.11/site-packages/zarr/core/config.py +153 -0
  22. .venv/lib/python3.11/site-packages/zarr/core/dtype/__init__.py +281 -0
  23. .venv/lib/python3.11/site-packages/zarr/core/dtype/__pycache__/__init__.cpython-311.pyc +0 -0
  24. .venv/lib/python3.11/site-packages/zarr/core/dtype/__pycache__/common.cpython-311.pyc +0 -0
  25. .venv/lib/python3.11/site-packages/zarr/core/dtype/__pycache__/registry.cpython-311.pyc +0 -0
  26. .venv/lib/python3.11/site-packages/zarr/core/dtype/__pycache__/wrapper.cpython-311.pyc +0 -0
  27. .venv/lib/python3.11/site-packages/zarr/core/dtype/common.py +233 -0
  28. .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__init__.py +0 -0
  29. .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/__init__.cpython-311.pyc +0 -0
  30. .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/bool.cpython-311.pyc +0 -0
  31. .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/bytes.cpython-311.pyc +0 -0
  32. .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/common.cpython-311.pyc +0 -0
  33. .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/complex.cpython-311.pyc +0 -0
  34. .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/float.cpython-311.pyc +0 -0
  35. .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/int.cpython-311.pyc +0 -0
  36. .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/string.cpython-311.pyc +0 -0
  37. .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/structured.cpython-311.pyc +0 -0
  38. .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/time.cpython-311.pyc +0 -0
  39. .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/bool.py +319 -0
  40. .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/bytes.py +1279 -0
  41. .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/common.py +581 -0
  42. .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/complex.py +418 -0
  43. .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/float.py +435 -0
  44. .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/int.py +1521 -0
  45. .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/string.py +825 -0
  46. .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/structured.py +477 -0
  47. .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/time.py +885 -0
  48. .venv/lib/python3.11/site-packages/zarr/core/dtype/registry.py +212 -0
  49. .venv/lib/python3.11/site-packages/zarr/core/dtype/wrapper.py +300 -0
  50. .venv/lib/python3.11/site-packages/zarr/core/group.py +0 -0
.venv/lib/python3.11/site-packages/zarr-3.1.6.dist-info/INSTALLER ADDED
@@ -0,0 +1 @@
 
 
1
+ uv
.venv/lib/python3.11/site-packages/zarr-3.1.6.dist-info/METADATA ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Metadata-Version: 2.4
2
+ Name: zarr
3
+ Version: 3.1.6
4
+ Summary: An implementation of chunked, compressed, N-dimensional arrays for Python
5
+ Project-URL: issues, https://github.com/zarr-developers/zarr-python/issues
6
+ Project-URL: changelog, https://zarr.readthedocs.io/en/stable/release-notes.html
7
+ Project-URL: Discussions, https://github.com/zarr-developers/zarr-python/discussions
8
+ Project-URL: documentation, https://zarr.readthedocs.io/
9
+ Project-URL: homepage, https://github.com/zarr-developers/zarr-python
10
+ Author-email: Alistair Miles <alimanfoo@googlemail.com>
11
+ Maintainer: jakirkham, Norman Rzepka, Ryan Abernathey, David Stansby, Deepak Cherian
12
+ Maintainer-email: Davis Bennett <davis.v.bennett@gmail.com>, Josh Moore <josh@openmicroscopy.org>, Joe Hamman <joe@earthmover.io>, Juan Nunez-Iglesias <juan.nunez-iglesias@monash.edu>, Martin Durant <mdurant@anaconda.com>, Tom Augspurger <tom.w.augspurger@gmail.com>
13
+ License-Expression: MIT
14
+ License-File: LICENSE.txt
15
+ Keywords: Python,compressed,ndimensional-arrays,zarr
16
+ Classifier: Development Status :: 6 - Mature
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: Intended Audience :: Information Technology
19
+ Classifier: Intended Audience :: Science/Research
20
+ Classifier: Operating System :: Unix
21
+ Classifier: Programming Language :: Python
22
+ Classifier: Programming Language :: Python :: 3
23
+ Classifier: Programming Language :: Python :: 3.11
24
+ Classifier: Programming Language :: Python :: 3.12
25
+ Classifier: Programming Language :: Python :: 3.13
26
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
+ Requires-Python: >=3.11
28
+ Requires-Dist: donfig>=0.8
29
+ Requires-Dist: google-crc32c>=1.5
30
+ Requires-Dist: numcodecs>=0.14
31
+ Requires-Dist: numpy>=2.0
32
+ Requires-Dist: packaging>=22.0
33
+ Requires-Dist: typing-extensions>=4.12
34
+ Provides-Extra: cli
35
+ Requires-Dist: typer; extra == 'cli'
36
+ Provides-Extra: gpu
37
+ Requires-Dist: cupy-cuda12x; extra == 'gpu'
38
+ Provides-Extra: optional
39
+ Requires-Dist: universal-pathlib; extra == 'optional'
40
+ Provides-Extra: remote
41
+ Requires-Dist: fsspec>=2023.10.0; extra == 'remote'
42
+ Requires-Dist: obstore>=0.5.1; extra == 'remote'
43
+ Description-Content-Type: text/markdown
44
+
45
+ <div align="center">
46
+ <img src="https://raw.githubusercontent.com/zarr-developers/community/main/logos/logo2.png"><br>
47
+ </div>
48
+
49
+ # Zarr
50
+
51
+ <table>
52
+ <tr>
53
+ <td>Latest Release</td>
54
+ <td>
55
+ <a href="https://pypi.org/project/zarr/">
56
+ <img src="https://badge.fury.io/py/zarr.svg" alt="latest release" />
57
+ </a>
58
+ </td>
59
+ </tr>
60
+ <tr>
61
+ <td></td>
62
+ <td>
63
+ <a href="https://anaconda.org/anaconda/zarr/">
64
+ <img src="https://anaconda.org/conda-forge/zarr/badges/version.svg" alt="latest release" />
65
+ </a>
66
+ </td>
67
+ </tr>
68
+ <tr>
69
+ <td>Package Status</td>
70
+ <td>
71
+ <a href="https://pypi.org/project/zarr/">
72
+ <img src="https://img.shields.io/pypi/status/zarr.svg" alt="status" />
73
+ </a>
74
+ </td>
75
+ </tr>
76
+ <tr>
77
+ <td>License</td>
78
+ <td>
79
+ <a href="https://github.com/zarr-developers/zarr-python/blob/main/LICENSE.txt">
80
+ <img src="https://img.shields.io/pypi/l/zarr.svg" alt="license" />
81
+ </a>
82
+ </td>
83
+ </tr>
84
+ <tr>
85
+ <td>Build Status</td>
86
+ <td>
87
+ <a href="https://github.com/zarr-developers/zarr-python/blob/main/.github/workflows/python-package.yml">
88
+ <img src="https://github.com/zarr-developers/zarr-python/actions/workflows/python-package.yml/badge.svg" alt="build status" />
89
+ </a>
90
+ </td>
91
+ </tr>
92
+ <tr>
93
+ <td>Pre-commit Status</td>
94
+ <td>
95
+ <a href="https://github.com/zarr-developers/zarr-python/blob/main/.pre-commit-config.yaml">
96
+ <img src="https://results.pre-commit.ci/badge/github/zarr-developers/zarr-python/main.svg" alt="pre-commit status" />
97
+ </a>
98
+ </td>
99
+ </tr>
100
+ <tr>
101
+ <td>Coverage</td>
102
+ <td>
103
+ <a href="https://codecov.io/gh/zarr-developers/zarr-python">
104
+ <img src="https://codecov.io/gh/zarr-developers/zarr-python/branch/main/graph/badge.svg" alt="coverage" />
105
+ </a>
106
+ </td>
107
+ </tr>
108
+ <tr>
109
+ <td>Downloads</td>
110
+ <td>
111
+ <a href="https://zarr.readthedocs.io">
112
+ <img src="https://pepy.tech/badge/zarr" alt="pypi downloads" />
113
+ </a>
114
+ </td>
115
+ </tr>
116
+ <tr>
117
+ <td>Developer Chat</td>
118
+ <td>
119
+ <a href="https://ossci.zulipchat.com/">
120
+ <img src="https://img.shields.io/badge/zulip-join_chat-brightgreen.svg" />
121
+ </a>
122
+ </td>
123
+ </tr>
124
+ <tr>
125
+ <td>Funding</td>
126
+ <td>
127
+ <a href="https://chanzuckerberg.com/eoss/">
128
+ <img src="https://img.shields.io/badge/funded%20by-EOSS-FF414B.svg?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PHBhdGggZD0iTTIyLjA5OCAyMS4wNzRjLTEuMjM1Ljg2LTIuNjggMS4zOTktNC42ODggMS40MzgtMi40MjYuMDUtNC4yMzgtMS41NTEtNC40MDYtMy45MThhNC40NjUgNC40NjUgMCAwIDEgMS4xODctMy4zOCA0LjQ4IDQuNDggMCAwIDEgMy4yODYtMS40NDQgNC42NzQgNC42NzQgMCAwIDEgMS44OTQuMzc1cy0uMTU2IDEuMzItLjIxIDEuOTE0bDEuOTg3LjAxNS4zNTYtMi45NzYtLjU2My0uMzU2YTYuNTQxIDYuNTQxIDAgMCAwLTMuNDg0LS45NjkgNi41OTMgNi41OTMgMCAwIDAtNC43NSAyLjA4NiA2LjQxNSA2LjQxNSAwIDAgMC0xLjcxNSA0Ljg3NWMuMTEzIDEuNjEuODA5IDMuMDc4IDEuOTUgNC4xNDEgMS4xNTYgMS4wNyAyLjcxNCAxLjY0NSA0LjM5OCAxLjYzMy4wMzkgMCAuMDc4IDAgLjEyNS0uMDA0YTkuOTE4IDkuOTE4IDAgMCAwIDMuNDMzLS43MjNsMS40MzQtMi43ODlzLS4wMjctLjA2Mi0uMjM0LjA3OCIgc3R5bGU9InN0cm9rZTpub25lO2ZpbGwtcnVsZTpub256ZXJvO2ZpbGw6I2ZmZjtmaWxsLW9wYWNpdHk6MSIvPjxwYXRoIGQ9Im0yOC44NjMgMjguMjE1LS4yNDIgMi43NDJ2LjAzMWMtLjEwMSAxLjAzNS0uNjUyIDEuOTE0LTEuNDg0IDIuMzUyLS42Ni4zNDctMS4zOC4zNDctMi4wMjgtLjAwOC0uNjc1LS4zNjMtLjk5Mi0uOTE4LTEuMTE3LTEuNjEzLS4xODctMS4xMDIuNDM4LTIuMjU0IDEuMjkzLTIuODMybDEzLjM1Mi04LjY4OGMuMDk3LjczOC4xNTIgMS40ODUuMTUyIDIuMjUgMCA4Ljk5Mi03LjMwOSAxNi4zMDUtMTYuMjkzIDE2LjMwNS04Ljk4OCAwLTE2LjI4OS03LjMxMy0xNi4yODktMTYuMzA1IDAtOC45ODggNy4zMDUtMTYuMyAxNi4yOTMtMTYuM3MxMi40OTYgNC4wOSAxNC45ODQgOS45MUwzOS4xMTQgMTVDMzYuMjYxIDguNjY0IDI5Ljg5MyA0LjIzNCAyMi41IDQuMjM0Yy03LjM5NSAwLTE4LjIxNSA4LjE3Mi0xOC4yMTUgMTguMjE1IDAgMTAuMDQ3IDguMTcyIDE4LjIxMSAxOC4yMTEgMTguMjExIDEwLjA0IDAgMTguMjE1LTguMTcyIDE4LjIxNS0xOC4yMSAwLTEwLjA0LS4xMS0yLjI5NC0uMzEzLTMuMzkxYTE5Ljk5NyAxOS45OTcgMCAwIDAtLjQ1My0xLjgzMmwtMy43OTMgMi4zNy01LjAyIDMuMThjLS4xNzUtLjY2OC0uNTgxLTEuMzQzLTEuNDQtMS43My0xLjAxMi0uNDY1LTIuNjYtLjExLTMuODY4LjU4MiAwIDAgMy4zMDUtNi40MDIgMy44Ni03LjQ1LjAzNS0uMDY2LS4wMTYtLjE0OC0uMDk4LS4xNDhoLTYuMThsLS4yNSAyLjA2N0gyNi41MDhsLTQuNzMgOS4wNjJjLS4wOTQuMTc2LjA5Ny4zNjMuMjc3LjI3NGwyLjUyMy0xLjM0YzEuMjE5LS42MjUgMy4yNS0xLjkxIDQuMjU0LTEuMTY4LjE0NS4xMS4zMzIuMzk4LjM0OC42OTVhLjM1Ny4zNTcgMCAwIDEtLjE0OS4yOTNsLTUuMDQzIDMuNDA2Yy0xLjYzNiAxLjE2OC0yLjI3NyAyLjkxLTIuMTIgNC41NTUuMTI0IDEuMzc1Ljk4NCAyLjU2NiAyLjI1NyAzLjI2MmE0LjE3IDQuMTcgMCAwIDAgMi4xMTMuNTE1IDQuMTY5IDQuMTY5IDAgMCAwIDEuODY0LS41YzEuNDM3LS43NTQgMi4zOTQtMi4yMzQgMi41NjItMy45NDVsLjQwMi00LjYxMy0yLjE5OSAxLjYzM1ptMCAwIiBzdHlsZT0ic3Ryb2tlOm5vbmU7ZmlsbC1ydWxlOm5vbnplcm87ZmlsbDojZmZmO2ZpbGwtb3BhY2l0eToxIi8+PC9zdmc+" alt="CZI's Essential Open Source Software for Science" />
129
+ </a>
130
+ </td>
131
+ </tr>
132
+ <tr>
133
+ <td>Citation</td>
134
+ <td>
135
+ <a href="https://doi.org/10.5281/zenodo.3773450">
136
+ <img src="https://zenodo.org/badge/DOI/10.5281/zenodo.3773450.svg" alt="DOI" />
137
+ </a>
138
+ </td>
139
+ </tr>
140
+
141
+ </table>
142
+
143
+ ## What is it?
144
+
145
+ Zarr is a Python package providing an implementation of compressed, chunked, N-dimensional arrays, designed for use in parallel computing. See the [documentation](https://zarr.readthedocs.io) for more information.
146
+
147
+ ## Main Features
148
+
149
+ - [**Create**](https://zarr.readthedocs.io/en/stable/user-guide/arrays.html#creating-an-array) N-dimensional arrays with any NumPy `dtype`.
150
+ - [**Chunk arrays**](https://zarr.readthedocs.io/en/stable/user-guide/performance.html#chunk-optimizations) along any dimension.
151
+ - [**Compress**](https://zarr.readthedocs.io/en/stable/user-guide/arrays.html#compressors) and/or filter chunks using any NumCodecs codec.
152
+ - [**Store arrays**](https://zarr.readthedocs.io/en/stable/user-guide/storage.html) in memory, on disk, inside a zip file, on S3, etc...
153
+ - [**Read**](https://zarr.readthedocs.io/en/stable/user-guide/arrays.html#reading-and-writing-data) an array [**concurrently**](https://zarr.readthedocs.io/en/stable/user-guide/performance.html#parallel-computing-and-synchronization) from multiple threads or processes.
154
+ - [**Write**](https://zarr.readthedocs.io/en/stable/user-guide/arrays.html#reading-and-writing-data) to an array concurrently from multiple threads or processes.
155
+ - Organize arrays into hierarchies via [**groups**](https://zarr.readthedocs.io/en/stable/quickstart.html#hierarchical-groups).
156
+
157
+ ## Where to get it
158
+
159
+ Zarr can be installed from PyPI using `pip`:
160
+
161
+ ```bash
162
+ pip install zarr
163
+ ```
164
+
165
+ or via `conda`:
166
+
167
+ ```bash
168
+ conda install -c conda-forge zarr
169
+ ```
170
+
171
+ For more details, including how to install from source, see the [installation documentation](https://zarr.readthedocs.io/en/stable/index.html#installation).
.venv/lib/python3.11/site-packages/zarr-3.1.6.dist-info/RECORD ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ../../../bin/zarr,sha256=PG0LRLGAhLssz-2QBC3m0yp4HOpbKfCfrnf235ik8hE,342
2
+ zarr-3.1.6.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
3
+ zarr-3.1.6.dist-info/METADATA,sha256=-diAtnIVxk9WPannPXucRNDlga_53MNQVUfW6VAPRUM,8608
4
+ zarr-3.1.6.dist-info/RECORD,,
5
+ zarr-3.1.6.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ zarr-3.1.6.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
7
+ zarr-3.1.6.dist-info/entry_points.txt,sha256=87EDiIFOzr9bpH-HNPSQRDKM-p9unAZrBvybRDNCEcw,75
8
+ zarr-3.1.6.dist-info/licenses/LICENSE.txt,sha256=HXdW8z09og1TsWLC65vrqR7g3BrrRllyi23I8vdZQcs,1124
9
+ zarr/__init__.py,sha256=s8jh56_lxQLeevIDxX93X5wsmTOrx17NWoQ6rjQd24A,4270
10
+ zarr/_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ zarr/_cli/cli.py,sha256=uTI0eBKOkh8Lanr1DyX2HxgcWeVbtpVnSrYGis5Hg2E,5852
12
+ zarr/_compat.py,sha256=a6rDJz6Z7R4wcFgfkOw8BmgPb20CwO0tfE7fU1h4d6c,3589
13
+ zarr/_version.py,sha256=LCICTIfLZ__LkFNkrPDD-bSal5fP2QP0iQ5beTR8e2Y,704
14
+ zarr/abc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ zarr/abc/buffer.py,sha256=EHVr5Dwg759DzFEqwN2diAaNeUcXlF68lvyFBbaCJbA,196
16
+ zarr/abc/codec.py,sha256=GI34j_VwNVPMaIkyLqWryhKMPKDfD7-D-SkIkrDw5Qw,16290
17
+ zarr/abc/metadata.py,sha256=c1IkPTX3rEO_5pY9T05OH6uZbnhVvGHUV--GbMxpzoM,1405
18
+ zarr/abc/numcodec.py,sha256=pJbdF-NqsJZEu_TvuEZj3QJManR6wXNRCaVOTfLBa40,2814
19
+ zarr/abc/store.py,sha256=MkpqzxU7zb7cxrBAj_JGBX0lq-72k1Hrg0ajzaILYTo,23663
20
+ zarr/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ zarr/api/asynchronous.py,sha256=CFNu9K7_tfs0HXdJz-ApqrNNVZ1069qNRiSKulnVNb0,49204
22
+ zarr/api/synchronous.py,sha256=1MHbYAq6rGbThqa5HWBhCGZlwx34gD1PIi-CGEh3NFM,54557
23
+ zarr/buffer/__init__.py,sha256=qJ_P2iFiWwwW1EnNJzJnRlv0JWgZQR09OCYDOeOr6E4,283
24
+ zarr/buffer/cpu.py,sha256=ZMXfM3bgxk_Dar9R9BaNtt2oaF9D34p2_Bz_CHsgDYE,270
25
+ zarr/buffer/gpu.py,sha256=6CjqB7BFb4oG1QkqF1O2VF8lT3Set2i4PDPhgbWYXpw,137
26
+ zarr/codecs/__init__.py,sha256=QhbUyxk17cwe9Ag7Xc70xSuW35YyYrvG9QJ_idfrs78,3410
27
+ zarr/codecs/_v2.py,sha256=fwymGyufsW5dd6Ba2y_EF0oLRLSfw50Yy1p9wy0lzZE,3649
28
+ zarr/codecs/blosc.py,sha256=64fw9-OFH94Mcv8rNeymocRxTY5AMMkQGlerEshjyaY,11074
29
+ zarr/codecs/bytes.py,sha256=d7iMb0G4hb71Ll2H4S3PgjMwSw8Sdy1v_ssXhTgGenU,4183
30
+ zarr/codecs/crc32c_.py,sha256=2c5QCjLSbEgvbU3cWSrgM2HH9vxGkuHYL0afpsZzbg0,2572
31
+ zarr/codecs/gzip.py,sha256=1TjO91IVemCIfQ5BwMexzcNcvXAJmQXzfI0Qnw_FZhM,2503
32
+ zarr/codecs/numcodecs/__init__.py,sha256=9E4nPdDj10lgCEsqNlcfAg8X4MPmVk_VsnfOBM8d0bI,901
33
+ zarr/codecs/numcodecs/_codecs.py,sha256=P13EXUHc-o0Kqoj2zLuyrS8Dm-czpR1pXA1yn5BIZJY,11953
34
+ zarr/codecs/sharding.py,sha256=pDxta4FW4laLjsDDHwQS5E-rKf38H8msi4bkrLJzgZo,28162
35
+ zarr/codecs/transpose.py,sha256=ZluSDxmHBzIySLOvSmvcusqGTKHO-lUkiErrJVdEe3I,4463
36
+ zarr/codecs/vlen_utf8.py,sha256=CoOfIqma7THsLQOnlFSh60ulvk2vYDIMfe8sgnVa-KA,4536
37
+ zarr/codecs/zstd.py,sha256=groE9suVC8Imuzg5eprg4HXIfCwGp96s40NvXG9TK6Y,3324
38
+ zarr/convenience.py,sha256=ysKzQGUWaSey7I9LJMYhK3NSgJW0iRBoF8RI_FV0uYQ,800
39
+ zarr/core/__init__.py,sha256=vPyRTNVnQXIblFLBgdbcs9CBGsL6xa3RmEGrQbHVXQY,283
40
+ zarr/core/_info.py,sha256=yPWnlqPSryJXy2tCnyxiYt7spG4oSIuJJIMmCNw09eo,5015
41
+ zarr/core/_tree.py,sha256=WTv5vafZOTu4YIzEJe_WtSH7e59nx6zPfvrxsVnmSLU,5074
42
+ zarr/core/array.py,sha256=ff-XPTnzvGK3JNcn4uWf3MPtYzfInDY6Sncl9q-JTLM,222706
43
+ zarr/core/array_spec.py,sha256=URY0qzEz13wxoYSEfER8ofUUFqqZGa0f8ajJLI6oz5s,3855
44
+ zarr/core/attributes.py,sha256=wRNaIjvLMW8tgh1iw4dB1s08xvgEF4DMhW0a9HR81CE,1687
45
+ zarr/core/buffer/__init__.py,sha256=jnvcnaKdaWfOpmNPOdf4adFeYnh2gTaK-dFI0dVzJxc,421
46
+ zarr/core/buffer/core.py,sha256=IJxnBEY8RJsT6BxQ5LvyiXIhIsuym3MJKQuMqk3XzUs,18024
47
+ zarr/core/buffer/cpu.py,sha256=k2uLEcE10_bb3UPpOp_AGvA4aPCDT-17luF5zDr69o4,7690
48
+ zarr/core/buffer/gpu.py,sha256=4hCdSBtDzLWyyXeTehHSEqqjqQyrk6NAVOO6hT0D48Q,7777
49
+ zarr/core/chunk_grids.py,sha256=H0bwdTOsQH5okmpBZEE1yS-hhZ4FVV8I9nOnPoBdo7U,10655
50
+ zarr/core/chunk_key_encodings.py,sha256=s0NDx4bAjWHRE_Epk3EPJoZhQkHomasJNcrmX-o-dd0,4379
51
+ zarr/core/codec_pipeline.py,sha256=aYadeiOGNNfhIbSpiAKp9FbaPqlODr7geQYdWI-y0Ng,22003
52
+ zarr/core/common.py,sha256=AFi00SCRtsfRSxsqmjUWi9Z4s7NzYX8U5NO7qE8ikj4,7834
53
+ zarr/core/config.py,sha256=lWPBGQm4xrwQd9Woye7Ok0RRE-mjdfLARvE4KSWD1m4,6283
54
+ zarr/core/dtype/__init__.py,sha256=tO6uM5pxHie1tuEosYNRm5M9lMMi29gJeSQznoj0Dfc,9111
55
+ zarr/core/dtype/common.py,sha256=HIbGlU1Ja_3G7FcF3wuNuJkebu_Bn9seLRWKRCehukc,8651
56
+ zarr/core/dtype/npy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
+ zarr/core/dtype/npy/bool.py,sha256=HRUqa-DtUpTAm9nZcrOg7ikQ-tNy3AwHHn_yPbVy41c,8908
58
+ zarr/core/dtype/npy/bytes.py,sha256=7h_uA75HyX7uJ5frcgZMeeNsjoHnreUMvpElFVNDE9c,39979
59
+ zarr/core/dtype/npy/common.py,sha256=-mQSr44DuJ-Emp_enxPNIq3-E37Ia6Mj8WMHlPgwAu0,14314
60
+ zarr/core/dtype/npy/complex.py,sha256=o0-2hlqQx7rB2UhvaVj0JdawEw7QUWCYFXJlTXIhVL4,12928
61
+ zarr/core/dtype/npy/float.py,sha256=BCBZj9oLVjwpYZT3pb2aA-sBeWyfffHzOcXw_Xgu3GU,13631
62
+ zarr/core/dtype/npy/int.py,sha256=PbUz-OEQkdCb6X_K4soF1wVYvU7BWLF6mhYsTgOdCFw,47134
63
+ zarr/core/dtype/npy/string.py,sha256=IYsgr8asN8yYG_YOFbtLgk11iV8O27bZKG3dD4ZdkqM,24952
64
+ zarr/core/dtype/npy/structured.py,sha256=y2k6fJd8G9iVq6oF0neyBeygCazdWAS-8Z01ayI-Z0Q,15332
65
+ zarr/core/dtype/npy/time.py,sha256=8tTA2jMtLPMN1BHuibUjdjUmnboQ-4Z7Hy2r4O7iw8U,27921
66
+ zarr/core/dtype/registry.py,sha256=m-edO29HLCAdPdWpAy_Eph5L_YTS0kMsw2E2gk17e2E,7396
67
+ zarr/core/dtype/wrapper.py,sha256=xJvjMDWm0T_ArG3eD_qnzZ0IyYOm5m038EzovHu6-OU,9724
68
+ zarr/core/group.py,sha256=vLoumjmvibKg-0nFDQ_yiNzHWcHbSqrsGgHKMlCNzEI,150283
69
+ zarr/core/indexing.py,sha256=TynMb87bWmL6XJ31UzPn6PmpNitJxIyeL7U00yY-ST8,60057
70
+ zarr/core/metadata/__init__.py,sha256=ej8tBNY67cbFAQdjQo8akiyFgdnNFXtN5SzaRD0cDk4,539
71
+ zarr/core/metadata/common.py,sha256=r2w276Hnok9EWqb2D8uWKMqq7PhUKhc7PvQOX3W44SE,255
72
+ zarr/core/metadata/io.py,sha256=2EiPzMtqv-AD5xEIiZqepMuV34yiqR3vxSdNESXHxro,3194
73
+ zarr/core/metadata/v2.py,sha256=PJk-Tb2pl2cawRMbzNaHoA779LLAWKVWpe0FrKkxVy0,12557
74
+ zarr/core/metadata/v3.py,sha256=wIEDF621hb9brc_aSCJH8UVPwWripHK265n8duG2RZ8,17250
75
+ zarr/core/sync.py,sha256=YW6RRuupot1GbOBA7hXDfp3SsFRKyI8cKcTY2koPSSk,7319
76
+ zarr/core/sync_group.py,sha256=EC-s7osWK6Csj0yCTwaTV814pwi92XLm-LD5JYnMAyY,6063
77
+ zarr/creation.py,sha256=qkqT4ibcoE1ovVLvOD_m7ja_TVKJLhPsMc169M0A4Zo,802
78
+ zarr/dtype.py,sha256=6UirY7ewXg4Oa7BSLKuVB_klwrUWI3EY7AKkzGaIH8Q,1828
79
+ zarr/errors.py,sha256=9rGz46pr1VMQKCkWv65MRc2h5M0YlxWriHMEg17OlYU,3679
80
+ zarr/experimental/__init__.py,sha256=d5z1ssdeTxAhWIUFQ9c5vWSd4FlBJBDVeLhAGfXegZE,89
81
+ zarr/experimental/cache_store.py,sha256=_7rqe82js0ZZHZZoZXTMxAmdHeyGcyCxuywjIYbjw68,17282
82
+ zarr/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
83
+ zarr/metadata/migrate_v3.py,sha256=5IqCWcqDZQmNbRKdeTOzyyRY51uu9wqXXsx4DcEqVAY,10453
84
+ zarr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
+ zarr/registry.py,sha256=rbJnN7QZL-Vb1pQTyjGAFEDTTkVzQPdi1pKgFJhvZmI,11490
86
+ zarr/storage/__init__.py,sha256=u3WB2W2ufq-zveezvceyfMBtlbvsO8sQeBBmqNvIIis,1331
87
+ zarr/storage/_common.py,sha256=8vEsKZCT1nA4dMMD30emCTR1jkMwbCvzeS0iy2GAALk,22615
88
+ zarr/storage/_fsspec.py,sha256=FI9kJ-XC38ONObOACsJWQ68IURtqO5zj0OgDWZciZgw,14702
89
+ zarr/storage/_local.py,sha256=NChyhZkaPs4NNzMXLrYDAHEbQd7vkotZsnKrLSCY07Y,20278
90
+ zarr/storage/_logging.py,sha256=9rWvjpZLtvi0fs9Lu30YZ6EvwZqBNaucLO6WTpER1PU,7546
91
+ zarr/storage/_memory.py,sha256=OseWGwGvT6S_Lvg8eGoo4twjtOsaeV37_NdLgrmPONo,17582
92
+ zarr/storage/_obstore.py,sha256=8T17S05SrS80NOJUGauhpU8IPWo_FXyrmmpRh1ppr8w,17505
93
+ zarr/storage/_utils.py,sha256=FkoUnXY7wEZTHiL-bdnkThGqQRPhSbQp8csEVc4o6Ng,4969
94
+ zarr/storage/_wrapper.py,sha256=OBh6r1WNGeQqtYlxewrkN-JXJzoRvrkn40WOW3UG_ZQ,4876
95
+ zarr/storage/_zip.py,sha256=Yc2RKoNFhOKkQq4XBNvvyB3cuyQUsjjQRAViy1hGMSY,9232
96
+ zarr/testing/__init__.py,sha256=d1QaEs3zOnKTEQSDX1Z62HgsEEGQnjM7RjH3UU1Jkfg,436
97
+ zarr/testing/buffer.py,sha256=r2E0FMSomfPaXocxs0t07xotgoClEdkpYbH70v6hLig,2330
98
+ zarr/testing/conftest.py,sha256=IxBNMxVMC_PNze4KoSdnjWsMgrA8iU_aHBuiHJFq0lk,456
99
+ zarr/testing/stateful.py,sha256=TFQYgsGlqNcGkpt9PR_py8H9croxdkmryiJoYwQ_Yhs,24665
100
+ zarr/testing/store.py,sha256=Lo3gTsk33eIJUqZFHvcbVnZSXAobQ1c_QOeNbvHK9BM,27570
101
+ zarr/testing/strategies.py,sha256=GlFnSvJ76Y7ayslJXP6zygGiXczlJvRPc-mfDhpMmSU,15509
102
+ zarr/testing/utils.py,sha256=C0CMZsoDm4MfWWo00m2ZdH2JAmVZ4DrKvt3K5N7kGgs,1121
103
+ zarr/types.py,sha256=lSktZQyOx7yn-BKrJZvxdpP-SDQkrFBfBxUhJDyb1Bk,664
.venv/lib/python3.11/site-packages/zarr-3.1.6.dist-info/REQUESTED ADDED
File without changes
.venv/lib/python3.11/site-packages/zarr-3.1.6.dist-info/WHEEL ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
.venv/lib/python3.11/site-packages/zarr-3.1.6.dist-info/entry_points.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ [console_scripts]
2
+ zarr = zarr._cli.cli:app
3
+
4
+ [pytest11]
5
+ zarr = zarr.testing
.venv/lib/python3.11/site-packages/zarr-3.1.6.dist-info/licenses/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015-2025 Zarr Developers <https://github.com/zarr-developers>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
.venv/lib/python3.11/site-packages/zarr/core/attributes.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import MutableMapping
4
+ from typing import TYPE_CHECKING
5
+
6
+ from zarr.core.common import JSON
7
+
8
+ if TYPE_CHECKING:
9
+ from collections.abc import Iterator
10
+
11
+ from zarr.core.group import Group
12
+ from zarr.types import AnyArray
13
+
14
+
15
+ class Attributes(MutableMapping[str, JSON]):
16
+ def __init__(self, obj: AnyArray | Group) -> None:
17
+ # key=".zattrs", read_only=False, cache=True, synchronizer=None
18
+ self._obj = obj
19
+
20
+ def __getitem__(self, key: str) -> JSON:
21
+ return self._obj.metadata.attributes[key]
22
+
23
+ def __setitem__(self, key: str, value: JSON) -> None:
24
+ new_attrs = dict(self._obj.metadata.attributes)
25
+ new_attrs[key] = value
26
+ self._obj = self._obj.update_attributes(new_attrs)
27
+
28
+ def __delitem__(self, key: str) -> None:
29
+ new_attrs = dict(self._obj.metadata.attributes)
30
+ del new_attrs[key]
31
+ self.put(new_attrs)
32
+
33
+ def __iter__(self) -> Iterator[str]:
34
+ return iter(self._obj.metadata.attributes)
35
+
36
+ def __len__(self) -> int:
37
+ return len(self._obj.metadata.attributes)
38
+
39
+ def put(self, d: dict[str, JSON]) -> None:
40
+ """
41
+ Overwrite all attributes with the values from `d`.
42
+
43
+ Equivalent to the following pseudo-code, but performed atomically.
44
+
45
+ ```python
46
+ attrs = {"a": 1, "b": 2}
47
+ attrs.clear()
48
+ attrs.update({"a": "3", "c": 4})
49
+ print(attrs)
50
+ #> {'a': '3', 'c': 4}
51
+ ```
52
+ """
53
+ self._obj.metadata.attributes.clear()
54
+ self._obj = self._obj.update_attributes(d)
55
+
56
+ def asdict(self) -> dict[str, JSON]:
57
+ return dict(self._obj.metadata.attributes)
.venv/lib/python3.11/site-packages/zarr/core/buffer/__init__.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from zarr.core.buffer.core import (
2
+ ArrayLike,
3
+ Buffer,
4
+ BufferPrototype,
5
+ NDArrayLike,
6
+ NDArrayLikeOrScalar,
7
+ NDBuffer,
8
+ default_buffer_prototype,
9
+ )
10
+ from zarr.core.buffer.cpu import numpy_buffer_prototype
11
+
12
+ __all__ = [
13
+ "ArrayLike",
14
+ "Buffer",
15
+ "BufferPrototype",
16
+ "NDArrayLike",
17
+ "NDArrayLikeOrScalar",
18
+ "NDBuffer",
19
+ "default_buffer_prototype",
20
+ "numpy_buffer_prototype",
21
+ ]
.venv/lib/python3.11/site-packages/zarr/core/buffer/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (669 Bytes). View file
 
.venv/lib/python3.11/site-packages/zarr/core/buffer/__pycache__/core.cpython-311.pyc ADDED
Binary file (27.4 kB). View file
 
.venv/lib/python3.11/site-packages/zarr/core/buffer/__pycache__/cpu.cpython-311.pyc ADDED
Binary file (11.8 kB). View file
 
.venv/lib/python3.11/site-packages/zarr/core/buffer/__pycache__/gpu.cpython-311.pyc ADDED
Binary file (11.3 kB). View file
 
.venv/lib/python3.11/site-packages/zarr/core/buffer/core.py ADDED
@@ -0,0 +1,586 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+ from abc import ABC, abstractmethod
5
+ from collections.abc import Iterable
6
+ from typing import (
7
+ TYPE_CHECKING,
8
+ Any,
9
+ Literal,
10
+ NamedTuple,
11
+ Protocol,
12
+ SupportsIndex,
13
+ cast,
14
+ runtime_checkable,
15
+ )
16
+
17
+ import numpy as np
18
+ import numpy.typing as npt
19
+
20
+ if TYPE_CHECKING:
21
+ from collections.abc import Iterable, Sequence
22
+ from typing import Self
23
+
24
+ from zarr.codecs.bytes import Endian
25
+ from zarr.core.common import BytesLike
26
+
27
+ # Everything here is imported into ``zarr.core.buffer`` namespace.
28
+ __all__: list[str] = []
29
+
30
+
31
+ @runtime_checkable
32
+ class ArrayLike(Protocol):
33
+ """Protocol for the array-like type that underlie Buffer"""
34
+
35
+ @property
36
+ def dtype(self) -> np.dtype[Any]: ...
37
+
38
+ @property
39
+ def ndim(self) -> int: ...
40
+
41
+ @property
42
+ def size(self) -> int: ...
43
+
44
+ def __getitem__(self, key: slice) -> Self: ...
45
+
46
+ def __setitem__(self, key: slice, value: Any) -> None: ...
47
+
48
+
49
+ @runtime_checkable
50
+ class NDArrayLike(Protocol):
51
+ """Protocol for the nd-array-like type that underlie NDBuffer"""
52
+
53
+ @property
54
+ def dtype(self) -> np.dtype[Any]: ...
55
+
56
+ @property
57
+ def ndim(self) -> int: ...
58
+
59
+ @property
60
+ def size(self) -> int: ...
61
+
62
+ @property
63
+ def shape(self) -> tuple[int, ...]: ...
64
+
65
+ def __len__(self) -> int: ...
66
+
67
+ def __getitem__(self, key: slice) -> Self: ...
68
+
69
+ def __setitem__(self, key: slice, value: Any) -> None: ...
70
+
71
+ def __array__(self) -> npt.NDArray[Any]: ...
72
+
73
+ def reshape(
74
+ self, shape: tuple[int, ...] | Literal[-1], *, order: Literal["A", "C", "F"] = ...
75
+ ) -> Self: ...
76
+
77
+ def view(self, dtype: npt.DTypeLike) -> Self: ...
78
+
79
+ def astype(
80
+ self,
81
+ dtype: npt.DTypeLike,
82
+ order: Literal["K", "A", "C", "F"] = ...,
83
+ *,
84
+ copy: bool = ...,
85
+ ) -> Self: ...
86
+
87
+ def fill(self, value: Any) -> None: ...
88
+
89
+ def copy(self) -> Self: ...
90
+
91
+ def transpose(self, axes: SupportsIndex | Sequence[SupportsIndex] | None) -> Self: ...
92
+
93
+ def ravel(self, order: Literal["K", "A", "C", "F"] = ...) -> Self: ...
94
+
95
+ def all(self) -> bool: ...
96
+
97
+ def __eq__(self, other: object) -> Self: # type: ignore[override]
98
+ """Element-wise equal
99
+
100
+ Notes
101
+ -----
102
+ Type checkers such as mypy complains because the return type isn't a bool like
103
+ its supertype "object", which violates the Liskov substitution principle.
104
+ This is true, but since NumPy's ndarray is defined as an element-wise equal,
105
+ our hands are tied.
106
+ """
107
+
108
+
109
+ ScalarType = int | float | complex | bytes | str | bool | np.generic
110
+ NDArrayLikeOrScalar = ScalarType | NDArrayLike
111
+
112
+
113
+ def check_item_key_is_1d_contiguous(key: Any) -> None:
114
+ """Raises error if `key` isn't a 1d contiguous slice"""
115
+ if not isinstance(key, slice):
116
+ raise TypeError(
117
+ f"Item key has incorrect type (expected slice, got {key.__class__.__name__})"
118
+ )
119
+ if not (key.step is None or key.step == 1):
120
+ raise ValueError("slice must be contiguous")
121
+
122
+
123
+ class Buffer(ABC):
124
+ """A flat contiguous memory block
125
+
126
+ We use Buffer throughout Zarr to represent a contiguous block of memory.
127
+
128
+ A Buffer is backed by an underlying array-like instance that represents
129
+ the memory. The memory type is unspecified; can be regular host memory,
130
+ CUDA device memory, or something else. The only requirement is that the
131
+ array-like instance can be copied/converted to a regular Numpy array
132
+ (host memory).
133
+
134
+ Notes
135
+ -----
136
+ This buffer is untyped, so all indexing and sizes are in bytes.
137
+
138
+ Parameters
139
+ ----------
140
+ array_like
141
+ array-like object that must be 1-dim, contiguous, and byte dtype.
142
+ """
143
+
144
+ def __init__(self, array_like: ArrayLike) -> None:
145
+ if array_like.ndim != 1:
146
+ raise ValueError("array_like: only 1-dim allowed")
147
+ if array_like.dtype != np.dtype("B"):
148
+ raise ValueError("array_like: only byte dtype allowed")
149
+ self._data = array_like
150
+
151
+ @classmethod
152
+ @abstractmethod
153
+ def create_zero_length(cls) -> Self:
154
+ """Create an empty buffer with length zero
155
+
156
+ Returns
157
+ -------
158
+ New empty 0-length buffer
159
+ """
160
+ if cls is Buffer:
161
+ raise NotImplementedError("Cannot call abstract method on the abstract class 'Buffer'")
162
+ return cls(
163
+ cast("ArrayLike", None)
164
+ ) # This line will never be reached, but it satisfies the type checker
165
+
166
+ @classmethod
167
+ def from_array_like(cls, array_like: ArrayLike) -> Self:
168
+ """Create a new buffer of an array-like object
169
+
170
+ Parameters
171
+ ----------
172
+ array_like
173
+ array-like object that must be 1-dim, contiguous, and byte dtype.
174
+
175
+ Returns
176
+ -------
177
+ New buffer representing `array_like`
178
+ """
179
+ return cls(array_like)
180
+
181
+ @classmethod
182
+ @abstractmethod
183
+ def from_buffer(cls, buffer: Buffer) -> Self:
184
+ """Create a new buffer of an existing Buffer
185
+
186
+ This is useful if you want to ensure that an existing buffer is
187
+ of the correct subclass of Buffer. E.g., MemoryStore uses this
188
+ to return a buffer instance of the subclass specified by its
189
+ BufferPrototype argument.
190
+
191
+ Typically, this only copies data if the data has to be moved between
192
+ memory types, such as from host to device memory.
193
+
194
+ Parameters
195
+ ----------
196
+ buffer
197
+ buffer object.
198
+
199
+ Returns
200
+ -------
201
+ A new buffer representing the content of the input buffer
202
+
203
+ Notes
204
+ -----
205
+ Subclasses of `Buffer` must override this method to implement
206
+ more optimal conversions that avoid copies where possible
207
+ """
208
+ if cls is Buffer:
209
+ raise NotImplementedError("Cannot call abstract method on the abstract class 'Buffer'")
210
+ return cls(
211
+ cast("ArrayLike", None)
212
+ ) # This line will never be reached, but it satisfies the type checker
213
+
214
+ @classmethod
215
+ @abstractmethod
216
+ def from_bytes(cls, bytes_like: BytesLike) -> Self:
217
+ """Create a new buffer of a bytes-like object (host memory)
218
+
219
+ Parameters
220
+ ----------
221
+ bytes_like
222
+ bytes-like object
223
+
224
+ Returns
225
+ -------
226
+ New buffer representing `bytes_like`
227
+ """
228
+ if cls is Buffer:
229
+ raise NotImplementedError("Cannot call abstract method on the abstract class 'Buffer'")
230
+ return cls(
231
+ cast("ArrayLike", None)
232
+ ) # This line will never be reached, but it satisfies the type checker
233
+
234
+ def as_array_like(self) -> ArrayLike:
235
+ """Returns the underlying array (host or device memory) of this buffer
236
+
237
+ This will never copy data.
238
+
239
+ Returns
240
+ -------
241
+ The underlying 1d array such as a NumPy or CuPy array.
242
+ """
243
+ return self._data
244
+
245
+ @abstractmethod
246
+ def as_numpy_array(self) -> npt.NDArray[Any]:
247
+ """Returns the buffer as a NumPy array (host memory).
248
+
249
+ Notes
250
+ -----
251
+ Might have to copy data, consider using `.as_array_like()` instead.
252
+
253
+ Returns
254
+ -------
255
+ NumPy array of this buffer (might be a data copy)
256
+ """
257
+ ...
258
+
259
+ def as_buffer_like(self) -> BytesLike:
260
+ """Returns the buffer as an object that implements the Python buffer protocol.
261
+
262
+ Notes
263
+ -----
264
+ Might have to copy data, since the implementation uses `.as_numpy_array()`.
265
+
266
+ Returns
267
+ -------
268
+ An object that implements the Python buffer protocol
269
+ """
270
+ return memoryview(self.as_numpy_array()) # type: ignore[arg-type]
271
+
272
+ def to_bytes(self) -> bytes:
273
+ """Returns the buffer as `bytes` (host memory).
274
+
275
+ Warnings
276
+ --------
277
+ Will always copy data, only use this method for small buffers such as metadata
278
+ buffers. If possible, use `.as_numpy_array()` or `.as_array_like()` instead.
279
+
280
+ Returns
281
+ -------
282
+ `bytes` of this buffer (data copy)
283
+ """
284
+ return bytes(self.as_numpy_array())
285
+
286
+ def __getitem__(self, key: slice) -> Self:
287
+ check_item_key_is_1d_contiguous(key)
288
+ return self.__class__(self._data.__getitem__(key))
289
+
290
+ def __setitem__(self, key: slice, value: Any) -> None:
291
+ check_item_key_is_1d_contiguous(key)
292
+ self._data.__setitem__(key, value)
293
+
294
+ def __len__(self) -> int:
295
+ return self._data.size
296
+
297
+ @abstractmethod
298
+ def combine(self, others: Iterable[Buffer]) -> Self:
299
+ """Concatenate many buffers"""
300
+ ...
301
+
302
+ def __add__(self, other: Buffer) -> Self:
303
+ """Concatenate two buffers"""
304
+ return self.combine([other])
305
+
306
+ def __eq__(self, other: object) -> bool:
307
+ # Another Buffer class can override this to choose a more efficient path
308
+ return isinstance(other, Buffer) and np.array_equal(
309
+ self.as_numpy_array(), other.as_numpy_array()
310
+ )
311
+
312
+
313
+ class NDBuffer:
314
+ """An n-dimensional memory block
315
+
316
+ We use NDBuffer throughout Zarr to represent a n-dimensional memory block.
317
+
318
+ An NDBuffer is backed by an underlying ndarray-like instance that represents
319
+ the memory. The memory type is unspecified; can be regular host memory,
320
+ CUDA device memory, or something else. The only requirement is that the
321
+ ndarray-like instance can be copied/converted to a regular Numpy array
322
+ (host memory).
323
+
324
+ Notes
325
+ -----
326
+ The two buffer classes Buffer and NDBuffer are very similar. In fact, Buffer
327
+ is a special case of NDBuffer where dim=1, stride=1, and dtype="B". However,
328
+ in order to use Python's type system to differentiate between the contiguous
329
+ Buffer and the n-dim (non-contiguous) NDBuffer, we keep the definition of the
330
+ two classes separate.
331
+
332
+ Parameters
333
+ ----------
334
+ array : ndarray_like
335
+ ndarray-like object that is convertible to a regular Numpy array.
336
+ """
337
+
338
+ def __init__(self, array: NDArrayLike) -> None:
339
+ self._data = array
340
+
341
+ @classmethod
342
+ @abstractmethod
343
+ def create(
344
+ cls,
345
+ *,
346
+ shape: Iterable[int],
347
+ dtype: npt.DTypeLike,
348
+ order: Literal["C", "F"] = "C",
349
+ fill_value: Any | None = None,
350
+ ) -> Self:
351
+ """Create a new buffer and its underlying ndarray-like object
352
+
353
+ Parameters
354
+ ----------
355
+ shape
356
+ The shape of the buffer and its underlying ndarray-like object
357
+ dtype
358
+ The datatype of the buffer and its underlying ndarray-like object
359
+ order
360
+ Whether to store multi-dimensional data in row-major (C-style) or
361
+ column-major (Fortran-style) order in memory.
362
+ fill_value
363
+ If not None, fill the new buffer with a scalar value.
364
+
365
+ Returns
366
+ -------
367
+ New buffer representing a new ndarray_like object
368
+
369
+ Notes
370
+ -----
371
+ A subclass can overwrite this method to create an ndarray-like object
372
+ other then the default Numpy array.
373
+ """
374
+ if cls is NDBuffer:
375
+ raise NotImplementedError(
376
+ "Cannot call abstract method on the abstract class 'NDBuffer'"
377
+ )
378
+ return cls(
379
+ cast("NDArrayLike", None)
380
+ ) # This line will never be reached, but it satisfies the type checker
381
+
382
+ @classmethod
383
+ def empty(
384
+ cls, shape: tuple[int, ...], dtype: npt.DTypeLike, order: Literal["C", "F"] = "C"
385
+ ) -> Self:
386
+ """
387
+ Create an empty buffer with the given shape, dtype, and order.
388
+
389
+ This method can be faster than ``NDBuffer.create`` because it doesn't
390
+ have to initialize the memory used by the underlying ndarray-like
391
+ object.
392
+
393
+ Parameters
394
+ ----------
395
+ shape
396
+ The shape of the buffer and its underlying ndarray-like object
397
+ dtype
398
+ The datatype of the buffer and its underlying ndarray-like object
399
+ order
400
+ Whether to store multi-dimensional data in row-major (C-style) or
401
+ column-major (Fortran-style) order in memory.
402
+
403
+ Returns
404
+ -------
405
+ buffer
406
+ New buffer representing a new ndarray_like object with empty data.
407
+
408
+ See Also
409
+ --------
410
+ NDBuffer.create
411
+ Create a new buffer with some initial fill value.
412
+ """
413
+ # Implementations should override this method if they have a faster way
414
+ # to allocate an empty buffer.
415
+ return cls.create(shape=shape, dtype=dtype, order=order)
416
+
417
+ @classmethod
418
+ def from_ndarray_like(cls, ndarray_like: NDArrayLike) -> Self:
419
+ """Create a new buffer of an ndarray-like object
420
+
421
+ Parameters
422
+ ----------
423
+ ndarray_like
424
+ ndarray-like object
425
+
426
+ Returns
427
+ -------
428
+ New buffer representing `ndarray_like`
429
+ """
430
+ return cls(ndarray_like)
431
+
432
+ @classmethod
433
+ @abstractmethod
434
+ def from_numpy_array(cls, array_like: npt.ArrayLike) -> Self:
435
+ """Create a new buffer of Numpy array-like object
436
+
437
+ Parameters
438
+ ----------
439
+ array_like
440
+ Object that can be coerced into a Numpy array
441
+
442
+ Returns
443
+ -------
444
+ New buffer representing `array_like`
445
+ """
446
+ if cls is NDBuffer:
447
+ raise NotImplementedError(
448
+ "Cannot call abstract method on the abstract class 'NDBuffer'"
449
+ )
450
+ return cls(
451
+ cast("NDArrayLike", None)
452
+ ) # This line will never be reached, but it satisfies the type checker
453
+
454
+ def as_ndarray_like(self) -> NDArrayLike:
455
+ """Returns the underlying array (host or device memory) of this buffer
456
+
457
+ This will never copy data.
458
+
459
+ Returns
460
+ -------
461
+ The underlying array such as a NumPy or CuPy array.
462
+ """
463
+ return self._data
464
+
465
+ @abstractmethod
466
+ def as_numpy_array(self) -> npt.NDArray[Any]:
467
+ """Returns the buffer as a NumPy array (host memory).
468
+
469
+ Warnings
470
+ --------
471
+ Might have to copy data, consider using `.as_ndarray_like()` instead.
472
+
473
+ Returns
474
+ -------
475
+ NumPy array of this buffer (might be a data copy)
476
+ """
477
+ ...
478
+
479
+ def as_scalar(self) -> ScalarType:
480
+ """Returns the buffer as a scalar value"""
481
+ if self._data.size != 1:
482
+ raise ValueError("Buffer does not contain a single scalar value")
483
+ return cast("ScalarType", self.as_numpy_array()[()])
484
+
485
+ @property
486
+ def dtype(self) -> np.dtype[Any]:
487
+ return self._data.dtype
488
+
489
+ @property
490
+ def shape(self) -> tuple[int, ...]:
491
+ return self._data.shape
492
+
493
+ @property
494
+ def byteorder(self) -> Endian:
495
+ from zarr.codecs.bytes import Endian
496
+
497
+ if self.dtype.byteorder == "<":
498
+ return Endian.little
499
+ elif self.dtype.byteorder == ">":
500
+ return Endian.big
501
+ else:
502
+ return Endian(sys.byteorder)
503
+
504
+ def reshape(self, newshape: tuple[int, ...] | Literal[-1]) -> Self:
505
+ return self.__class__(self._data.reshape(newshape))
506
+
507
+ def squeeze(self, axis: tuple[int, ...]) -> Self:
508
+ newshape = tuple(a for i, a in enumerate(self.shape) if i not in axis)
509
+ return self.__class__(self._data.reshape(newshape))
510
+
511
+ def astype(self, dtype: npt.DTypeLike, order: Literal["K", "A", "C", "F"] = "K") -> Self:
512
+ return self.__class__(self._data.astype(dtype=dtype, order=order))
513
+
514
+ @abstractmethod
515
+ def __getitem__(self, key: Any) -> Self: ...
516
+
517
+ @abstractmethod
518
+ def __setitem__(self, key: Any, value: Any) -> None: ...
519
+
520
+ def __len__(self) -> int:
521
+ return self._data.__len__()
522
+
523
+ def __repr__(self) -> str:
524
+ return f"<NDBuffer shape={self.shape} dtype={self.dtype} {self._data!r}>"
525
+
526
+ def all_equal(self, other: Any, equal_nan: bool = True) -> bool:
527
+ """Compare to `other` using np.array_equal."""
528
+ if other is None:
529
+ # Handle None fill_value for Zarr V2
530
+ return False
531
+ # Handle positive and negative zero by comparing bit patterns:
532
+ if (
533
+ np.asarray(other).dtype.kind == "f"
534
+ and other == 0.0
535
+ and self._data.dtype.kind not in ("U", "S", "T", "O", "V")
536
+ ):
537
+ _data, other = np.broadcast_arrays(self._data, np.asarray(other, self._data.dtype))
538
+ void_dtype = "V" + str(_data.dtype.itemsize)
539
+ return np.array_equal(_data.view(void_dtype), other.view(void_dtype))
540
+ # use array_equal to obtain equal_nan=True functionality
541
+ # Since fill-value is a scalar, isn't there a faster path than allocating a new array for fill value
542
+ # every single time we have to write data?
543
+ _data, other = np.broadcast_arrays(self._data, other)
544
+ return np.array_equal(
545
+ _data,
546
+ other,
547
+ equal_nan=equal_nan
548
+ if self._data.dtype.kind not in ("U", "S", "T", "O", "V")
549
+ else False,
550
+ )
551
+
552
+ def fill(self, value: Any) -> None:
553
+ self._data.fill(value)
554
+
555
+ def copy(self) -> Self:
556
+ return self.__class__(self._data.copy())
557
+
558
+ def transpose(self, axes: SupportsIndex | Sequence[SupportsIndex] | None) -> Self:
559
+ return self.__class__(self._data.transpose(axes))
560
+
561
+
562
+ class BufferPrototype(NamedTuple):
563
+ """Prototype of the Buffer and NDBuffer class
564
+
565
+ The protocol must be pickable.
566
+
567
+ Attributes
568
+ ----------
569
+ buffer
570
+ The Buffer class to use when Zarr needs to create new Buffer.
571
+ nd_buffer
572
+ The NDBuffer class to use when Zarr needs to create new NDBuffer.
573
+ """
574
+
575
+ buffer: type[Buffer]
576
+ nd_buffer: type[NDBuffer]
577
+
578
+
579
+ # The default buffer prototype used throughout the Zarr codebase.
580
+ def default_buffer_prototype() -> BufferPrototype:
581
+ from zarr.registry import (
582
+ get_buffer_class,
583
+ get_ndbuffer_class,
584
+ )
585
+
586
+ return BufferPrototype(buffer=get_buffer_class(), nd_buffer=get_ndbuffer_class())
.venv/lib/python3.11/site-packages/zarr/core/buffer/cpu.py ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from typing import (
4
+ TYPE_CHECKING,
5
+ Any,
6
+ Literal,
7
+ )
8
+
9
+ import numpy as np
10
+ import numpy.typing as npt
11
+
12
+ from zarr.core.buffer import core
13
+ from zarr.registry import (
14
+ register_buffer,
15
+ register_ndbuffer,
16
+ )
17
+
18
+ if TYPE_CHECKING:
19
+ from collections.abc import Callable, Iterable
20
+ from typing import Self
21
+
22
+ from zarr.core.buffer.core import ArrayLike, NDArrayLike
23
+ from zarr.core.common import BytesLike
24
+
25
+
26
+ class Buffer(core.Buffer):
27
+ """A flat contiguous memory block
28
+
29
+ We use Buffer throughout Zarr to represent a contiguous block of memory.
30
+
31
+ A Buffer is backed by an underlying array-like instance that represents
32
+ the memory. The memory type is unspecified; can be regular host memory,
33
+ CUDA device memory, or something else. The only requirement is that the
34
+ array-like instance can be copied/converted to a regular Numpy array
35
+ (host memory).
36
+
37
+ Notes
38
+ -----
39
+ This buffer is untyped, so all indexing and sizes are in bytes.
40
+
41
+ Parameters
42
+ ----------
43
+ array_like
44
+ array-like object that must be 1-dim, contiguous, and byte dtype.
45
+ """
46
+
47
+ def __init__(self, array_like: ArrayLike) -> None:
48
+ super().__init__(array_like)
49
+
50
+ @classmethod
51
+ def create_zero_length(cls) -> Self:
52
+ return cls(np.array([], dtype="B"))
53
+
54
+ @classmethod
55
+ def from_buffer(cls, buffer: core.Buffer) -> Self:
56
+ """Create a new buffer of an existing Buffer
57
+
58
+ This is useful if you want to ensure that an existing buffer is
59
+ of the correct subclass of Buffer. E.g., MemoryStore uses this
60
+ to return a buffer instance of the subclass specified by its
61
+ BufferPrototype argument.
62
+
63
+ Typically, this only copies data if the data has to be moved between
64
+ memory types, such as from host to device memory.
65
+
66
+ Parameters
67
+ ----------
68
+ buffer
69
+ buffer object.
70
+
71
+ Returns
72
+ -------
73
+ A new buffer representing the content of the input buffer
74
+
75
+ Notes
76
+ -----
77
+ Subclasses of `Buffer` must override this method to implement
78
+ more optimal conversions that avoid copies where possible
79
+ """
80
+ return cls.from_array_like(buffer.as_numpy_array())
81
+
82
+ @classmethod
83
+ def from_bytes(cls, bytes_like: BytesLike) -> Self:
84
+ """Create a new buffer of a bytes-like object (host memory)
85
+
86
+ Parameters
87
+ ----------
88
+ bytes_like
89
+ bytes-like object
90
+
91
+ Returns
92
+ -------
93
+ New buffer representing `bytes_like`
94
+ """
95
+ return cls.from_array_like(np.frombuffer(bytes_like, dtype="B"))
96
+
97
+ def as_numpy_array(self) -> npt.NDArray[Any]:
98
+ """Returns the buffer as a NumPy array (host memory).
99
+
100
+ Notes
101
+ -----
102
+ Might have to copy data, consider using `.as_array_like()` instead.
103
+
104
+ Returns
105
+ -------
106
+ NumPy array of this buffer (might be a data copy)
107
+ """
108
+ return np.asanyarray(self._data)
109
+
110
+ def combine(self, others: Iterable[core.Buffer]) -> Self:
111
+ data = [np.asanyarray(self._data)]
112
+ for buf in others:
113
+ other_array = buf.as_array_like()
114
+ assert other_array.dtype == np.dtype("B")
115
+ data.append(np.asanyarray(other_array))
116
+ return self.__class__(np.concatenate(data))
117
+
118
+
119
+ class NDBuffer(core.NDBuffer):
120
+ """An n-dimensional memory block
121
+
122
+ We use NDBuffer throughout Zarr to represent a n-dimensional memory block.
123
+
124
+ An NDBuffer is backed by an underlying ndarray-like instance that represents
125
+ the memory. The memory type is unspecified; can be regular host memory,
126
+ CUDA device memory, or something else. The only requirement is that the
127
+ ndarray-like instance can be copied/converted to a regular Numpy array
128
+ (host memory).
129
+
130
+ Notes
131
+ -----
132
+ The two buffer classes Buffer and NDBuffer are very similar. In fact, Buffer
133
+ is a special case of NDBuffer where dim=1, stride=1, and dtype="B". However,
134
+ in order to use Python's type system to differentiate between the contiguous
135
+ Buffer and the n-dim (non-contiguous) NDBuffer, we keep the definition of the
136
+ two classes separate.
137
+
138
+ Parameters
139
+ ----------
140
+ array
141
+ ndarray-like object that is convertible to a regular Numpy array.
142
+ """
143
+
144
+ def __init__(self, array: NDArrayLike) -> None:
145
+ super().__init__(array)
146
+
147
+ @classmethod
148
+ def create(
149
+ cls,
150
+ *,
151
+ shape: Iterable[int],
152
+ dtype: npt.DTypeLike,
153
+ order: Literal["C", "F"] = "C",
154
+ fill_value: Any | None = None,
155
+ ) -> Self:
156
+ # np.zeros is much faster than np.full, and therefore using it when possible is better.
157
+ if fill_value is None or (isinstance(fill_value, int) and fill_value == 0):
158
+ return cls(np.zeros(shape=tuple(shape), dtype=dtype, order=order))
159
+ else:
160
+ return cls(np.full(shape=tuple(shape), fill_value=fill_value, dtype=dtype, order=order))
161
+
162
+ @classmethod
163
+ def empty(
164
+ cls, shape: tuple[int, ...], dtype: npt.DTypeLike, order: Literal["C", "F"] = "C"
165
+ ) -> Self:
166
+ return cls(np.empty(shape=shape, dtype=dtype, order=order))
167
+
168
+ @classmethod
169
+ def from_numpy_array(cls, array_like: npt.ArrayLike) -> Self:
170
+ return cls.from_ndarray_like(np.asanyarray(array_like))
171
+
172
+ def as_numpy_array(self) -> npt.NDArray[Any]:
173
+ """Returns the buffer as a NumPy array (host memory).
174
+
175
+ Warnings
176
+ --------
177
+ Might have to copy data, consider using `.as_ndarray_like()` instead.
178
+
179
+ Returns
180
+ -------
181
+ NumPy array of this buffer (might be a data copy)
182
+ """
183
+ return np.asanyarray(self._data)
184
+
185
+ def __getitem__(self, key: Any) -> Self:
186
+ return self.__class__(np.asanyarray(self._data.__getitem__(key)))
187
+
188
+ def __setitem__(self, key: Any, value: Any) -> None:
189
+ if isinstance(value, NDBuffer):
190
+ value = value._data
191
+ self._data.__setitem__(key, value)
192
+
193
+
194
+ def as_numpy_array_wrapper(
195
+ func: Callable[[npt.NDArray[Any]], bytes], buf: core.Buffer, prototype: core.BufferPrototype
196
+ ) -> core.Buffer:
197
+ """Converts the input of `func` to a numpy array and the output back to `Buffer`.
198
+
199
+ This function is useful when calling a `func` that only support host memory such
200
+ as `GZip.decode` and `Blosc.decode`. In this case, use this wrapper to convert
201
+ the input `buf` to a Numpy array and convert the result back into a `Buffer`.
202
+
203
+ Parameters
204
+ ----------
205
+ func
206
+ The callable that will be called with the converted `buf` as input.
207
+ `func` must return bytes, which will be converted into a `Buffer`
208
+ before returned.
209
+ buf
210
+ The buffer that will be converted to a Numpy array before given as
211
+ input to `func`.
212
+ prototype
213
+ The prototype of the output buffer.
214
+
215
+ Returns
216
+ -------
217
+ The result of `func` converted to a `Buffer`
218
+ """
219
+ return prototype.buffer.from_bytes(func(buf.as_numpy_array()))
220
+
221
+
222
+ # CPU buffer prototype using numpy arrays
223
+ buffer_prototype = core.BufferPrototype(buffer=Buffer, nd_buffer=NDBuffer)
224
+ # default_buffer_prototype = buffer_prototype
225
+
226
+
227
+ # The numpy prototype used for E.g. when reading the shard index
228
+ def numpy_buffer_prototype() -> core.BufferPrototype:
229
+ return core.BufferPrototype(buffer=Buffer, nd_buffer=NDBuffer)
230
+
231
+
232
+ register_buffer(Buffer, qualname="zarr.buffer.cpu.Buffer")
233
+ register_ndbuffer(NDBuffer, qualname="zarr.buffer.cpu.NDBuffer")
234
+
235
+
236
+ # backwards compatibility
237
+ register_buffer(Buffer, qualname="zarr.core.buffer.cpu.Buffer")
238
+ register_ndbuffer(NDBuffer, qualname="zarr.core.buffer.cpu.NDBuffer")
.venv/lib/python3.11/site-packages/zarr/core/buffer/gpu.py ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import warnings
4
+ from typing import (
5
+ TYPE_CHECKING,
6
+ Any,
7
+ Literal,
8
+ cast,
9
+ )
10
+
11
+ import numpy as np
12
+ import numpy.typing as npt
13
+
14
+ from zarr.core.buffer import core
15
+ from zarr.core.buffer.core import ArrayLike, BufferPrototype, NDArrayLike
16
+ from zarr.errors import ZarrUserWarning
17
+ from zarr.registry import (
18
+ register_buffer,
19
+ register_ndbuffer,
20
+ )
21
+
22
+ if TYPE_CHECKING:
23
+ from collections.abc import Iterable
24
+ from typing import Self
25
+
26
+ from zarr.core.common import BytesLike
27
+
28
+ try:
29
+ import cupy as cp
30
+ except ImportError:
31
+ cp = None
32
+
33
+
34
+ class Buffer(core.Buffer):
35
+ """A flat contiguous memory block on the GPU
36
+
37
+ We use Buffer throughout Zarr to represent a contiguous block of memory.
38
+
39
+ A Buffer is backed by an underlying array-like instance that represents
40
+ the memory. The memory type is unspecified; can be regular host memory,
41
+ CUDA device memory, or something else. The only requirement is that the
42
+ array-like instance can be copied/converted to a regular Numpy array
43
+ (host memory).
44
+
45
+ Notes
46
+ -----
47
+ This buffer is untyped, so all indexing and sizes are in bytes.
48
+
49
+ Parameters
50
+ ----------
51
+ array_like
52
+ array-like object that must be 1-dim, contiguous, and byte dtype.
53
+ """
54
+
55
+ def __init__(self, array_like: ArrayLike) -> None:
56
+ if cp is None:
57
+ raise ImportError(
58
+ "Cannot use zarr.buffer.gpu.Buffer without cupy. Please install cupy."
59
+ )
60
+
61
+ if array_like.ndim != 1:
62
+ raise ValueError("array_like: only 1-dim allowed")
63
+ if array_like.dtype != np.dtype("B"):
64
+ raise ValueError("array_like: only byte dtype allowed")
65
+
66
+ if not hasattr(array_like, "__cuda_array_interface__"):
67
+ # Slow copy based path for arrays that don't support the __cuda_array_interface__
68
+ # TODO: Add a fast zero-copy path for arrays that support the dlpack protocol
69
+ msg = (
70
+ "Creating a zarr.buffer.gpu.Buffer with an array that does not support the "
71
+ "__cuda_array_interface__ for zero-copy transfers, "
72
+ "falling back to slow copy based path"
73
+ )
74
+ warnings.warn(
75
+ msg,
76
+ category=ZarrUserWarning,
77
+ stacklevel=2,
78
+ )
79
+ self._data = cp.asarray(array_like)
80
+
81
+ @classmethod
82
+ def create_zero_length(cls) -> Self:
83
+ """Create an empty buffer with length zero
84
+
85
+ Returns
86
+ -------
87
+ New empty 0-length buffer
88
+ """
89
+ return cls(cp.array([], dtype="B"))
90
+
91
+ @classmethod
92
+ def from_buffer(cls, buffer: core.Buffer) -> Self:
93
+ """Create a GPU Buffer given an arbitrary Buffer
94
+ This will try to be zero-copy if `buffer` is already on the
95
+ GPU and will trigger a copy if not.
96
+
97
+ Returns
98
+ -------
99
+ New GPU Buffer constructed from `buffer`
100
+ """
101
+ return cls(buffer.as_array_like())
102
+
103
+ @classmethod
104
+ def from_bytes(cls, bytes_like: BytesLike) -> Self:
105
+ return cls.from_array_like(cp.frombuffer(bytes_like, dtype="B"))
106
+
107
+ def as_numpy_array(self) -> npt.NDArray[Any]:
108
+ return cast("npt.NDArray[Any]", cp.asnumpy(self._data))
109
+
110
+ def combine(self, others: Iterable[core.Buffer]) -> Self:
111
+ data = [cp.asanyarray(self._data)]
112
+ for other in others:
113
+ other_array = other.as_array_like()
114
+ assert other_array.dtype == np.dtype("B")
115
+ gpu_other = Buffer(other_array)
116
+ gpu_other_array = gpu_other.as_array_like()
117
+ data.append(cp.asanyarray(gpu_other_array))
118
+ return self.__class__(cp.concatenate(data))
119
+
120
+
121
+ class NDBuffer(core.NDBuffer):
122
+ """A n-dimensional memory block on the GPU
123
+
124
+ We use NDBuffer throughout Zarr to represent a n-dimensional memory block.
125
+
126
+ An NDBuffer is backed by an underlying ndarray-like instance that represents
127
+ the memory. The memory type is unspecified; can be regular host memory,
128
+ CUDA device memory, or something else. The only requirement is that the
129
+ ndarray-like instance can be copied/converted to a regular Numpy array
130
+ (host memory).
131
+
132
+ Notes
133
+ -----
134
+ The two buffer classes Buffer and NDBuffer are very similar. In fact, Buffer
135
+ is a special case of NDBuffer where dim=1, stride=1, and dtype="B". However,
136
+ in order to use Python's type system to differentiate between the contiguous
137
+ Buffer and the n-dim (non-contiguous) NDBuffer, we keep the definition of the
138
+ two classes separate.
139
+
140
+ Parameters
141
+ ----------
142
+ array
143
+ ndarray-like object that is convertible to a regular Numpy array.
144
+ """
145
+
146
+ def __init__(self, array: NDArrayLike) -> None:
147
+ if cp is None:
148
+ raise ImportError(
149
+ "Cannot use zarr.buffer.gpu.NDBuffer without cupy. Please install cupy."
150
+ )
151
+
152
+ # assert array.ndim > 0
153
+ assert array.dtype != object
154
+ self._data = array
155
+
156
+ if not hasattr(array, "__cuda_array_interface__"):
157
+ # Slow copy based path for arrays that don't support the __cuda_array_interface__
158
+ # TODO: Add a fast zero-copy path for arrays that support the dlpack protocol
159
+ msg = (
160
+ "Creating a zarr.buffer.gpu.NDBuffer with an array that does not support the "
161
+ "__cuda_array_interface__ for zero-copy transfers, "
162
+ "falling back to slow copy based path"
163
+ )
164
+ warnings.warn(
165
+ msg,
166
+ stacklevel=2,
167
+ )
168
+ self._data = cp.asarray(array)
169
+
170
+ @classmethod
171
+ def create(
172
+ cls,
173
+ *,
174
+ shape: Iterable[int],
175
+ dtype: npt.DTypeLike,
176
+ order: Literal["C", "F"] = "C",
177
+ fill_value: Any | None = None,
178
+ ) -> Self:
179
+ ret = cls(cp.empty(shape=tuple(shape), dtype=dtype, order=order))
180
+ if fill_value is not None:
181
+ ret.fill(fill_value)
182
+ return ret
183
+
184
+ @classmethod
185
+ def empty(
186
+ cls, shape: tuple[int, ...], dtype: npt.DTypeLike, order: Literal["C", "F"] = "C"
187
+ ) -> Self:
188
+ return cls(cp.empty(shape=shape, dtype=dtype, order=order))
189
+
190
+ @classmethod
191
+ def from_numpy_array(cls, array_like: npt.ArrayLike) -> Self:
192
+ """Create a new buffer of Numpy array-like object
193
+
194
+ Parameters
195
+ ----------
196
+ array_like
197
+ Object that can be coerced into a Numpy array
198
+
199
+ Returns
200
+ -------
201
+ New buffer representing `array_like`
202
+ """
203
+ return cls(cp.asarray(array_like))
204
+
205
+ def as_numpy_array(self) -> npt.NDArray[Any]:
206
+ """Returns the buffer as a NumPy array (host memory).
207
+
208
+ Warnings
209
+ --------
210
+ Might have to copy data, consider using `.as_ndarray_like()` instead.
211
+
212
+ Returns
213
+ -------
214
+ NumPy array of this buffer (might be a data copy)
215
+ """
216
+ return cast("npt.NDArray[Any]", cp.asnumpy(self._data))
217
+
218
+ def __getitem__(self, key: Any) -> Self:
219
+ return self.__class__(self._data.__getitem__(key))
220
+
221
+ def __setitem__(self, key: Any, value: Any) -> None:
222
+ if isinstance(value, NDBuffer):
223
+ value = value._data
224
+ elif isinstance(value, core.NDBuffer):
225
+ gpu_value = NDBuffer(value.as_ndarray_like())
226
+ value = gpu_value._data
227
+ self._data.__setitem__(key, value)
228
+
229
+
230
+ buffer_prototype = BufferPrototype(buffer=Buffer, nd_buffer=NDBuffer)
231
+
232
+ register_buffer(Buffer, qualname="zarr.buffer.gpu.Buffer")
233
+ register_ndbuffer(NDBuffer, qualname="zarr.buffer.gpu.NDBuffer")
234
+
235
+ # backwards compatibility
236
+ register_buffer(Buffer, qualname="zarr.core.buffer.gpu.Buffer")
237
+ register_ndbuffer(NDBuffer, qualname="zarr.core.buffer.gpu.NDBuffer")
.venv/lib/python3.11/site-packages/zarr/core/chunk_grids.py ADDED
@@ -0,0 +1,306 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import itertools
4
+ import math
5
+ import numbers
6
+ import operator
7
+ import warnings
8
+ from abc import abstractmethod
9
+ from dataclasses import dataclass
10
+ from functools import reduce
11
+ from typing import TYPE_CHECKING, Any, Literal
12
+
13
+ import numpy as np
14
+
15
+ import zarr
16
+ from zarr.abc.metadata import Metadata
17
+ from zarr.core.common import (
18
+ JSON,
19
+ NamedConfig,
20
+ ShapeLike,
21
+ ceildiv,
22
+ parse_named_configuration,
23
+ parse_shapelike,
24
+ )
25
+ from zarr.errors import ZarrUserWarning
26
+
27
+ if TYPE_CHECKING:
28
+ from collections.abc import Iterator
29
+ from typing import Self
30
+
31
+ from zarr.core.array import ShardsLike
32
+
33
+
34
+ def _guess_chunks(
35
+ shape: tuple[int, ...] | int,
36
+ typesize: int,
37
+ *,
38
+ increment_bytes: int = 256 * 1024,
39
+ min_bytes: int = 128 * 1024,
40
+ max_bytes: int = 64 * 1024 * 1024,
41
+ ) -> tuple[int, ...]:
42
+ """
43
+ Iteratively guess an appropriate chunk layout for an array, given its shape and
44
+ the size of each element in bytes, and size constraints expressed in bytes. This logic is
45
+ adapted from h5py.
46
+
47
+ Parameters
48
+ ----------
49
+ shape : tuple[int, ...]
50
+ The chunk shape.
51
+ typesize : int
52
+ The size, in bytes, of each element of the chunk.
53
+ increment_bytes : int = 256 * 1024
54
+ The number of bytes used to increment or decrement the target chunk size in bytes.
55
+ min_bytes : int = 128 * 1024
56
+ The soft lower bound on the final chunk size in bytes.
57
+ max_bytes : int = 64 * 1024 * 1024
58
+ The hard upper bound on the final chunk size in bytes.
59
+
60
+ Returns
61
+ -------
62
+ tuple[int, ...]
63
+
64
+ """
65
+ if min_bytes >= max_bytes:
66
+ raise ValueError(f"Cannot have more min_bytes ({min_bytes}) than max_bytes ({max_bytes})")
67
+ if isinstance(shape, int):
68
+ shape = (shape,)
69
+
70
+ if typesize == 0:
71
+ return shape
72
+
73
+ ndims = len(shape)
74
+ # require chunks to have non-zero length for all dimensions
75
+ chunks = np.maximum(np.array(shape, dtype="=f8"), 1)
76
+
77
+ # Determine the optimal chunk size in bytes using a PyTables expression.
78
+ # This is kept as a float.
79
+ dset_size = np.prod(chunks) * typesize
80
+ target_size = increment_bytes * (2 ** np.log10(dset_size / (1024.0 * 1024)))
81
+
82
+ if target_size > max_bytes:
83
+ target_size = max_bytes
84
+ elif target_size < min_bytes:
85
+ target_size = min_bytes
86
+
87
+ idx = 0
88
+ while True:
89
+ # Repeatedly loop over the axes, dividing them by 2. Stop when:
90
+ # 1a. We're smaller than the target chunk size, OR
91
+ # 1b. We're within 50% of the target chunk size, AND
92
+ # 2. The chunk is smaller than the maximum chunk size
93
+
94
+ chunk_bytes = np.prod(chunks) * typesize
95
+
96
+ if (
97
+ chunk_bytes < target_size or abs(chunk_bytes - target_size) / target_size < 0.5
98
+ ) and chunk_bytes < max_bytes:
99
+ break
100
+
101
+ if np.prod(chunks) == 1:
102
+ break # Element size larger than max_bytes
103
+
104
+ chunks[idx % ndims] = math.ceil(chunks[idx % ndims] / 2.0)
105
+ idx += 1
106
+
107
+ return tuple(int(x) for x in chunks)
108
+
109
+
110
+ def normalize_chunks(chunks: Any, shape: tuple[int, ...], typesize: int) -> tuple[int, ...]:
111
+ """Convenience function to normalize the `chunks` argument for an array
112
+ with the given `shape`."""
113
+
114
+ # N.B., expect shape already normalized
115
+
116
+ # handle auto-chunking
117
+ if chunks is None or chunks is True:
118
+ return _guess_chunks(shape, typesize)
119
+
120
+ # handle no chunking
121
+ if chunks is False:
122
+ return shape
123
+
124
+ # handle 1D convenience form
125
+ if isinstance(chunks, numbers.Integral):
126
+ chunks = tuple(int(chunks) for _ in shape)
127
+
128
+ # handle dask-style chunks (iterable of iterables)
129
+ if all(isinstance(c, (tuple, list)) for c in chunks):
130
+ for i, c in enumerate(chunks):
131
+ if any(x != y for x, y in itertools.pairwise(c[:-1])) or (len(c) > 1 and c[-1] > c[0]):
132
+ raise ValueError(
133
+ f"Irregular chunk sizes in dimension {i}: {tuple(c)}. "
134
+ "Only uniform chunks (with an optional smaller final chunk) are supported."
135
+ )
136
+ chunks = tuple(c[0] for c in chunks)
137
+
138
+ # handle bad dimensionality
139
+ if len(chunks) > len(shape):
140
+ raise ValueError("too many dimensions in chunks")
141
+
142
+ # handle underspecified chunks
143
+ if len(chunks) < len(shape):
144
+ # assume chunks across remaining dimensions
145
+ chunks += shape[len(chunks) :]
146
+
147
+ # handle None or -1 in chunks
148
+ if -1 in chunks or None in chunks:
149
+ chunks = tuple(
150
+ s if c == -1 or c is None else int(c) for s, c in zip(shape, chunks, strict=False)
151
+ )
152
+
153
+ if not all(isinstance(c, numbers.Integral) for c in chunks):
154
+ raise TypeError("non integer value in chunks")
155
+
156
+ return tuple(int(c) for c in chunks)
157
+
158
+
159
+ @dataclass(frozen=True)
160
+ class ChunkGrid(Metadata):
161
+ @classmethod
162
+ def from_dict(cls, data: dict[str, JSON] | ChunkGrid | NamedConfig[str, Any]) -> ChunkGrid:
163
+ if isinstance(data, ChunkGrid):
164
+ return data
165
+
166
+ name_parsed, _ = parse_named_configuration(data)
167
+ if name_parsed == "regular":
168
+ return RegularChunkGrid._from_dict(data)
169
+ raise ValueError(f"Unknown chunk grid. Got {name_parsed}.")
170
+
171
+ @abstractmethod
172
+ def all_chunk_coords(self, array_shape: tuple[int, ...]) -> Iterator[tuple[int, ...]]:
173
+ pass
174
+
175
+ @abstractmethod
176
+ def get_nchunks(self, array_shape: tuple[int, ...]) -> int:
177
+ pass
178
+
179
+
180
+ @dataclass(frozen=True)
181
+ class RegularChunkGrid(ChunkGrid):
182
+ chunk_shape: tuple[int, ...]
183
+
184
+ def __init__(self, *, chunk_shape: ShapeLike) -> None:
185
+ chunk_shape_parsed = parse_shapelike(chunk_shape)
186
+
187
+ object.__setattr__(self, "chunk_shape", chunk_shape_parsed)
188
+
189
+ @classmethod
190
+ def _from_dict(cls, data: dict[str, JSON] | NamedConfig[str, Any]) -> Self:
191
+ _, configuration_parsed = parse_named_configuration(data, "regular")
192
+
193
+ return cls(**configuration_parsed) # type: ignore[arg-type]
194
+
195
+ def to_dict(self) -> dict[str, JSON]:
196
+ return {"name": "regular", "configuration": {"chunk_shape": tuple(self.chunk_shape)}}
197
+
198
+ def all_chunk_coords(self, array_shape: tuple[int, ...]) -> Iterator[tuple[int, ...]]:
199
+ return itertools.product(
200
+ *(range(ceildiv(s, c)) for s, c in zip(array_shape, self.chunk_shape, strict=False))
201
+ )
202
+
203
+ def get_nchunks(self, array_shape: tuple[int, ...]) -> int:
204
+ return reduce(
205
+ operator.mul,
206
+ itertools.starmap(ceildiv, zip(array_shape, self.chunk_shape, strict=True)),
207
+ 1,
208
+ )
209
+
210
+
211
+ def _guess_num_chunks_per_axis_shard(
212
+ chunk_shape: tuple[int, ...], item_size: int, max_bytes: int, array_shape: tuple[int, ...]
213
+ ) -> int:
214
+ """Generate the number of chunks per axis to hit a target max byte size for a shard.
215
+
216
+ For example, for a (2,2,2) chunk size and item size 4, maximum bytes of 256 would return 2.
217
+ In other words the shard would be a (2,2,2) grid of (2,2,2) chunks
218
+ i.e., prod(chunk_shape) * (returned_val * len(chunk_shape)) * item_size = 256 bytes.
219
+
220
+ Parameters
221
+ ----------
222
+ chunk_shape
223
+ The shape of the (inner) chunks.
224
+ item_size
225
+ The item size of the data i.e., 2 for uint16.
226
+ max_bytes
227
+ The maximum number of bytes per shard to allow.
228
+ array_shape
229
+ The shape of the underlying array.
230
+
231
+ Returns
232
+ -------
233
+ The number of chunks per axis.
234
+ """
235
+ bytes_per_chunk = np.prod(chunk_shape) * item_size
236
+ if max_bytes < bytes_per_chunk:
237
+ return 1
238
+ num_axes = len(chunk_shape)
239
+ chunks_per_shard = 1
240
+ # First check for byte size, second check to make sure we don't go bigger than the array shape
241
+ while (bytes_per_chunk * ((chunks_per_shard + 1) ** num_axes)) <= max_bytes and all(
242
+ c * (chunks_per_shard + 1) <= a for c, a in zip(chunk_shape, array_shape, strict=True)
243
+ ):
244
+ chunks_per_shard += 1
245
+ return chunks_per_shard
246
+
247
+
248
+ def _auto_partition(
249
+ *,
250
+ array_shape: tuple[int, ...],
251
+ chunk_shape: tuple[int, ...] | Literal["auto"],
252
+ shard_shape: ShardsLike | None,
253
+ item_size: int,
254
+ ) -> tuple[tuple[int, ...] | None, tuple[int, ...]]:
255
+ """
256
+ Automatically determine the shard shape and chunk shape for an array, given the shape and dtype of the array.
257
+ If `shard_shape` is `None` and the chunk_shape is "auto", the chunks will be set heuristically based
258
+ on the dtype and shape of the array.
259
+ If `shard_shape` is "auto", then the shard shape will be set heuristically from the dtype and shape
260
+ of the array; if the `chunk_shape` is also "auto", then the chunks will be set heuristically as well,
261
+ given the dtype and shard shape. Otherwise, the chunks will be returned as-is.
262
+ """
263
+ if shard_shape is None:
264
+ _shards_out: None | tuple[int, ...] = None
265
+ if chunk_shape == "auto":
266
+ _chunks_out = _guess_chunks(array_shape, item_size)
267
+ else:
268
+ _chunks_out = chunk_shape
269
+ else:
270
+ if chunk_shape == "auto":
271
+ # aim for a 1MiB chunk
272
+ _chunks_out = _guess_chunks(array_shape, item_size, max_bytes=1048576)
273
+ else:
274
+ _chunks_out = chunk_shape
275
+
276
+ if shard_shape == "auto":
277
+ warnings.warn(
278
+ "Automatic shard shape inference is experimental and may change without notice.",
279
+ ZarrUserWarning,
280
+ stacklevel=2,
281
+ )
282
+ _shards_out = ()
283
+ target_shard_size_bytes = zarr.config.get("array.target_shard_size_bytes", None)
284
+ num_chunks_per_shard_axis = (
285
+ _guess_num_chunks_per_axis_shard(
286
+ chunk_shape=_chunks_out,
287
+ item_size=item_size,
288
+ max_bytes=target_shard_size_bytes,
289
+ array_shape=array_shape,
290
+ )
291
+ if (has_auto_shard := (target_shard_size_bytes is not None))
292
+ else 2
293
+ )
294
+ for a_shape, c_shape in zip(array_shape, _chunks_out, strict=True):
295
+ # The previous heuristic was `a_shape // c_shape > 8` and now, with target_shard_size_bytes, we only check that the shard size is less than the array size.
296
+ can_shard_axis = a_shape // c_shape > 8 if not has_auto_shard else True
297
+ if can_shard_axis:
298
+ _shards_out += (c_shape * num_chunks_per_shard_axis,)
299
+ else:
300
+ _shards_out += (c_shape,)
301
+ elif isinstance(shard_shape, dict):
302
+ _shards_out = tuple(shard_shape["shape"])
303
+ else:
304
+ _shards_out = shard_shape
305
+
306
+ return _shards_out, _chunks_out
.venv/lib/python3.11/site-packages/zarr/core/chunk_key_encodings.py ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC, abstractmethod
4
+ from dataclasses import dataclass
5
+ from typing import TYPE_CHECKING, Any, ClassVar, Literal, TypeAlias, TypedDict, cast
6
+
7
+ if TYPE_CHECKING:
8
+ from typing import NotRequired, Self
9
+
10
+ from zarr.abc.metadata import Metadata
11
+ from zarr.core.common import (
12
+ JSON,
13
+ NamedConfig,
14
+ parse_named_configuration,
15
+ )
16
+ from zarr.registry import get_chunk_key_encoding_class, register_chunk_key_encoding
17
+
18
+ SeparatorLiteral = Literal[".", "/"]
19
+
20
+
21
+ def parse_separator(data: JSON) -> SeparatorLiteral:
22
+ if data not in (".", "/"):
23
+ raise ValueError(f"Expected an '.' or '/' separator. Got {data} instead.")
24
+ return cast("SeparatorLiteral", data)
25
+
26
+
27
+ class ChunkKeyEncodingParams(TypedDict):
28
+ name: Literal["v2", "default"]
29
+ separator: NotRequired[SeparatorLiteral]
30
+
31
+
32
+ @dataclass(frozen=True)
33
+ class ChunkKeyEncoding(ABC, Metadata):
34
+ """
35
+ Defines how chunk coordinates are mapped to store keys.
36
+
37
+ Subclasses must define a class variable `name` and implement `encode_chunk_key`.
38
+ """
39
+
40
+ name: ClassVar[str]
41
+
42
+ @classmethod
43
+ def from_dict(cls, data: dict[str, JSON]) -> Self:
44
+ _, config_parsed = parse_named_configuration(data, require_configuration=False)
45
+ return cls(**config_parsed or {})
46
+
47
+ def to_dict(self) -> dict[str, JSON]:
48
+ return {"name": self.name, "configuration": super().to_dict()}
49
+
50
+ def decode_chunk_key(self, chunk_key: str) -> tuple[int, ...]:
51
+ """
52
+ Optional: decode a chunk key string into chunk coordinates.
53
+ Not required for normal operation; override if needed for testing or debugging.
54
+ """
55
+ raise NotImplementedError(f"{self.__class__.__name__} does not implement decode_chunk_key.")
56
+
57
+ @abstractmethod
58
+ def encode_chunk_key(self, chunk_coords: tuple[int, ...]) -> str:
59
+ """
60
+ Encode chunk coordinates into a chunk key string.
61
+ Must be implemented by subclasses.
62
+ """
63
+
64
+
65
+ ChunkKeyEncodingLike: TypeAlias = (
66
+ dict[str, JSON] | ChunkKeyEncodingParams | ChunkKeyEncoding | NamedConfig[str, Any]
67
+ )
68
+
69
+
70
+ @dataclass(frozen=True)
71
+ class DefaultChunkKeyEncoding(ChunkKeyEncoding):
72
+ name: ClassVar[Literal["default"]] = "default"
73
+ separator: SeparatorLiteral = "/"
74
+
75
+ def __post_init__(self) -> None:
76
+ separator_parsed = parse_separator(self.separator)
77
+ object.__setattr__(self, "separator", separator_parsed)
78
+
79
+ def decode_chunk_key(self, chunk_key: str) -> tuple[int, ...]:
80
+ if chunk_key == "c":
81
+ return ()
82
+ return tuple(map(int, chunk_key[1:].split(self.separator)))
83
+
84
+ def encode_chunk_key(self, chunk_coords: tuple[int, ...]) -> str:
85
+ return self.separator.join(map(str, ("c",) + chunk_coords))
86
+
87
+
88
+ @dataclass(frozen=True)
89
+ class V2ChunkKeyEncoding(ChunkKeyEncoding):
90
+ name: ClassVar[Literal["v2"]] = "v2"
91
+ separator: SeparatorLiteral = "."
92
+
93
+ def __post_init__(self) -> None:
94
+ separator_parsed = parse_separator(self.separator)
95
+ object.__setattr__(self, "separator", separator_parsed)
96
+
97
+ def decode_chunk_key(self, chunk_key: str) -> tuple[int, ...]:
98
+ return tuple(map(int, chunk_key.split(self.separator)))
99
+
100
+ def encode_chunk_key(self, chunk_coords: tuple[int, ...]) -> str:
101
+ chunk_identifier = self.separator.join(map(str, chunk_coords))
102
+ return "0" if chunk_identifier == "" else chunk_identifier
103
+
104
+
105
+ def parse_chunk_key_encoding(data: ChunkKeyEncodingLike) -> ChunkKeyEncoding:
106
+ """
107
+ Take an implicit specification of a chunk key encoding and parse it into a ChunkKeyEncoding object.
108
+ """
109
+ if isinstance(data, ChunkKeyEncoding):
110
+ return data
111
+
112
+ # handle ChunkKeyEncodingParams
113
+ if "name" in data and "separator" in data:
114
+ data = {"name": data["name"], "configuration": {"separator": data["separator"]}} # type: ignore[typeddict-item]
115
+
116
+ # Now must be a named config
117
+ data = cast("dict[str, JSON]", data)
118
+
119
+ name_parsed, _ = parse_named_configuration(data, require_configuration=False)
120
+ try:
121
+ chunk_key_encoding = get_chunk_key_encoding_class(name_parsed).from_dict(data)
122
+ except KeyError as e:
123
+ raise ValueError(f"Unknown chunk key encoding: {e.args[0]!r}") from e
124
+
125
+ return chunk_key_encoding
126
+
127
+
128
+ register_chunk_key_encoding("default", DefaultChunkKeyEncoding)
129
+ register_chunk_key_encoding("v2", V2ChunkKeyEncoding)
.venv/lib/python3.11/site-packages/zarr/core/codec_pipeline.py ADDED
@@ -0,0 +1,562 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from itertools import islice, pairwise
5
+ from typing import TYPE_CHECKING, Any, TypeVar
6
+ from warnings import warn
7
+
8
+ from zarr.abc.codec import (
9
+ ArrayArrayCodec,
10
+ ArrayBytesCodec,
11
+ ArrayBytesCodecPartialDecodeMixin,
12
+ ArrayBytesCodecPartialEncodeMixin,
13
+ BytesBytesCodec,
14
+ Codec,
15
+ CodecPipeline,
16
+ )
17
+ from zarr.core.common import concurrent_map
18
+ from zarr.core.config import config
19
+ from zarr.core.indexing import SelectorTuple, is_scalar
20
+ from zarr.errors import ZarrUserWarning
21
+ from zarr.registry import register_pipeline
22
+
23
+ if TYPE_CHECKING:
24
+ from collections.abc import Iterable, Iterator
25
+ from typing import Self
26
+
27
+ from zarr.abc.store import ByteGetter, ByteSetter
28
+ from zarr.core.array_spec import ArraySpec
29
+ from zarr.core.buffer import Buffer, BufferPrototype, NDBuffer
30
+ from zarr.core.chunk_grids import ChunkGrid
31
+ from zarr.core.dtype.wrapper import TBaseDType, TBaseScalar, ZDType
32
+
33
+ T = TypeVar("T")
34
+ U = TypeVar("U")
35
+
36
+
37
+ def _unzip2(iterable: Iterable[tuple[T, U]]) -> tuple[list[T], list[U]]:
38
+ out0: list[T] = []
39
+ out1: list[U] = []
40
+ for item0, item1 in iterable:
41
+ out0.append(item0)
42
+ out1.append(item1)
43
+ return (out0, out1)
44
+
45
+
46
+ def batched(iterable: Iterable[T], n: int) -> Iterable[tuple[T, ...]]:
47
+ if n < 1:
48
+ raise ValueError("n must be at least one")
49
+ it = iter(iterable)
50
+ while batch := tuple(islice(it, n)):
51
+ yield batch
52
+
53
+
54
+ def resolve_batched(codec: Codec, chunk_specs: Iterable[ArraySpec]) -> Iterable[ArraySpec]:
55
+ return [codec.resolve_metadata(chunk_spec) for chunk_spec in chunk_specs]
56
+
57
+
58
+ def fill_value_or_default(chunk_spec: ArraySpec) -> Any:
59
+ fill_value = chunk_spec.fill_value
60
+ if fill_value is None:
61
+ # Zarr V2 allowed `fill_value` to be null in the metadata.
62
+ # Zarr V3 requires it to be set. This has already been
63
+ # validated when decoding the metadata, but we support reading
64
+ # Zarr V2 data and need to support the case where fill_value
65
+ # is None.
66
+ return chunk_spec.dtype.default_scalar()
67
+ else:
68
+ return fill_value
69
+
70
+
71
+ @dataclass(frozen=True)
72
+ class BatchedCodecPipeline(CodecPipeline):
73
+ """Default codec pipeline.
74
+
75
+ This batched codec pipeline divides the chunk batches into batches of a configurable
76
+ batch size ("mini-batch"). Fetching, decoding, encoding and storing are performed in
77
+ lock step for each mini-batch. Multiple mini-batches are processing concurrently.
78
+ """
79
+
80
+ array_array_codecs: tuple[ArrayArrayCodec, ...]
81
+ array_bytes_codec: ArrayBytesCodec
82
+ bytes_bytes_codecs: tuple[BytesBytesCodec, ...]
83
+ batch_size: int
84
+
85
+ def evolve_from_array_spec(self, array_spec: ArraySpec) -> Self:
86
+ return type(self).from_codecs(c.evolve_from_array_spec(array_spec=array_spec) for c in self)
87
+
88
+ @classmethod
89
+ def from_codecs(cls, codecs: Iterable[Codec], *, batch_size: int | None = None) -> Self:
90
+ array_array_codecs, array_bytes_codec, bytes_bytes_codecs = codecs_from_list(codecs)
91
+
92
+ return cls(
93
+ array_array_codecs=array_array_codecs,
94
+ array_bytes_codec=array_bytes_codec,
95
+ bytes_bytes_codecs=bytes_bytes_codecs,
96
+ batch_size=batch_size or config.get("codec_pipeline.batch_size"),
97
+ )
98
+
99
+ @property
100
+ def supports_partial_decode(self) -> bool:
101
+ """Determines whether the codec pipeline supports partial decoding.
102
+
103
+ Currently, only codec pipelines with a single ArrayBytesCodec that supports
104
+ partial decoding can support partial decoding. This limitation is due to the fact
105
+ that ArrayArrayCodecs can change the slice selection leading to non-contiguous
106
+ slices and BytesBytesCodecs can change the chunk bytes in a way that slice
107
+ selections cannot be attributed to byte ranges anymore which renders partial
108
+ decoding infeasible.
109
+
110
+ This limitation may softened in the future."""
111
+ return (len(self.array_array_codecs) + len(self.bytes_bytes_codecs)) == 0 and isinstance(
112
+ self.array_bytes_codec, ArrayBytesCodecPartialDecodeMixin
113
+ )
114
+
115
+ @property
116
+ def supports_partial_encode(self) -> bool:
117
+ """Determines whether the codec pipeline supports partial encoding.
118
+
119
+ Currently, only codec pipelines with a single ArrayBytesCodec that supports
120
+ partial encoding can support partial encoding. This limitation is due to the fact
121
+ that ArrayArrayCodecs can change the slice selection leading to non-contiguous
122
+ slices and BytesBytesCodecs can change the chunk bytes in a way that slice
123
+ selections cannot be attributed to byte ranges anymore which renders partial
124
+ encoding infeasible.
125
+
126
+ This limitation may softened in the future."""
127
+ return (len(self.array_array_codecs) + len(self.bytes_bytes_codecs)) == 0 and isinstance(
128
+ self.array_bytes_codec, ArrayBytesCodecPartialEncodeMixin
129
+ )
130
+
131
+ def __iter__(self) -> Iterator[Codec]:
132
+ yield from self.array_array_codecs
133
+ yield self.array_bytes_codec
134
+ yield from self.bytes_bytes_codecs
135
+
136
+ def validate(
137
+ self,
138
+ *,
139
+ shape: tuple[int, ...],
140
+ dtype: ZDType[TBaseDType, TBaseScalar],
141
+ chunk_grid: ChunkGrid,
142
+ ) -> None:
143
+ for codec in self:
144
+ codec.validate(shape=shape, dtype=dtype, chunk_grid=chunk_grid)
145
+
146
+ def compute_encoded_size(self, byte_length: int, array_spec: ArraySpec) -> int:
147
+ for codec in self:
148
+ byte_length = codec.compute_encoded_size(byte_length, array_spec)
149
+ array_spec = codec.resolve_metadata(array_spec)
150
+ return byte_length
151
+
152
+ def _codecs_with_resolved_metadata_batched(
153
+ self, chunk_specs: Iterable[ArraySpec]
154
+ ) -> tuple[
155
+ list[tuple[ArrayArrayCodec, list[ArraySpec]]],
156
+ tuple[ArrayBytesCodec, list[ArraySpec]],
157
+ list[tuple[BytesBytesCodec, list[ArraySpec]]],
158
+ ]:
159
+ aa_codecs_with_spec: list[tuple[ArrayArrayCodec, list[ArraySpec]]] = []
160
+ chunk_specs = list(chunk_specs)
161
+ for aa_codec in self.array_array_codecs:
162
+ aa_codecs_with_spec.append((aa_codec, chunk_specs))
163
+ chunk_specs = [aa_codec.resolve_metadata(chunk_spec) for chunk_spec in chunk_specs]
164
+
165
+ ab_codec_with_spec = (self.array_bytes_codec, chunk_specs)
166
+ chunk_specs = [
167
+ self.array_bytes_codec.resolve_metadata(chunk_spec) for chunk_spec in chunk_specs
168
+ ]
169
+
170
+ bb_codecs_with_spec: list[tuple[BytesBytesCodec, list[ArraySpec]]] = []
171
+ for bb_codec in self.bytes_bytes_codecs:
172
+ bb_codecs_with_spec.append((bb_codec, chunk_specs))
173
+ chunk_specs = [bb_codec.resolve_metadata(chunk_spec) for chunk_spec in chunk_specs]
174
+
175
+ return (aa_codecs_with_spec, ab_codec_with_spec, bb_codecs_with_spec)
176
+
177
+ async def decode_batch(
178
+ self,
179
+ chunk_bytes_and_specs: Iterable[tuple[Buffer | None, ArraySpec]],
180
+ ) -> Iterable[NDBuffer | None]:
181
+ chunk_bytes_batch: Iterable[Buffer | None]
182
+ chunk_bytes_batch, chunk_specs = _unzip2(chunk_bytes_and_specs)
183
+ (
184
+ aa_codecs_with_spec,
185
+ ab_codec_with_spec,
186
+ bb_codecs_with_spec,
187
+ ) = self._codecs_with_resolved_metadata_batched(chunk_specs)
188
+
189
+ for bb_codec, chunk_spec_batch in bb_codecs_with_spec[::-1]:
190
+ chunk_bytes_batch = await bb_codec.decode(
191
+ zip(chunk_bytes_batch, chunk_spec_batch, strict=False)
192
+ )
193
+
194
+ ab_codec, chunk_spec_batch = ab_codec_with_spec
195
+ chunk_array_batch = await ab_codec.decode(
196
+ zip(chunk_bytes_batch, chunk_spec_batch, strict=False)
197
+ )
198
+
199
+ for aa_codec, chunk_spec_batch in aa_codecs_with_spec[::-1]:
200
+ chunk_array_batch = await aa_codec.decode(
201
+ zip(chunk_array_batch, chunk_spec_batch, strict=False)
202
+ )
203
+
204
+ return chunk_array_batch
205
+
206
+ async def decode_partial_batch(
207
+ self,
208
+ batch_info: Iterable[tuple[ByteGetter, SelectorTuple, ArraySpec]],
209
+ ) -> Iterable[NDBuffer | None]:
210
+ assert self.supports_partial_decode
211
+ assert isinstance(self.array_bytes_codec, ArrayBytesCodecPartialDecodeMixin)
212
+ return await self.array_bytes_codec.decode_partial(batch_info)
213
+
214
+ async def encode_batch(
215
+ self,
216
+ chunk_arrays_and_specs: Iterable[tuple[NDBuffer | None, ArraySpec]],
217
+ ) -> Iterable[Buffer | None]:
218
+ chunk_array_batch: Iterable[NDBuffer | None]
219
+ chunk_specs: Iterable[ArraySpec]
220
+ chunk_array_batch, chunk_specs = _unzip2(chunk_arrays_and_specs)
221
+
222
+ for aa_codec in self.array_array_codecs:
223
+ chunk_array_batch = await aa_codec.encode(
224
+ zip(chunk_array_batch, chunk_specs, strict=False)
225
+ )
226
+ chunk_specs = resolve_batched(aa_codec, chunk_specs)
227
+
228
+ chunk_bytes_batch = await self.array_bytes_codec.encode(
229
+ zip(chunk_array_batch, chunk_specs, strict=False)
230
+ )
231
+ chunk_specs = resolve_batched(self.array_bytes_codec, chunk_specs)
232
+
233
+ for bb_codec in self.bytes_bytes_codecs:
234
+ chunk_bytes_batch = await bb_codec.encode(
235
+ zip(chunk_bytes_batch, chunk_specs, strict=False)
236
+ )
237
+ chunk_specs = resolve_batched(bb_codec, chunk_specs)
238
+
239
+ return chunk_bytes_batch
240
+
241
+ async def encode_partial_batch(
242
+ self,
243
+ batch_info: Iterable[tuple[ByteSetter, NDBuffer, SelectorTuple, ArraySpec]],
244
+ ) -> None:
245
+ assert self.supports_partial_encode
246
+ assert isinstance(self.array_bytes_codec, ArrayBytesCodecPartialEncodeMixin)
247
+ await self.array_bytes_codec.encode_partial(batch_info)
248
+
249
+ async def read_batch(
250
+ self,
251
+ batch_info: Iterable[tuple[ByteGetter, ArraySpec, SelectorTuple, SelectorTuple, bool]],
252
+ out: NDBuffer,
253
+ drop_axes: tuple[int, ...] = (),
254
+ ) -> None:
255
+ if self.supports_partial_decode:
256
+ chunk_array_batch = await self.decode_partial_batch(
257
+ [
258
+ (byte_getter, chunk_selection, chunk_spec)
259
+ for byte_getter, chunk_spec, chunk_selection, *_ in batch_info
260
+ ]
261
+ )
262
+ for chunk_array, (_, chunk_spec, _, out_selection, _) in zip(
263
+ chunk_array_batch, batch_info, strict=False
264
+ ):
265
+ if chunk_array is not None:
266
+ if drop_axes:
267
+ chunk_array = chunk_array.squeeze(axis=drop_axes)
268
+ out[out_selection] = chunk_array
269
+ else:
270
+ out[out_selection] = fill_value_or_default(chunk_spec)
271
+ else:
272
+ chunk_bytes_batch = await concurrent_map(
273
+ [(byte_getter, array_spec.prototype) for byte_getter, array_spec, *_ in batch_info],
274
+ lambda byte_getter, prototype: byte_getter.get(prototype),
275
+ config.get("async.concurrency"),
276
+ )
277
+ chunk_array_batch = await self.decode_batch(
278
+ [
279
+ (chunk_bytes, chunk_spec)
280
+ for chunk_bytes, (_, chunk_spec, *_) in zip(
281
+ chunk_bytes_batch, batch_info, strict=False
282
+ )
283
+ ],
284
+ )
285
+ for chunk_array, (_, chunk_spec, chunk_selection, out_selection, _) in zip(
286
+ chunk_array_batch, batch_info, strict=False
287
+ ):
288
+ if chunk_array is not None:
289
+ tmp = chunk_array[chunk_selection]
290
+ if drop_axes:
291
+ tmp = tmp.squeeze(axis=drop_axes)
292
+ out[out_selection] = tmp
293
+ else:
294
+ out[out_selection] = fill_value_or_default(chunk_spec)
295
+
296
+ def _merge_chunk_array(
297
+ self,
298
+ existing_chunk_array: NDBuffer | None,
299
+ value: NDBuffer,
300
+ out_selection: SelectorTuple,
301
+ chunk_spec: ArraySpec,
302
+ chunk_selection: SelectorTuple,
303
+ is_complete_chunk: bool,
304
+ drop_axes: tuple[int, ...],
305
+ ) -> NDBuffer:
306
+ if (
307
+ is_complete_chunk
308
+ and value.shape == chunk_spec.shape
309
+ # Guard that this is not a partial chunk at the end with is_complete_chunk=True
310
+ and value[out_selection].shape == chunk_spec.shape
311
+ ):
312
+ return value
313
+ if existing_chunk_array is None:
314
+ chunk_array = chunk_spec.prototype.nd_buffer.create(
315
+ shape=chunk_spec.shape,
316
+ dtype=chunk_spec.dtype.to_native_dtype(),
317
+ order=chunk_spec.order,
318
+ fill_value=fill_value_or_default(chunk_spec),
319
+ )
320
+ else:
321
+ chunk_array = existing_chunk_array.copy() # make a writable copy
322
+ if chunk_selection == () or is_scalar(
323
+ value.as_ndarray_like(), chunk_spec.dtype.to_native_dtype()
324
+ ):
325
+ chunk_value = value
326
+ else:
327
+ chunk_value = value[out_selection]
328
+ # handle missing singleton dimensions
329
+ if drop_axes:
330
+ item = tuple(
331
+ None # equivalent to np.newaxis
332
+ if idx in drop_axes
333
+ else slice(None)
334
+ for idx in range(chunk_spec.ndim)
335
+ )
336
+ chunk_value = chunk_value[item]
337
+ chunk_array[chunk_selection] = chunk_value
338
+ return chunk_array
339
+
340
+ async def write_batch(
341
+ self,
342
+ batch_info: Iterable[tuple[ByteSetter, ArraySpec, SelectorTuple, SelectorTuple, bool]],
343
+ value: NDBuffer,
344
+ drop_axes: tuple[int, ...] = (),
345
+ ) -> None:
346
+ if self.supports_partial_encode:
347
+ # Pass scalar values as is
348
+ if len(value.shape) == 0:
349
+ await self.encode_partial_batch(
350
+ [
351
+ (byte_setter, value, chunk_selection, chunk_spec)
352
+ for byte_setter, chunk_spec, chunk_selection, out_selection, _ in batch_info
353
+ ],
354
+ )
355
+ else:
356
+ await self.encode_partial_batch(
357
+ [
358
+ (byte_setter, value[out_selection], chunk_selection, chunk_spec)
359
+ for byte_setter, chunk_spec, chunk_selection, out_selection, _ in batch_info
360
+ ],
361
+ )
362
+
363
+ else:
364
+ # Read existing bytes if not total slice
365
+ async def _read_key(
366
+ byte_setter: ByteSetter | None, prototype: BufferPrototype
367
+ ) -> Buffer | None:
368
+ if byte_setter is None:
369
+ return None
370
+ return await byte_setter.get(prototype=prototype)
371
+
372
+ chunk_bytes_batch: Iterable[Buffer | None]
373
+ chunk_bytes_batch = await concurrent_map(
374
+ [
375
+ (
376
+ None if is_complete_chunk else byte_setter,
377
+ chunk_spec.prototype,
378
+ )
379
+ for byte_setter, chunk_spec, chunk_selection, _, is_complete_chunk in batch_info
380
+ ],
381
+ _read_key,
382
+ config.get("async.concurrency"),
383
+ )
384
+ chunk_array_decoded = await self.decode_batch(
385
+ [
386
+ (chunk_bytes, chunk_spec)
387
+ for chunk_bytes, (_, chunk_spec, *_) in zip(
388
+ chunk_bytes_batch, batch_info, strict=False
389
+ )
390
+ ],
391
+ )
392
+
393
+ chunk_array_merged = [
394
+ self._merge_chunk_array(
395
+ chunk_array,
396
+ value,
397
+ out_selection,
398
+ chunk_spec,
399
+ chunk_selection,
400
+ is_complete_chunk,
401
+ drop_axes,
402
+ )
403
+ for chunk_array, (
404
+ _,
405
+ chunk_spec,
406
+ chunk_selection,
407
+ out_selection,
408
+ is_complete_chunk,
409
+ ) in zip(chunk_array_decoded, batch_info, strict=False)
410
+ ]
411
+ chunk_array_batch: list[NDBuffer | None] = []
412
+ for chunk_array, (_, chunk_spec, *_) in zip(
413
+ chunk_array_merged, batch_info, strict=False
414
+ ):
415
+ if chunk_array is None:
416
+ chunk_array_batch.append(None) # type: ignore[unreachable]
417
+ else:
418
+ if not chunk_spec.config.write_empty_chunks and chunk_array.all_equal(
419
+ fill_value_or_default(chunk_spec)
420
+ ):
421
+ chunk_array_batch.append(None)
422
+ else:
423
+ chunk_array_batch.append(chunk_array)
424
+
425
+ chunk_bytes_batch = await self.encode_batch(
426
+ [
427
+ (chunk_array, chunk_spec)
428
+ for chunk_array, (_, chunk_spec, *_) in zip(
429
+ chunk_array_batch, batch_info, strict=False
430
+ )
431
+ ],
432
+ )
433
+
434
+ async def _write_key(byte_setter: ByteSetter, chunk_bytes: Buffer | None) -> None:
435
+ if chunk_bytes is None:
436
+ await byte_setter.delete()
437
+ else:
438
+ await byte_setter.set(chunk_bytes)
439
+
440
+ await concurrent_map(
441
+ [
442
+ (byte_setter, chunk_bytes)
443
+ for chunk_bytes, (byte_setter, *_) in zip(
444
+ chunk_bytes_batch, batch_info, strict=False
445
+ )
446
+ ],
447
+ _write_key,
448
+ config.get("async.concurrency"),
449
+ )
450
+
451
+ async def decode(
452
+ self,
453
+ chunk_bytes_and_specs: Iterable[tuple[Buffer | None, ArraySpec]],
454
+ ) -> Iterable[NDBuffer | None]:
455
+ output: list[NDBuffer | None] = []
456
+ for batch_info in batched(chunk_bytes_and_specs, self.batch_size):
457
+ output.extend(await self.decode_batch(batch_info))
458
+ return output
459
+
460
+ async def encode(
461
+ self,
462
+ chunk_arrays_and_specs: Iterable[tuple[NDBuffer | None, ArraySpec]],
463
+ ) -> Iterable[Buffer | None]:
464
+ output: list[Buffer | None] = []
465
+ for single_batch_info in batched(chunk_arrays_and_specs, self.batch_size):
466
+ output.extend(await self.encode_batch(single_batch_info))
467
+ return output
468
+
469
+ async def read(
470
+ self,
471
+ batch_info: Iterable[tuple[ByteGetter, ArraySpec, SelectorTuple, SelectorTuple, bool]],
472
+ out: NDBuffer,
473
+ drop_axes: tuple[int, ...] = (),
474
+ ) -> None:
475
+ await concurrent_map(
476
+ [
477
+ (single_batch_info, out, drop_axes)
478
+ for single_batch_info in batched(batch_info, self.batch_size)
479
+ ],
480
+ self.read_batch,
481
+ config.get("async.concurrency"),
482
+ )
483
+
484
+ async def write(
485
+ self,
486
+ batch_info: Iterable[tuple[ByteSetter, ArraySpec, SelectorTuple, SelectorTuple, bool]],
487
+ value: NDBuffer,
488
+ drop_axes: tuple[int, ...] = (),
489
+ ) -> None:
490
+ await concurrent_map(
491
+ [
492
+ (single_batch_info, value, drop_axes)
493
+ for single_batch_info in batched(batch_info, self.batch_size)
494
+ ],
495
+ self.write_batch,
496
+ config.get("async.concurrency"),
497
+ )
498
+
499
+
500
+ def codecs_from_list(
501
+ codecs: Iterable[Codec],
502
+ ) -> tuple[tuple[ArrayArrayCodec, ...], ArrayBytesCodec, tuple[BytesBytesCodec, ...]]:
503
+ from zarr.codecs.sharding import ShardingCodec
504
+
505
+ array_array: tuple[ArrayArrayCodec, ...] = ()
506
+ array_bytes_maybe: ArrayBytesCodec | None = None
507
+ bytes_bytes: tuple[BytesBytesCodec, ...] = ()
508
+
509
+ if any(isinstance(codec, ShardingCodec) for codec in codecs) and len(tuple(codecs)) > 1:
510
+ warn(
511
+ "Combining a `sharding_indexed` codec disables partial reads and "
512
+ "writes, which may lead to inefficient performance.",
513
+ category=ZarrUserWarning,
514
+ stacklevel=3,
515
+ )
516
+
517
+ for prev_codec, cur_codec in pairwise((None, *codecs)):
518
+ if isinstance(cur_codec, ArrayArrayCodec):
519
+ if isinstance(prev_codec, ArrayBytesCodec | BytesBytesCodec):
520
+ msg = (
521
+ f"Invalid codec order. ArrayArrayCodec {cur_codec}"
522
+ "must be preceded by another ArrayArrayCodec. "
523
+ f"Got {type(prev_codec)} instead."
524
+ )
525
+ raise TypeError(msg)
526
+ array_array += (cur_codec,)
527
+
528
+ elif isinstance(cur_codec, ArrayBytesCodec):
529
+ if isinstance(prev_codec, BytesBytesCodec):
530
+ msg = (
531
+ f"Invalid codec order. ArrayBytes codec {cur_codec}"
532
+ f" must be preceded by an ArrayArrayCodec. Got {type(prev_codec)} instead."
533
+ )
534
+ raise TypeError(msg)
535
+
536
+ if array_bytes_maybe is not None:
537
+ msg = (
538
+ f"Got two instances of ArrayBytesCodec: {array_bytes_maybe} and {cur_codec}. "
539
+ "Only one array-to-bytes codec is allowed."
540
+ )
541
+ raise ValueError(msg)
542
+
543
+ array_bytes_maybe = cur_codec
544
+
545
+ elif isinstance(cur_codec, BytesBytesCodec):
546
+ if isinstance(prev_codec, ArrayArrayCodec):
547
+ msg = (
548
+ f"Invalid codec order. BytesBytesCodec {cur_codec}"
549
+ "must be preceded by either another BytesBytesCodec, or an ArrayBytesCodec. "
550
+ f"Got {type(prev_codec)} instead."
551
+ )
552
+ bytes_bytes += (cur_codec,)
553
+ else:
554
+ raise TypeError
555
+
556
+ if array_bytes_maybe is None:
557
+ raise ValueError("Required ArrayBytesCodec was not found.")
558
+ else:
559
+ return array_array, array_bytes_maybe, bytes_bytes
560
+
561
+
562
+ register_pipeline(BatchedCodecPipeline)
.venv/lib/python3.11/site-packages/zarr/core/common.py ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import functools
5
+ import math
6
+ import operator
7
+ import warnings
8
+ from collections.abc import Iterable, Mapping, Sequence
9
+ from enum import Enum
10
+ from itertools import starmap
11
+ from typing import (
12
+ TYPE_CHECKING,
13
+ Any,
14
+ Final,
15
+ Generic,
16
+ Literal,
17
+ NotRequired,
18
+ TypedDict,
19
+ TypeVar,
20
+ cast,
21
+ overload,
22
+ )
23
+
24
+ import numpy as np
25
+ from typing_extensions import ReadOnly
26
+
27
+ from zarr.core.config import config as zarr_config
28
+ from zarr.errors import ZarrRuntimeWarning
29
+
30
+ if TYPE_CHECKING:
31
+ from collections.abc import Awaitable, Callable, Iterator
32
+
33
+
34
+ ZARR_JSON = "zarr.json"
35
+ ZARRAY_JSON = ".zarray"
36
+ ZGROUP_JSON = ".zgroup"
37
+ ZATTRS_JSON = ".zattrs"
38
+ ZMETADATA_V2_JSON = ".zmetadata"
39
+
40
+ BytesLike = bytes | bytearray | memoryview
41
+ ShapeLike = Iterable[int | np.integer[Any]] | int | np.integer[Any]
42
+ # For backwards compatibility
43
+ ChunkCoords = tuple[int, ...]
44
+ ZarrFormat = Literal[2, 3]
45
+ NodeType = Literal["array", "group"]
46
+ JSON = str | int | float | bool | Mapping[str, "JSON"] | Sequence["JSON"] | None
47
+ MemoryOrder = Literal["C", "F"]
48
+ AccessModeLiteral = Literal["r", "r+", "a", "w", "w-"]
49
+ ANY_ACCESS_MODE: Final = "r", "r+", "a", "w", "w-"
50
+ DimensionNames = Iterable[str | None] | None
51
+
52
+ TName = TypeVar("TName", bound=str)
53
+ TConfig = TypeVar("TConfig", bound=Mapping[str, object])
54
+
55
+
56
+ class NamedConfig(TypedDict, Generic[TName, TConfig]):
57
+ """
58
+ A typed dictionary representing an object with a name and configuration, where the configuration
59
+ is an optional mapping of string keys to values, e.g. another typed dictionary or a JSON object.
60
+
61
+ This class is generic with two type parameters: the type of the name (``TName``) and the type of
62
+ the configuration (``TConfig``).
63
+ """
64
+
65
+ name: ReadOnly[TName]
66
+ """The name of the object."""
67
+
68
+ configuration: NotRequired[ReadOnly[TConfig]]
69
+ """The configuration of the object. Not required."""
70
+
71
+
72
+ class NamedRequiredConfig(TypedDict, Generic[TName, TConfig]):
73
+ """
74
+ A typed dictionary representing an object with a name and configuration, where the configuration
75
+ is a mapping of string keys to values, e.g. another typed dictionary or a JSON object.
76
+
77
+ This class is generic with two type parameters: the type of the name (``TName``) and the type of
78
+ the configuration (``TConfig``).
79
+ """
80
+
81
+ name: ReadOnly[TName]
82
+ """The name of the object."""
83
+
84
+ configuration: ReadOnly[TConfig]
85
+ """The configuration of the object."""
86
+
87
+
88
+ def product(tup: tuple[int, ...]) -> int:
89
+ return functools.reduce(operator.mul, tup, 1)
90
+
91
+
92
+ def ceildiv(a: float, b: float) -> int:
93
+ if a == 0:
94
+ return 0
95
+ return math.ceil(a / b)
96
+
97
+
98
+ T = TypeVar("T", bound=tuple[Any, ...])
99
+ V = TypeVar("V")
100
+
101
+
102
+ async def concurrent_map(
103
+ items: Iterable[T],
104
+ func: Callable[..., Awaitable[V]],
105
+ limit: int | None = None,
106
+ ) -> list[V]:
107
+ if limit is None:
108
+ return await asyncio.gather(*list(starmap(func, items)))
109
+
110
+ else:
111
+ sem = asyncio.Semaphore(limit)
112
+
113
+ async def run(item: tuple[Any]) -> V:
114
+ async with sem:
115
+ return await func(*item)
116
+
117
+ return await asyncio.gather(*[asyncio.ensure_future(run(item)) for item in items])
118
+
119
+
120
+ E = TypeVar("E", bound=Enum)
121
+
122
+
123
+ def enum_names(enum: type[E]) -> Iterator[str]:
124
+ for item in enum:
125
+ yield item.name
126
+
127
+
128
+ def parse_enum(data: object, cls: type[E]) -> E:
129
+ if isinstance(data, cls):
130
+ return data
131
+ if not isinstance(data, str):
132
+ raise TypeError(f"Expected str, got {type(data)}")
133
+ if data in enum_names(cls):
134
+ return cls(data)
135
+ raise ValueError(f"Value must be one of {list(enum_names(cls))!r}. Got {data} instead.")
136
+
137
+
138
+ def parse_name(data: JSON, expected: str | None = None) -> str:
139
+ if isinstance(data, str):
140
+ if expected is None or data == expected:
141
+ return data
142
+ raise ValueError(f"Expected '{expected}'. Got {data} instead.")
143
+ else:
144
+ raise TypeError(f"Expected a string, got an instance of {type(data)}.")
145
+
146
+
147
+ def parse_configuration(data: JSON) -> JSON:
148
+ if not isinstance(data, dict):
149
+ raise TypeError(f"Expected dict, got {type(data)}")
150
+ return data
151
+
152
+
153
+ @overload
154
+ def parse_named_configuration(
155
+ data: JSON | NamedConfig[str, Any], expected_name: str | None = None
156
+ ) -> tuple[str, dict[str, JSON]]: ...
157
+
158
+
159
+ @overload
160
+ def parse_named_configuration(
161
+ data: JSON | NamedConfig[str, Any],
162
+ expected_name: str | None = None,
163
+ *,
164
+ require_configuration: bool = True,
165
+ ) -> tuple[str, dict[str, JSON] | None]: ...
166
+
167
+
168
+ def parse_named_configuration(
169
+ data: JSON | NamedConfig[str, Any],
170
+ expected_name: str | None = None,
171
+ *,
172
+ require_configuration: bool = True,
173
+ ) -> tuple[str, JSON | None]:
174
+ if not isinstance(data, dict):
175
+ raise TypeError(f"Expected dict, got {type(data)}")
176
+ if "name" not in data:
177
+ raise ValueError(f"Named configuration does not have a 'name' key. Got {data}.")
178
+ name_parsed = parse_name(data["name"], expected_name)
179
+ if "configuration" in data:
180
+ configuration_parsed = parse_configuration(data["configuration"])
181
+ elif require_configuration:
182
+ raise ValueError(f"Named configuration does not have a 'configuration' key. Got {data}.")
183
+ else:
184
+ configuration_parsed = None
185
+ return name_parsed, configuration_parsed
186
+
187
+
188
+ def parse_shapelike(data: ShapeLike) -> tuple[int, ...]:
189
+ """
190
+ Parse a shape-like input into an explicit shape.
191
+ """
192
+ if isinstance(data, int | np.integer):
193
+ if data < 0:
194
+ raise ValueError(f"Expected a non-negative integer. Got {data} instead")
195
+ return (int(data),)
196
+ try:
197
+ data_tuple = tuple(data)
198
+ except TypeError as e:
199
+ msg = f"Expected an integer or an iterable of integers. Got {data} instead."
200
+ raise TypeError(msg) from e
201
+
202
+ if not all(isinstance(v, int | np.integer) for v in data_tuple):
203
+ msg = f"Expected an iterable of integers. Got {data} instead."
204
+ raise TypeError(msg)
205
+ if not all(v > -1 for v in data_tuple):
206
+ msg = f"Expected all values to be non-negative. Got {data} instead."
207
+ raise ValueError(msg)
208
+
209
+ # cast NumPy scalars to plain python ints
210
+ return tuple(int(x) for x in data_tuple)
211
+
212
+
213
+ def parse_fill_value(data: Any) -> Any:
214
+ # todo: real validation
215
+ return data
216
+
217
+
218
+ def parse_order(data: Any) -> Literal["C", "F"]:
219
+ if data in ("C", "F"):
220
+ return cast("Literal['C', 'F']", data)
221
+ raise ValueError(f"Expected one of ('C', 'F'), got {data} instead.")
222
+
223
+
224
+ def parse_bool(data: Any) -> bool:
225
+ if isinstance(data, bool):
226
+ return data
227
+ raise ValueError(f"Expected bool, got {data} instead.")
228
+
229
+
230
+ def _warn_write_empty_chunks_kwarg() -> None:
231
+ # TODO: link to docs page on array configuration in this message
232
+ msg = (
233
+ "The `write_empty_chunks` keyword argument is deprecated and will be removed in future versions. "
234
+ "To control whether empty chunks are written to storage, either use the `config` keyword "
235
+ "argument, as in `config={'write_empty_chunks': True}`,"
236
+ "or change the global 'array.write_empty_chunks' configuration variable."
237
+ )
238
+ warnings.warn(msg, ZarrRuntimeWarning, stacklevel=2)
239
+
240
+
241
+ def _warn_order_kwarg() -> None:
242
+ # TODO: link to docs page on array configuration in this message
243
+ msg = (
244
+ "The `order` keyword argument has no effect for Zarr format 3 arrays. "
245
+ "To control the memory layout of the array, either use the `config` keyword "
246
+ "argument, as in `config={'order': 'C'}`,"
247
+ "or change the global 'array.order' configuration variable."
248
+ )
249
+ warnings.warn(msg, ZarrRuntimeWarning, stacklevel=2)
250
+
251
+
252
+ def _default_zarr_format() -> ZarrFormat:
253
+ """Return the default zarr_version"""
254
+ return cast("ZarrFormat", int(zarr_config.get("default_zarr_format", 3)))
.venv/lib/python3.11/site-packages/zarr/core/config.py ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ The config module is responsible for managing the configuration of zarr and is based on the Donfig python library.
3
+ For selecting custom implementations of codecs, pipelines, buffers and ndbuffers, first register the implementations
4
+ in the registry and then select them in the config.
5
+
6
+ Example:
7
+ An implementation of the bytes codec in a class ``your.module.NewBytesCodec`` requires the value of ``codecs.bytes``
8
+ to be ``your.module.NewBytesCodec``. Donfig can be configured programmatically, by environment variables, or from
9
+ YAML files in standard locations.
10
+
11
+ ```python
12
+ from your.module import NewBytesCodec
13
+ from zarr.core.config import register_codec, config
14
+
15
+ register_codec("bytes", NewBytesCodec)
16
+ config.set({"codecs.bytes": "your.module.NewBytesCodec"})
17
+ ```
18
+
19
+ Instead of setting the value programmatically with ``config.set``, you can also set the value with an environment
20
+ variable. The environment variable ``ZARR_CODECS__BYTES`` can be set to ``your.module.NewBytesCodec``. The double
21
+ underscore ``__`` is used to indicate nested access.
22
+
23
+ ```bash
24
+ export ZARR_CODECS__BYTES="your.module.NewBytesCodec"
25
+ ```
26
+
27
+ For more information, see the Donfig documentation at https://github.com/pytroll/donfig.
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ from typing import TYPE_CHECKING, Any, Literal, cast
33
+
34
+ from donfig import Config as DConfig
35
+
36
+ if TYPE_CHECKING:
37
+ from donfig.config_obj import ConfigSet
38
+
39
+
40
+ class BadConfigError(ValueError):
41
+ _msg = "bad Config: %r"
42
+
43
+
44
+ class Config(DConfig): # type: ignore[misc]
45
+ """The Config will collect configuration from config files and environment variables
46
+
47
+ Example environment variables:
48
+ Grabs environment variables of the form "ZARR_FOO__BAR_BAZ=123" and
49
+ turns these into config variables of the form ``{"foo": {"bar-baz": 123}}``
50
+ It transforms the key and value in the following way:
51
+
52
+ - Lower-cases the key text
53
+ - Treats ``__`` (double-underscore) as nested access
54
+ - Calls ``ast.literal_eval`` on the value
55
+
56
+ """
57
+
58
+ def reset(self) -> None:
59
+ self.clear()
60
+ self.refresh()
61
+
62
+ def enable_gpu(self) -> ConfigSet:
63
+ """
64
+ Configure Zarr to use GPUs where possible.
65
+ """
66
+ return self.set(
67
+ {"buffer": "zarr.buffer.gpu.Buffer", "ndbuffer": "zarr.buffer.gpu.NDBuffer"}
68
+ )
69
+
70
+
71
+ # these keys were removed from the config as part of the 3.1.0 release.
72
+ # these deprecations should be removed in 3.1.1 or thereabouts.
73
+ deprecations = {
74
+ "array.v2_default_compressor.numeric": None,
75
+ "array.v2_default_compressor.string": None,
76
+ "array.v2_default_compressor.bytes": None,
77
+ "array.v2_default_filters.string": None,
78
+ "array.v2_default_filters.bytes": None,
79
+ "array.v3_default_filters.numeric": None,
80
+ "array.v3_default_filters.raw": None,
81
+ "array.v3_default_filters.bytes": None,
82
+ "array.v3_default_serializer.numeric": None,
83
+ "array.v3_default_serializer.string": None,
84
+ "array.v3_default_serializer.bytes": None,
85
+ "array.v3_default_compressors.string": None,
86
+ "array.v3_default_compressors.bytes": None,
87
+ "array.v3_default_compressors": None,
88
+ }
89
+
90
+ # The default configuration for zarr
91
+ config = Config(
92
+ "zarr",
93
+ defaults=[
94
+ {
95
+ "default_zarr_format": 3,
96
+ "array": {
97
+ "order": "C",
98
+ "write_empty_chunks": False,
99
+ "target_shard_size_bytes": None,
100
+ },
101
+ "async": {"concurrency": 10, "timeout": None},
102
+ "threading": {"max_workers": None},
103
+ "json_indent": 2,
104
+ "codec_pipeline": {
105
+ "path": "zarr.core.codec_pipeline.BatchedCodecPipeline",
106
+ "batch_size": 1,
107
+ },
108
+ "codecs": {
109
+ "blosc": "zarr.codecs.blosc.BloscCodec",
110
+ "gzip": "zarr.codecs.gzip.GzipCodec",
111
+ "zstd": "zarr.codecs.zstd.ZstdCodec",
112
+ "bytes": "zarr.codecs.bytes.BytesCodec",
113
+ "endian": "zarr.codecs.bytes.BytesCodec", # compatibility with earlier versions of ZEP1
114
+ "crc32c": "zarr.codecs.crc32c_.Crc32cCodec",
115
+ "sharding_indexed": "zarr.codecs.sharding.ShardingCodec",
116
+ "transpose": "zarr.codecs.transpose.TransposeCodec",
117
+ "vlen-utf8": "zarr.codecs.vlen_utf8.VLenUTF8Codec",
118
+ "vlen-bytes": "zarr.codecs.vlen_utf8.VLenBytesCodec",
119
+ "numcodecs.bz2": "zarr.codecs.numcodecs.BZ2",
120
+ "numcodecs.crc32": "zarr.codecs.numcodecs.CRC32",
121
+ "numcodecs.crc32c": "zarr.codecs.numcodecs.CRC32C",
122
+ "numcodecs.lz4": "zarr.codecs.numcodecs.LZ4",
123
+ "numcodecs.lzma": "zarr.codecs.numcodecs.LZMA",
124
+ "numcodecs.zfpy": "zarr.codecs.numcodecs.ZFPY",
125
+ "numcodecs.adler32": "zarr.codecs.numcodecs.Adler32",
126
+ "numcodecs.astype": "zarr.codecs.numcodecs.AsType",
127
+ "numcodecs.bitround": "zarr.codecs.numcodecs.BitRound",
128
+ "numcodecs.blosc": "zarr.codecs.numcodecs.Blosc",
129
+ "numcodecs.delta": "zarr.codecs.numcodecs.Delta",
130
+ "numcodecs.fixedscaleoffset": "zarr.codecs.numcodecs.FixedScaleOffset",
131
+ "numcodecs.fletcher32": "zarr.codecs.numcodecs.Fletcher32",
132
+ "numcodecs.gzip": "zarr.codecs.numcodecs.GZip",
133
+ "numcodecs.jenkins_lookup3": "zarr.codecs.numcodecs.JenkinsLookup3",
134
+ "numcodecs.pcodec": "zarr.codecs.numcodecs.PCodec",
135
+ "numcodecs.packbits": "zarr.codecs.numcodecs.PackBits",
136
+ "numcodecs.shuffle": "zarr.codecs.numcodecs.Shuffle",
137
+ "numcodecs.quantize": "zarr.codecs.numcodecs.Quantize",
138
+ "numcodecs.zlib": "zarr.codecs.numcodecs.Zlib",
139
+ "numcodecs.zstd": "zarr.codecs.numcodecs.Zstd",
140
+ },
141
+ "buffer": "zarr.buffer.cpu.Buffer",
142
+ "ndbuffer": "zarr.buffer.cpu.NDBuffer",
143
+ }
144
+ ],
145
+ deprecations=deprecations,
146
+ )
147
+
148
+
149
+ def parse_indexing_order(data: Any) -> Literal["C", "F"]:
150
+ if data in ("C", "F"):
151
+ return cast("Literal['C', 'F']", data)
152
+ msg = f"Expected one of ('C', 'F'), got {data} instead."
153
+ raise ValueError(msg)
.venv/lib/python3.11/site-packages/zarr/core/dtype/__init__.py ADDED
@@ -0,0 +1,281 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Sequence
4
+ from typing import TYPE_CHECKING, Final, TypeAlias
5
+
6
+ from zarr.core.dtype.common import (
7
+ DataTypeValidationError,
8
+ DTypeJSON,
9
+ )
10
+ from zarr.core.dtype.npy.bool import Bool
11
+ from zarr.core.dtype.npy.bytes import (
12
+ NullTerminatedBytes,
13
+ NullterminatedBytesJSON_V2,
14
+ NullTerminatedBytesJSON_V3,
15
+ RawBytes,
16
+ RawBytesJSON_V2,
17
+ RawBytesJSON_V3,
18
+ VariableLengthBytes,
19
+ VariableLengthBytesJSON_V2,
20
+ )
21
+ from zarr.core.dtype.npy.complex import Complex64, Complex128
22
+ from zarr.core.dtype.npy.float import Float16, Float32, Float64
23
+ from zarr.core.dtype.npy.int import Int8, Int16, Int32, Int64, UInt8, UInt16, UInt32, UInt64
24
+ from zarr.core.dtype.npy.structured import Structured, StructuredJSON_V2, StructuredJSON_V3
25
+ from zarr.core.dtype.npy.time import (
26
+ DateTime64,
27
+ DateTime64JSON_V2,
28
+ DateTime64JSON_V3,
29
+ TimeDelta64,
30
+ TimeDelta64JSON_V2,
31
+ TimeDelta64JSON_V3,
32
+ )
33
+
34
+ if TYPE_CHECKING:
35
+ from zarr.core.common import ZarrFormat
36
+
37
+ from collections.abc import Mapping
38
+
39
+ import numpy as np
40
+ import numpy.typing as npt
41
+
42
+ from zarr.core.common import JSON
43
+ from zarr.core.dtype.npy.string import (
44
+ FixedLengthUTF32,
45
+ FixedLengthUTF32JSON_V2,
46
+ FixedLengthUTF32JSON_V3,
47
+ VariableLengthUTF8,
48
+ VariableLengthUTF8JSON_V2,
49
+ )
50
+ from zarr.core.dtype.registry import DataTypeRegistry
51
+ from zarr.core.dtype.wrapper import TBaseDType, TBaseScalar, ZDType
52
+
53
+ __all__ = [
54
+ "Bool",
55
+ "Complex64",
56
+ "Complex128",
57
+ "DataTypeRegistry",
58
+ "DataTypeValidationError",
59
+ "DateTime64",
60
+ "DateTime64JSON_V2",
61
+ "DateTime64JSON_V3",
62
+ "FixedLengthUTF32",
63
+ "FixedLengthUTF32JSON_V2",
64
+ "FixedLengthUTF32JSON_V3",
65
+ "Float16",
66
+ "Float32",
67
+ "Float64",
68
+ "Int8",
69
+ "Int16",
70
+ "Int32",
71
+ "Int64",
72
+ "NullTerminatedBytes",
73
+ "NullTerminatedBytesJSON_V3",
74
+ "NullterminatedBytesJSON_V2",
75
+ "RawBytes",
76
+ "RawBytesJSON_V2",
77
+ "RawBytesJSON_V3",
78
+ "Structured",
79
+ "StructuredJSON_V2",
80
+ "StructuredJSON_V3",
81
+ "TBaseDType",
82
+ "TBaseScalar",
83
+ "TimeDelta64",
84
+ "TimeDelta64JSON_V2",
85
+ "TimeDelta64JSON_V3",
86
+ "UInt8",
87
+ "UInt16",
88
+ "UInt32",
89
+ "UInt64",
90
+ "VariableLengthBytes",
91
+ "VariableLengthBytesJSON_V2",
92
+ "VariableLengthUTF8",
93
+ "VariableLengthUTF8JSON_V2",
94
+ "ZDType",
95
+ "data_type_registry",
96
+ "parse_data_type",
97
+ "parse_dtype",
98
+ ]
99
+
100
+ data_type_registry = DataTypeRegistry()
101
+
102
+ IntegerDType = Int8 | Int16 | Int32 | Int64 | UInt8 | UInt16 | UInt32 | UInt64
103
+ INTEGER_DTYPE: Final = Int8, Int16, Int32, Int64, UInt8, UInt16, UInt32, UInt64
104
+
105
+ FloatDType = Float16 | Float32 | Float64
106
+ FLOAT_DTYPE: Final = Float16, Float32, Float64
107
+
108
+ ComplexFloatDType = Complex64 | Complex128
109
+ COMPLEX_FLOAT_DTYPE: Final = Complex64, Complex128
110
+
111
+ StringDType = FixedLengthUTF32 | VariableLengthUTF8
112
+ STRING_DTYPE: Final = FixedLengthUTF32, VariableLengthUTF8
113
+
114
+ TimeDType = DateTime64 | TimeDelta64
115
+ TIME_DTYPE: Final = DateTime64, TimeDelta64
116
+
117
+ BytesDType = RawBytes | NullTerminatedBytes | VariableLengthBytes
118
+ BYTES_DTYPE: Final = RawBytes, NullTerminatedBytes, VariableLengthBytes
119
+
120
+ AnyDType = (
121
+ Bool
122
+ | IntegerDType
123
+ | FloatDType
124
+ | ComplexFloatDType
125
+ | StringDType
126
+ | BytesDType
127
+ | Structured
128
+ | TimeDType
129
+ | VariableLengthBytes
130
+ )
131
+ # mypy has trouble inferring the type of variablelengthstring dtype, because its class definition
132
+ # depends on the installed numpy version. That's why the type: ignore statement is needed here.
133
+ ANY_DTYPE: Final = (
134
+ Bool,
135
+ *INTEGER_DTYPE,
136
+ *FLOAT_DTYPE,
137
+ *COMPLEX_FLOAT_DTYPE,
138
+ *STRING_DTYPE,
139
+ *BYTES_DTYPE,
140
+ Structured,
141
+ *TIME_DTYPE,
142
+ VariableLengthBytes,
143
+ )
144
+
145
+ # These are aliases for variable-length UTF-8 strings
146
+ # We handle them when a user requests a data type instead of using NumPy's dtype inferece because
147
+ # the default NumPy behavior -- to inspect the user-provided array data and choose
148
+ # an appropriately sized U dtype -- is unworkable for Zarr.
149
+ VLEN_UTF8_ALIAS: Final = ("str", str, "string")
150
+
151
+ # This type models inputs that can be coerced to a ZDType
152
+ ZDTypeLike: TypeAlias = npt.DTypeLike | ZDType[TBaseDType, TBaseScalar] | Mapping[str, JSON] | str
153
+
154
+ for dtype in ANY_DTYPE:
155
+ # mypy does not know that all the elements of ANY_DTYPE are subclasses of ZDType
156
+ data_type_registry.register(dtype._zarr_v3_name, dtype) # type: ignore[arg-type]
157
+
158
+
159
+ # TODO: find a better name for this function
160
+ def get_data_type_from_native_dtype(dtype: npt.DTypeLike) -> ZDType[TBaseDType, TBaseScalar]:
161
+ """
162
+ Get a data type wrapper (an instance of ``ZDType``) from a native data type, e.g. a numpy dtype.
163
+ """
164
+ if not isinstance(dtype, np.dtype):
165
+ na_dtype: np.dtype[np.generic]
166
+ if isinstance(dtype, list):
167
+ # this is a valid _VoidDTypeLike check
168
+ na_dtype = np.dtype([tuple(d) for d in dtype])
169
+ else:
170
+ na_dtype = np.dtype(dtype)
171
+ else:
172
+ na_dtype = dtype
173
+ return data_type_registry.match_dtype(dtype=na_dtype)
174
+
175
+
176
+ def get_data_type_from_json(
177
+ dtype_spec: DTypeJSON, *, zarr_format: ZarrFormat
178
+ ) -> ZDType[TBaseDType, TBaseScalar]:
179
+ """
180
+ Given a JSON representation of a data type and a Zarr format version,
181
+ attempt to create a ZDType instance from the registered ZDType classes.
182
+ """
183
+ return data_type_registry.match_json(dtype_spec, zarr_format=zarr_format)
184
+
185
+
186
+ def parse_data_type(
187
+ dtype_spec: ZDTypeLike,
188
+ *,
189
+ zarr_format: ZarrFormat,
190
+ ) -> ZDType[TBaseDType, TBaseScalar]:
191
+ """
192
+ Interpret the input as a ZDType.
193
+
194
+ This function wraps ``parse_dtype``. The only difference is the function name. This function may
195
+ be deprecated in a future version of Zarr Python in favor of ``parse_dtype``.
196
+
197
+ Parameters
198
+ ----------
199
+ dtype_spec : ZDTypeLike
200
+ The input to be interpreted as a ZDType. This could be a ZDType, which will be returned
201
+ directly, or a JSON representation of a ZDType, or a native dtype, or a python object that
202
+ can be converted into a native dtype.
203
+ zarr_format : ZarrFormat
204
+ The Zarr format version. This parameter is required because this function will attempt to
205
+ parse the JSON representation of a data type, and the JSON representation of data types
206
+ varies between Zarr 2 and Zarr 3.
207
+
208
+ Returns
209
+ -------
210
+ ZDType[TBaseDType, TBaseScalar]
211
+ The ZDType corresponding to the input.
212
+
213
+ Examples
214
+ --------
215
+ ```python
216
+ from zarr.dtype import parse_data_type
217
+ import numpy as np
218
+ parse_data_type("int32", zarr_format=2)
219
+ # Int32(endianness='little')
220
+ parse_data_type(np.dtype('S10'), zarr_format=2)
221
+ # NullTerminatedBytes(length=10)
222
+ parse_data_type({"name": "numpy.datetime64", "configuration": {"unit": "s", "scale_factor": 10}}, zarr_format=3)
223
+ # DateTime64(endianness='little', scale_factor=10, unit='s')
224
+ ```
225
+ """
226
+ return parse_dtype(dtype_spec, zarr_format=zarr_format)
227
+
228
+
229
+ def parse_dtype(
230
+ dtype_spec: ZDTypeLike,
231
+ *,
232
+ zarr_format: ZarrFormat,
233
+ ) -> ZDType[TBaseDType, TBaseScalar]:
234
+ """
235
+ Convert the input as a ZDType.
236
+
237
+ Parameters
238
+ ----------
239
+ dtype_spec : ZDTypeLike
240
+ The input to be converted to a ZDType. This could be a ZDType, which will be returned
241
+ directly, or a JSON representation of a ZDType, or a numpy dtype, or a python object that
242
+ can be converted into a native dtype.
243
+ zarr_format : ZarrFormat
244
+ The Zarr format version. This parameter is required because this function will attempt to
245
+ parse the JSON representation of a data type, and the JSON representation of data types
246
+ varies between Zarr 2 and Zarr 3.
247
+
248
+ Returns
249
+ -------
250
+ ZDType[TBaseDType, TBaseScalar]
251
+ The ZDType corresponding to the input.
252
+
253
+ Examples
254
+ --------
255
+ ```python
256
+ from zarr.dtype import parse_dtype
257
+ import numpy as np
258
+ parse_dtype("int32", zarr_format=2)
259
+ # Int32(endianness='little')
260
+ parse_dtype(np.dtype('S10'), zarr_format=2)
261
+ # NullTerminatedBytes(length=10)
262
+ parse_dtype({"name": "numpy.datetime64", "configuration": {"unit": "s", "scale_factor": 10}}, zarr_format=3)
263
+ # DateTime64(endianness='little', scale_factor=10, unit='s')
264
+ ```
265
+ """
266
+ if isinstance(dtype_spec, ZDType):
267
+ return dtype_spec
268
+ # First attempt to interpret the input as JSON
269
+ if isinstance(dtype_spec, Mapping | str | Sequence):
270
+ try:
271
+ return get_data_type_from_json(dtype_spec, zarr_format=zarr_format) # type: ignore[arg-type]
272
+ except ValueError:
273
+ # no data type matched this JSON-like input
274
+ pass
275
+ if dtype_spec in VLEN_UTF8_ALIAS:
276
+ # If the dtype request is one of the aliases for variable-length UTF-8 strings,
277
+ # return that dtype.
278
+ return VariableLengthUTF8() # type: ignore[return-value]
279
+ # otherwise, we have either a numpy dtype string, or a zarr v3 dtype string, and in either case
280
+ # we can create a native dtype from it, and do the dtype inference from that
281
+ return get_data_type_from_native_dtype(dtype_spec) # type: ignore[arg-type]
.venv/lib/python3.11/site-packages/zarr/core/dtype/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (9.19 kB). View file
 
.venv/lib/python3.11/site-packages/zarr/core/dtype/__pycache__/common.cpython-311.pyc ADDED
Binary file (11.4 kB). View file
 
.venv/lib/python3.11/site-packages/zarr/core/dtype/__pycache__/registry.cpython-311.pyc ADDED
Binary file (9.14 kB). View file
 
.venv/lib/python3.11/site-packages/zarr/core/dtype/__pycache__/wrapper.cpython-311.pyc ADDED
Binary file (11.7 kB). View file
 
.venv/lib/python3.11/site-packages/zarr/core/dtype/common.py ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import warnings
4
+ from collections.abc import Mapping, Sequence
5
+ from dataclasses import dataclass
6
+ from typing import (
7
+ ClassVar,
8
+ Final,
9
+ Generic,
10
+ Literal,
11
+ TypedDict,
12
+ TypeGuard,
13
+ TypeVar,
14
+ )
15
+
16
+ from typing_extensions import ReadOnly
17
+
18
+ from zarr.core.common import NamedConfig
19
+ from zarr.errors import UnstableSpecificationWarning
20
+
21
+ EndiannessStr = Literal["little", "big"]
22
+ ENDIANNESS_STR: Final = "little", "big"
23
+
24
+ SpecialFloatStrings = Literal["NaN", "Infinity", "-Infinity"]
25
+ SPECIAL_FLOAT_STRINGS: Final = ("NaN", "Infinity", "-Infinity")
26
+
27
+ JSONFloatV2 = float | SpecialFloatStrings
28
+ JSONFloatV3 = float | SpecialFloatStrings | str
29
+
30
+ ObjectCodecID = Literal["vlen-utf8", "vlen-bytes", "vlen-array", "pickle", "json2", "msgpack2"]
31
+ # These are the ids of the known object codecs for zarr v2.
32
+ OBJECT_CODEC_IDS: Final = ("vlen-utf8", "vlen-bytes", "vlen-array", "pickle", "json2", "msgpack2")
33
+
34
+ # This is a wider type than our standard JSON type because we need
35
+ # to work with typeddict objects which are assignable to Mapping[str, object]
36
+ DTypeJSON = str | int | float | Sequence["DTypeJSON"] | None | Mapping[str, object]
37
+
38
+ # The DTypeJSON_V2 type exists because ZDType.from_json takes a single argument, which must contain
39
+ # all the information necessary to decode the data type. Zarr v2 supports multiple distinct
40
+ # data types that all used the "|O" data type identifier. These data types can only be
41
+ # discriminated on the basis of their "object codec", i.e. a special data type specific
42
+ # compressor or filter. So to figure out what data type a zarr v2 array has, we need the
43
+ # data type identifier from metadata, as well as an object codec id if the data type identifier
44
+ # is "|O".
45
+ # So we will pack the name of the dtype alongside the name of the object codec id, if applicable,
46
+ # in a single dict, and pass that to the data type inference logic.
47
+ # These type variables have a very wide bound because the individual zdtype
48
+ # classes can perform a very specific type check.
49
+
50
+ # This is the JSON representation of a structured dtype in zarr v2
51
+ StructuredName_V2 = Sequence["str | StructuredName_V2"]
52
+
53
+ # This models the type of the name a dtype might have in zarr v2 array metadata
54
+ DTypeName_V2 = StructuredName_V2 | str
55
+
56
+ TDTypeNameV2_co = TypeVar("TDTypeNameV2_co", bound=DTypeName_V2, covariant=True)
57
+ TObjectCodecID_co = TypeVar("TObjectCodecID_co", bound=None | str, covariant=True)
58
+
59
+
60
+ class DTypeConfig_V2(TypedDict, Generic[TDTypeNameV2_co, TObjectCodecID_co]):
61
+ name: ReadOnly[TDTypeNameV2_co]
62
+ object_codec_id: ReadOnly[TObjectCodecID_co]
63
+
64
+
65
+ DTypeSpec_V2 = DTypeConfig_V2[DTypeName_V2, None | str]
66
+
67
+
68
+ def check_structured_dtype_v2_inner(data: object) -> TypeGuard[StructuredName_V2]:
69
+ """
70
+ A type guard for the inner elements of a structured dtype. This is a recursive check because
71
+ the type is itself recursive.
72
+
73
+ This check ensures that all the elements are 2-element sequences beginning with a string
74
+ and ending with either another string or another 2-element sequence beginning with a string and
75
+ ending with another instance of that type.
76
+ """
77
+ if isinstance(data, (str, Mapping)):
78
+ return False
79
+ if not isinstance(data, Sequence):
80
+ return False
81
+ if len(data) != 2:
82
+ return False
83
+ if not (isinstance(data[0], str)):
84
+ return False
85
+ if isinstance(data[-1], str):
86
+ return True
87
+ elif isinstance(data[-1], Sequence):
88
+ return check_structured_dtype_v2_inner(data[-1])
89
+ return False
90
+
91
+
92
+ def check_structured_dtype_name_v2(data: Sequence[object]) -> TypeGuard[StructuredName_V2]:
93
+ """
94
+ Check that all the elements of a sequence are valid zarr v2 structured dtype identifiers
95
+ """
96
+ return all(check_structured_dtype_v2_inner(d) for d in data)
97
+
98
+
99
+ def check_dtype_name_v2(data: object) -> TypeGuard[DTypeName_V2]:
100
+ """
101
+ Type guard for narrowing the type of a python object to a valid zarr v2 dtype name.
102
+ """
103
+ if isinstance(data, str):
104
+ return True
105
+ elif isinstance(data, Sequence):
106
+ return check_structured_dtype_name_v2(data)
107
+ return False
108
+
109
+
110
+ def check_dtype_spec_v2(data: object) -> TypeGuard[DTypeSpec_V2]:
111
+ """
112
+ Type guard for narrowing a python object to an instance of DTypeSpec_V2
113
+ """
114
+ if not isinstance(data, Mapping):
115
+ return False
116
+ if set(data.keys()) != {"name", "object_codec_id"}:
117
+ return False
118
+ if not check_dtype_name_v2(data["name"]):
119
+ return False
120
+ return isinstance(data["object_codec_id"], str | None)
121
+
122
+
123
+ # By comparison, The JSON representation of a dtype in zarr v3 is much simpler.
124
+ # It's either a string, or a structured dict
125
+ DTypeSpec_V3 = str | NamedConfig[str, Mapping[str, object]]
126
+
127
+
128
+ def check_dtype_spec_v3(data: object) -> TypeGuard[DTypeSpec_V3]:
129
+ """
130
+ Type guard for narrowing the type of a python object to an instance of
131
+ DTypeSpec_V3, i.e either a string or a dict with a "name" field that's a string and a
132
+ "configuration" field that's a mapping with string keys.
133
+ """
134
+ if isinstance(data, str) or ( # noqa: SIM103
135
+ isinstance(data, Mapping)
136
+ and set(data.keys()) == {"name", "configuration"}
137
+ and isinstance(data["configuration"], Mapping)
138
+ and all(isinstance(k, str) for k in data["configuration"])
139
+ ):
140
+ return True
141
+ return False
142
+
143
+
144
+ def unpack_dtype_json(data: DTypeSpec_V2 | DTypeSpec_V3) -> DTypeJSON:
145
+ """
146
+ Return the array metadata form of the dtype JSON representation. For the Zarr V3 form of dtype
147
+ metadata, this is a no-op. For the Zarr V2 form of dtype metadata, this unpacks the dtype name.
148
+ """
149
+ if isinstance(data, Mapping) and set(data.keys()) == {"name", "object_codec_id"}:
150
+ return data["name"]
151
+ return data
152
+
153
+
154
+ class DataTypeValidationError(ValueError): ...
155
+
156
+
157
+ class ScalarTypeValidationError(ValueError): ...
158
+
159
+
160
+ @dataclass(frozen=True, kw_only=True)
161
+ class HasLength:
162
+ """
163
+ A mix-in class for data types with a length attribute, such as fixed-size collections
164
+ of unicode strings, or bytes.
165
+
166
+ Attributes
167
+ ----------
168
+ length : int
169
+ The length of the scalars belonging to this data type. Note that this class does not assign
170
+ a unit to the length. Child classes may assign units.
171
+ """
172
+
173
+ length: int
174
+
175
+
176
+ @dataclass(frozen=True, kw_only=True)
177
+ class HasEndianness:
178
+ """
179
+ A mix-in class for data types with an endianness attribute
180
+ """
181
+
182
+ endianness: EndiannessStr = "little"
183
+
184
+
185
+ @dataclass(frozen=True, kw_only=True)
186
+ class HasItemSize:
187
+ """
188
+ A mix-in class for data types with an item size attribute.
189
+ This mix-in bears a property ``item_size``, which denotes the size of each element of the data
190
+ type, in bytes.
191
+ """
192
+
193
+ @property
194
+ def item_size(self) -> int:
195
+ raise NotImplementedError
196
+
197
+
198
+ @dataclass(frozen=True, kw_only=True)
199
+ class HasObjectCodec:
200
+ """
201
+ A mix-in class for data types that require an object codec id.
202
+ This class bears the property ``object_codec_id``, which is the string name of an object
203
+ codec that is required to encode and decode the data type.
204
+
205
+ In zarr-python 2.x certain data types like variable-length strings or variable-length arrays
206
+ used the catch-all numpy "object" data type for their in-memory representation. But these data
207
+ types cannot be stored as numpy object data types, because the object data type does not define
208
+ a fixed memory layout. So these data types required a special codec, called an "object codec",
209
+ that effectively defined a compact representation for the data type, which was used to encode
210
+ and decode the data type.
211
+
212
+ Zarr-python 2.x would not allow the creation of arrays with the "object" data type if an object
213
+ codec was not specified, and thus the name of the object codec is effectively part of the data
214
+ type model.
215
+ """
216
+
217
+ object_codec_id: ClassVar[str]
218
+
219
+
220
+ def v3_unstable_dtype_warning(dtype: object) -> None:
221
+ """
222
+ Emit this warning when a data type does not have a stable zarr v3 spec
223
+ """
224
+ msg = (
225
+ f"The data type ({dtype}) does not have a Zarr V3 specification. "
226
+ "That means that the representation of arrays saved with this data type may change without "
227
+ "warning in a future version of Zarr Python. "
228
+ "Arrays stored with this data type may be unreadable by other Zarr libraries. "
229
+ "Use this data type at your own risk! "
230
+ "Check https://github.com/zarr-developers/zarr-extensions/tree/main/data-types for the "
231
+ "status of data type specifications for Zarr V3."
232
+ )
233
+ warnings.warn(msg, category=UnstableSpecificationWarning, stacklevel=2)
.venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__init__.py ADDED
File without changes
.venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (223 Bytes). View file
 
.venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/bool.cpython-311.pyc ADDED
Binary file (11.8 kB). View file
 
.venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/bytes.cpython-311.pyc ADDED
Binary file (49 kB). View file
 
.venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/common.cpython-311.pyc ADDED
Binary file (18.4 kB). View file
 
.venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/complex.cpython-311.pyc ADDED
Binary file (16.8 kB). View file
 
.venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/float.cpython-311.pyc ADDED
Binary file (17.4 kB). View file
 
.venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/int.cpython-311.pyc ADDED
Binary file (54.3 kB). View file
 
.venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/string.cpython-311.pyc ADDED
Binary file (31.7 kB). View file
 
.venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/structured.cpython-311.pyc ADDED
Binary file (20.9 kB). View file
 
.venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/time.cpython-311.pyc ADDED
Binary file (35.3 kB). View file
 
.venv/lib/python3.11/site-packages/zarr/core/dtype/npy/bool.py ADDED
@@ -0,0 +1,319 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import TYPE_CHECKING, ClassVar, Literal, Self, TypeGuard, overload
5
+
6
+ import numpy as np
7
+
8
+ from zarr.core.dtype.common import (
9
+ DataTypeValidationError,
10
+ DTypeConfig_V2,
11
+ DTypeJSON,
12
+ HasItemSize,
13
+ check_dtype_spec_v2,
14
+ )
15
+ from zarr.core.dtype.wrapper import TBaseDType, ZDType
16
+
17
+ if TYPE_CHECKING:
18
+ from zarr.core.common import JSON, ZarrFormat
19
+
20
+
21
+ @dataclass(frozen=True, kw_only=True, slots=True)
22
+ class Bool(ZDType[np.dtypes.BoolDType, np.bool_], HasItemSize):
23
+ """
24
+ A Zarr data type for arrays containing booleans.
25
+
26
+ Wraps the [`np.dtypes.BoolDType`][numpy.dtypes.BoolDType] data type. Scalars for this data type are instances of
27
+ [`np.bool_`][numpy.bool_].
28
+
29
+ Attributes
30
+ ----------
31
+
32
+ _zarr_v3_name : Literal["bool"] = "bool"
33
+ The Zarr v3 name of the dtype.
34
+ _zarr_v2_name : ``Literal["|b1"]`` = ``"|b1"``
35
+ The Zarr v2 name of the dtype, which is also a string representation
36
+ of the boolean dtype used by NumPy.
37
+ dtype_cls : ClassVar[type[np.dtypes.BoolDType]] = np.dtypes.BoolDType
38
+ The NumPy dtype class.
39
+
40
+ References
41
+ ----------
42
+ This class implements the boolean data type defined in Zarr V2 and V3.
43
+
44
+ See the [Zarr V2](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v2/v2.0.rst#data-type-encoding)and [Zarr V3](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v3/data-types/index.rst) specification documents for details.
45
+ """
46
+
47
+ _zarr_v3_name: ClassVar[Literal["bool"]] = "bool"
48
+ _zarr_v2_name: ClassVar[Literal["|b1"]] = "|b1"
49
+ dtype_cls = np.dtypes.BoolDType
50
+
51
+ @classmethod
52
+ def from_native_dtype(cls, dtype: TBaseDType) -> Self:
53
+ """
54
+ Create an instance of Bool from an instance of np.dtypes.BoolDType.
55
+
56
+ Parameters
57
+ ----------
58
+ dtype : TBaseDType
59
+ The NumPy boolean dtype instance to convert.
60
+
61
+ Returns
62
+ -------
63
+ Bool
64
+ An instance of Bool.
65
+
66
+ Raises
67
+ ------
68
+ DataTypeValidationError
69
+ If the provided dtype is not compatible with this ZDType.
70
+ """
71
+ if cls._check_native_dtype(dtype):
72
+ return cls()
73
+ raise DataTypeValidationError(
74
+ f"Invalid data type: {dtype}. Expected an instance of {cls.dtype_cls}"
75
+ )
76
+
77
+ def to_native_dtype(self: Self) -> np.dtypes.BoolDType:
78
+ """
79
+ Create a NumPy boolean dtype instance from this ZDType.
80
+
81
+ Returns
82
+ -------
83
+ np.dtypes.BoolDType
84
+ The NumPy boolean dtype.
85
+ """
86
+ return self.dtype_cls()
87
+
88
+ @classmethod
89
+ def _check_json_v2(
90
+ cls,
91
+ data: DTypeJSON,
92
+ ) -> TypeGuard[DTypeConfig_V2[Literal["|b1"], None]]:
93
+ """
94
+ Check that the input is a valid JSON representation of a Bool.
95
+
96
+ Parameters
97
+ ----------
98
+ data : DTypeJSON
99
+ The JSON data to check.
100
+
101
+ Returns
102
+ -------
103
+ ``TypeGuard[DTypeConfig_V2[Literal["|b1"], None]]``
104
+ True if the input is a valid JSON representation, False otherwise.
105
+ """
106
+ return (
107
+ check_dtype_spec_v2(data)
108
+ and data["name"] == cls._zarr_v2_name
109
+ and data["object_codec_id"] is None
110
+ )
111
+
112
+ @classmethod
113
+ def _check_json_v3(cls, data: DTypeJSON) -> TypeGuard[Literal["bool"]]:
114
+ """
115
+ Check that the input is a valid JSON representation of this class in Zarr V3.
116
+
117
+ Parameters
118
+ ----------
119
+ data : DTypeJSON
120
+ The JSON data to check.
121
+
122
+ Returns
123
+ -------
124
+ bool
125
+ True if the input is a valid JSON representation, False otherwise.
126
+ """
127
+ return data == cls._zarr_v3_name
128
+
129
+ @classmethod
130
+ def _from_json_v2(cls, data: DTypeJSON) -> Self:
131
+ """
132
+ Create an instance of Bool from Zarr V2-flavored JSON.
133
+
134
+ Parameters
135
+ ----------
136
+ data : DTypeJSON
137
+ The JSON data.
138
+
139
+ Returns
140
+ -------
141
+ Bool
142
+ An instance of Bool.
143
+
144
+ Raises
145
+ ------
146
+ DataTypeValidationError
147
+ If the input JSON is not a valid representation of this class.
148
+ """
149
+ if cls._check_json_v2(data):
150
+ return cls()
151
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected the string {cls._zarr_v2_name!r}"
152
+ raise DataTypeValidationError(msg)
153
+
154
+ @classmethod
155
+ def _from_json_v3(cls: type[Self], data: DTypeJSON) -> Self:
156
+ """
157
+ Create an instance of Bool from Zarr V3-flavored JSON.
158
+
159
+ Parameters
160
+ ----------
161
+ data : DTypeJSON
162
+ The JSON data.
163
+
164
+ Returns
165
+ -------
166
+ Bool
167
+ An instance of Bool.
168
+
169
+ Raises
170
+ ------
171
+ DataTypeValidationError
172
+ If the input JSON is not a valid representation of this class.
173
+ """
174
+ if cls._check_json_v3(data):
175
+ return cls()
176
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected the string {cls._zarr_v3_name!r}"
177
+ raise DataTypeValidationError(msg)
178
+
179
+ @overload
180
+ def to_json(self, zarr_format: Literal[2]) -> DTypeConfig_V2[Literal["|b1"], None]: ...
181
+
182
+ @overload
183
+ def to_json(self, zarr_format: Literal[3]) -> Literal["bool"]: ...
184
+
185
+ def to_json(
186
+ self, zarr_format: ZarrFormat
187
+ ) -> DTypeConfig_V2[Literal["|b1"], None] | Literal["bool"]:
188
+ """
189
+ Serialize this Bool instance to JSON.
190
+
191
+ Parameters
192
+ ----------
193
+ zarr_format : ZarrFormat
194
+ The Zarr format version (2 or 3).
195
+
196
+ Returns
197
+ -------
198
+ ``DTypeConfig_V2[Literal["|b1"], None] | Literal["bool"]``
199
+ The JSON representation of the Bool instance.
200
+
201
+ Raises
202
+ ------
203
+ ValueError
204
+ If the zarr_format is not 2 or 3.
205
+ """
206
+ if zarr_format == 2:
207
+ return {"name": self._zarr_v2_name, "object_codec_id": None}
208
+ elif zarr_format == 3:
209
+ return self._zarr_v3_name
210
+ raise ValueError(f"zarr_format must be 2 or 3, got {zarr_format}") # pragma: no cover
211
+
212
+ def _check_scalar(self, data: object) -> bool:
213
+ """
214
+ Check if the input can be cast to a boolean scalar.
215
+
216
+ Parameters
217
+ ----------
218
+ data : object
219
+ The data to check.
220
+
221
+ Returns
222
+ -------
223
+ bool
224
+ True if the input can be cast to a boolean scalar, False otherwise.
225
+ """
226
+ return True
227
+
228
+ def cast_scalar(self, data: object) -> np.bool_:
229
+ """
230
+ Cast the input to a numpy boolean scalar.
231
+
232
+ Parameters
233
+ ----------
234
+ data : object
235
+ The data to cast.
236
+
237
+ Returns
238
+ -------
239
+ bool : np.bool_
240
+ The numpy boolean scalar.
241
+
242
+ Raises
243
+ ------
244
+ TypeError
245
+ If the input cannot be converted to a numpy boolean.
246
+ """
247
+ if self._check_scalar(data):
248
+ return np.bool_(data)
249
+ msg = ( # pragma: no cover
250
+ f"Cannot convert object {data!r} with type {type(data)} to a scalar compatible with the "
251
+ f"data type {self}."
252
+ )
253
+ raise TypeError(msg) # pragma: no cover
254
+
255
+ def default_scalar(self) -> np.bool_:
256
+ """
257
+ Get the default value for the boolean dtype.
258
+
259
+ Returns
260
+ -------
261
+ bool : np.bool_
262
+ The default value.
263
+ """
264
+ return np.False_
265
+
266
+ def to_json_scalar(self, data: object, *, zarr_format: ZarrFormat) -> bool:
267
+ """
268
+ Convert a scalar to a python bool.
269
+
270
+ Parameters
271
+ ----------
272
+ data : object
273
+ The value to convert.
274
+ zarr_format : ZarrFormat
275
+ The zarr format version.
276
+
277
+ Returns
278
+ -------
279
+ bool
280
+ The JSON-serializable format.
281
+ """
282
+ return bool(data)
283
+
284
+ def from_json_scalar(self, data: JSON, *, zarr_format: ZarrFormat) -> np.bool_:
285
+ """
286
+ Read a JSON-serializable value as a numpy boolean scalar.
287
+
288
+ Parameters
289
+ ----------
290
+ data : JSON
291
+ The JSON-serializable value.
292
+ zarr_format : ZarrFormat
293
+ The zarr format version.
294
+
295
+ Returns
296
+ -------
297
+ bool : np.bool_
298
+ The numpy boolean scalar.
299
+
300
+ Raises
301
+ ------
302
+ TypeError
303
+ If the input is not a valid boolean type.
304
+ """
305
+ if self._check_scalar(data):
306
+ return np.bool_(data)
307
+ raise TypeError(f"Invalid type: {data}. Expected a boolean.") # pragma: no cover
308
+
309
+ @property
310
+ def item_size(self) -> int:
311
+ """
312
+ The size of a single scalar in bytes.
313
+
314
+ Returns
315
+ -------
316
+ int
317
+ The size of a single scalar in bytes.
318
+ """
319
+ return 1
.venv/lib/python3.11/site-packages/zarr/core/dtype/npy/bytes.py ADDED
@@ -0,0 +1,1279 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import base64
4
+ import re
5
+ from dataclasses import dataclass
6
+ from typing import ClassVar, Literal, Self, TypedDict, TypeGuard, cast, overload
7
+
8
+ import numpy as np
9
+
10
+ from zarr.core.common import JSON, NamedConfig, ZarrFormat
11
+ from zarr.core.dtype.common import (
12
+ DataTypeValidationError,
13
+ DTypeConfig_V2,
14
+ DTypeJSON,
15
+ HasItemSize,
16
+ HasLength,
17
+ HasObjectCodec,
18
+ check_dtype_spec_v2,
19
+ v3_unstable_dtype_warning,
20
+ )
21
+ from zarr.core.dtype.npy.common import check_json_str
22
+ from zarr.core.dtype.wrapper import TBaseDType, ZDType
23
+
24
+ BytesLike = np.bytes_ | str | bytes | int
25
+
26
+
27
+ class FixedLengthBytesConfig(TypedDict):
28
+ """
29
+ A configuration for a data type that takes a ``length_bytes`` parameter.
30
+
31
+ Attributes
32
+ ----------
33
+
34
+ length_bytes : int
35
+ The length in bytes of the data associated with this configuration.
36
+
37
+ Examples
38
+ --------
39
+ ```python
40
+ {
41
+ "length_bytes": 12
42
+ }
43
+ ```
44
+ """
45
+
46
+ length_bytes: int
47
+
48
+
49
+ class NullterminatedBytesJSON_V2(DTypeConfig_V2[str, None]):
50
+ """
51
+ A wrapper around the JSON representation of the ``NullTerminatedBytes`` data type in Zarr V2.
52
+
53
+ The ``name`` field of this class contains the value that would appear under the
54
+ ``dtype`` field in Zarr V2 array metadata.
55
+
56
+ References
57
+ ----------
58
+ The structure of the ``name`` field is defined in the Zarr V2
59
+ [specification document](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v2/v2.0.rst#data-type-encoding).
60
+
61
+
62
+ Examples
63
+ --------
64
+ ```python
65
+ {
66
+ "name": "|S10",
67
+ "object_codec_id": None
68
+ }
69
+ ```
70
+ """
71
+
72
+
73
+ class NullTerminatedBytesJSON_V3(
74
+ NamedConfig[Literal["null_terminated_bytes"], FixedLengthBytesConfig]
75
+ ):
76
+ """
77
+ The JSON representation of the ``NullTerminatedBytes`` data type in Zarr V3.
78
+
79
+ References
80
+ ----------
81
+ This representation is not currently defined in an external specification.
82
+
83
+
84
+ Examples
85
+ --------
86
+ ```python
87
+ {
88
+ "name": "null_terminated_bytes",
89
+ "configuration": {
90
+ "length_bytes": 12
91
+ }
92
+ }
93
+ ```
94
+
95
+ """
96
+
97
+
98
+ class RawBytesJSON_V2(DTypeConfig_V2[str, None]):
99
+ """
100
+ A wrapper around the JSON representation of the ``RawBytes`` data type in Zarr V2.
101
+
102
+ The ``name`` field of this class contains the value that would appear under the
103
+ ``dtype`` field in Zarr V2 array metadata.
104
+
105
+ References
106
+ ----------
107
+ The structure of the ``name`` field is defined in the Zarr V2
108
+ [specification document](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v2/v2.0.rst#data-type-encoding).
109
+
110
+
111
+ Examples
112
+ --------
113
+ ```python
114
+
115
+ {
116
+ "name": "|V10",
117
+ "object_codec_id": None
118
+ }
119
+ ```
120
+ """
121
+
122
+
123
+ class RawBytesJSON_V3(NamedConfig[Literal["raw_bytes"], FixedLengthBytesConfig]):
124
+ """
125
+ The JSON representation of the ``RawBytes`` data type in Zarr V3.
126
+
127
+ References
128
+ ----------
129
+ This representation is not currently defined in an external specification.
130
+
131
+
132
+ Examples
133
+ --------
134
+ ```python
135
+ {
136
+ "name": "raw_bytes",
137
+ "configuration": {
138
+ "length_bytes": 12
139
+ }
140
+ }
141
+ ```
142
+ """
143
+
144
+
145
+ class VariableLengthBytesJSON_V2(DTypeConfig_V2[Literal["|O"], Literal["vlen-bytes"]]):
146
+ """
147
+ A wrapper around the JSON representation of the ``VariableLengthBytes`` data type in Zarr V2.
148
+
149
+ The ``name`` field of this class contains the value that would appear under the
150
+ ``dtype`` field in Zarr V2 array metadata. The ``object_codec_id`` field is always ``"vlen-bytes"``
151
+
152
+ References
153
+ ----------
154
+ The structure of the ``name`` field is defined in the Zarr V2
155
+ [specification document](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v2/v2.0.rst#data-type-encoding).
156
+
157
+ Examples
158
+ --------
159
+ ```python
160
+ {
161
+ "name": "|O",
162
+ "object_codec_id": "vlen-bytes"
163
+ }
164
+ ```
165
+ """
166
+
167
+
168
+ @dataclass(frozen=True, kw_only=True)
169
+ class NullTerminatedBytes(ZDType[np.dtypes.BytesDType[int], np.bytes_], HasLength, HasItemSize):
170
+ """
171
+ A Zarr data type for arrays containing fixed-length null-terminated byte sequences.
172
+
173
+ Wraps the [`np.dtypes.BytesDType`][numpy.dtypes.BytesDType] data type. Scalars for this data type are instances of
174
+ [`np.bytes_`][numpy.bytes_].
175
+
176
+ This data type is parametrized by an integral length which specifies size in bytes of each
177
+ scalar. Because this data type uses null-terminated semantics, indexing into
178
+ NumPy arrays with this data type may return fewer than ``length`` bytes.
179
+
180
+ Attributes
181
+ ----------
182
+ dtype_cls: ClassVar[type[np.dtypes.BytesDType[int]]] = np.dtypes.BytesDType
183
+ The NumPy data type wrapped by this ZDType.
184
+ _zarr_v3_name : ClassVar[Literal["null_terminated_bytes"]]
185
+ length : int
186
+ The length of the bytes.
187
+
188
+ Notes
189
+ -----
190
+ This data type is designed for compatibility with NumPy arrays that use the NumPy ``bytes`` data type.
191
+ It may not be desirable for usage outside of that context. If compatibility
192
+ with the NumPy ``bytes`` data type is not essential, consider using the ``RawBytes``
193
+ or ``VariableLengthBytes`` data types instead.
194
+ """
195
+
196
+ dtype_cls = np.dtypes.BytesDType
197
+ _zarr_v3_name: ClassVar[Literal["null_terminated_bytes"]] = "null_terminated_bytes"
198
+
199
+ def __post_init__(self) -> None:
200
+ """
201
+ We don't allow instances of this class with length less than 1 because there is no way such
202
+ a data type can contain actual data.
203
+ """
204
+ if self.length < 1:
205
+ raise ValueError(f"length must be >= 1, got {self.length}.")
206
+
207
+ @classmethod
208
+ def from_native_dtype(cls, dtype: TBaseDType) -> Self:
209
+ """
210
+ Create an instance of NullTerminatedBytes from an instance of np.dtypes.BytesDType.
211
+
212
+ This method checks if the provided data type is an instance of np.dtypes.BytesDType.
213
+ If so, it returns a new instance of NullTerminatedBytes with a length equal to the
214
+ length of input data type.
215
+
216
+ Parameters
217
+ ----------
218
+ dtype : TBaseDType
219
+ The native dtype to convert.
220
+
221
+ Returns
222
+ -------
223
+ NullTerminatedBytes
224
+ An instance of NullTerminatedBytes with the specified length.
225
+
226
+ Raises
227
+ ------
228
+ DataTypeValidationError
229
+ If the dtype is not compatible with NullTerminatedBytes.
230
+ """
231
+
232
+ if cls._check_native_dtype(dtype):
233
+ return cls(length=dtype.itemsize)
234
+ raise DataTypeValidationError(
235
+ f"Invalid data type: {dtype}. Expected an instance of {cls.dtype_cls}"
236
+ )
237
+
238
+ def to_native_dtype(self) -> np.dtypes.BytesDType[int]:
239
+ """
240
+ Create a NumPy bytes dtype from this NullTerminatedBytes ZDType.
241
+
242
+ Returns
243
+ -------
244
+ np.dtypes.BytesDType[int]
245
+ A NumPy data type object representing null-terminated bytes with a specified length.
246
+ """
247
+
248
+ return self.dtype_cls(self.length)
249
+
250
+ @classmethod
251
+ def _check_json_v2(cls, data: DTypeJSON) -> TypeGuard[NullterminatedBytesJSON_V2]:
252
+ """
253
+ Check that the input is a valid JSON representation of NullTerminatedBytes in Zarr V2.
254
+
255
+ The input data must be a mapping that contains a "name" key that matches the pattern
256
+ "|S<number>" and an "object_codec_id" key that is None.
257
+
258
+ Parameters
259
+ ----------
260
+ data : DTypeJSON
261
+ The JSON data to check.
262
+
263
+ Returns
264
+ -------
265
+ bool
266
+ True if the input data is a valid representation, False otherwise.
267
+ """
268
+
269
+ return (
270
+ check_dtype_spec_v2(data)
271
+ and isinstance(data["name"], str)
272
+ and re.match(r"^\|S\d+$", data["name"]) is not None
273
+ and data["object_codec_id"] is None
274
+ )
275
+
276
+ @classmethod
277
+ def _check_json_v3(cls, data: DTypeJSON) -> TypeGuard[NullTerminatedBytesJSON_V3]:
278
+ """
279
+ Check that the input is a valid JSON representation of this class in Zarr V3.
280
+
281
+ Parameters
282
+ ----------
283
+ data : DTypeJSON
284
+ The JSON data to check.
285
+
286
+ Returns
287
+ -------
288
+ TypeGuard[NullTerminatedBytesJSON_V3]
289
+ True if the input is a valid representation of this class in Zarr V3, False
290
+ otherwise.
291
+ """
292
+ return (
293
+ isinstance(data, dict)
294
+ and set(data.keys()) == {"name", "configuration"}
295
+ and data["name"] == cls._zarr_v3_name
296
+ and isinstance(data["configuration"], dict)
297
+ and "length_bytes" in data["configuration"]
298
+ and isinstance(data["configuration"]["length_bytes"], int)
299
+ )
300
+
301
+ @classmethod
302
+ def _from_json_v2(cls, data: DTypeJSON) -> Self:
303
+ """
304
+ Create an instance of this class from Zarr V2-flavored JSON.
305
+
306
+ This method checks if the input data is a valid representation of
307
+ this class in Zarr V2. If so, it returns a new instance of
308
+ this class with a ``length`` as specified in the input data.
309
+
310
+ Parameters
311
+ ----------
312
+ data : DTypeJSON
313
+ The JSON data to parse.
314
+
315
+ Returns
316
+ -------
317
+ Self
318
+ An instance of this data type.
319
+
320
+ Raises
321
+ ------
322
+ DataTypeValidationError
323
+ If the input data is not a valid representation of this class.
324
+ """
325
+
326
+ if cls._check_json_v2(data):
327
+ name = data["name"]
328
+ return cls(length=int(name[2:]))
329
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected a string like '|S1', '|S2', etc"
330
+ raise DataTypeValidationError(msg)
331
+
332
+ @classmethod
333
+ def _from_json_v3(cls, data: DTypeJSON) -> Self:
334
+ """
335
+ Create an instance of this class from Zarr V3-flavored JSON.
336
+
337
+ This method checks if the input data is a valid representation of
338
+ this class in Zarr V3. If so, it returns a new instance of
339
+ this class with a ``length`` as specified in the input data.
340
+
341
+ Parameters
342
+ ----------
343
+ data : DTypeJSON
344
+ The JSON data to parse.
345
+
346
+ Returns
347
+ -------
348
+ Self
349
+ An instance of this data type.
350
+
351
+ Raises
352
+ ------
353
+ DataTypeValidationError
354
+ If the input data is not a valid representation of this class.
355
+ """
356
+ if cls._check_json_v3(data):
357
+ return cls(length=data["configuration"]["length_bytes"])
358
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected the string {cls._zarr_v3_name!r}"
359
+ raise DataTypeValidationError(msg)
360
+
361
+ @overload
362
+ def to_json(self, zarr_format: Literal[2]) -> NullterminatedBytesJSON_V2: ...
363
+
364
+ @overload
365
+ def to_json(self, zarr_format: Literal[3]) -> NullTerminatedBytesJSON_V3: ...
366
+
367
+ def to_json(
368
+ self, zarr_format: ZarrFormat
369
+ ) -> DTypeConfig_V2[str, None] | NullTerminatedBytesJSON_V3:
370
+ """
371
+ Generate a JSON representation of this data type.
372
+
373
+ Parameters
374
+ ----------
375
+ zarr_format : ZarrFormat
376
+ The zarr format version.
377
+
378
+ Returns
379
+ -------
380
+ NullterminatedBytesJSON_V2 | NullTerminatedBytesJSON_V3
381
+ The JSON-serializable representation of the data type
382
+ """
383
+ if zarr_format == 2:
384
+ return {"name": self.to_native_dtype().str, "object_codec_id": None}
385
+ elif zarr_format == 3:
386
+ v3_unstable_dtype_warning(self)
387
+ return {
388
+ "name": self._zarr_v3_name,
389
+ "configuration": {"length_bytes": self.length},
390
+ }
391
+ raise ValueError(f"zarr_format must be 2 or 3, got {zarr_format}") # pragma: no cover
392
+
393
+ def _check_scalar(self, data: object) -> TypeGuard[BytesLike]:
394
+ """
395
+ Check if the provided data is of type BytesLike.
396
+
397
+ This method is used to verify if the input data can be considered as a
398
+ scalar of bytes-like type, which includes NumPy bytes, strings, bytes,
399
+ and integers.
400
+
401
+ Parameters
402
+ ----------
403
+ data : object
404
+ The data to check.
405
+
406
+ Returns
407
+ -------
408
+ TypeGuard[BytesLike]
409
+ True if the data is bytes-like, False otherwise.
410
+ """
411
+
412
+ return isinstance(data, BytesLike)
413
+
414
+ def _cast_scalar_unchecked(self, data: BytesLike) -> np.bytes_:
415
+ """
416
+ Cast the provided scalar data to [`np.bytes_`][numpy.bytes_], truncating if necessary.
417
+
418
+ Parameters
419
+ ----------
420
+ data : BytesLike
421
+ The data to cast.
422
+
423
+ Returns
424
+ -------
425
+ bytes : [`np.bytes_`][numpy.bytes_]
426
+ The casted data as a NumPy bytes scalar.
427
+
428
+ Notes
429
+ -----
430
+ This method does not perform any type checking.
431
+ The input data must be bytes-like.
432
+ """
433
+ if isinstance(data, int):
434
+ return self.to_native_dtype().type(str(data)[: self.length])
435
+ else:
436
+ return self.to_native_dtype().type(data[: self.length])
437
+
438
+ def cast_scalar(self, data: object) -> np.bytes_:
439
+ """
440
+ Attempt to cast a given object to a NumPy bytes scalar.
441
+
442
+ This method first checks if the provided data is a valid scalar that can be
443
+ converted to a NumPy bytes scalar. If the check succeeds, the unchecked casting
444
+ operation is performed. If the data is not valid, a TypeError is raised.
445
+
446
+ Parameters
447
+ ----------
448
+ data : object
449
+ The data to be cast to a NumPy bytes scalar.
450
+
451
+ Returns
452
+ -------
453
+ bytes : [`np.bytes_`][numpy.bytes_]
454
+ The data cast as a NumPy bytes scalar.
455
+
456
+ Raises
457
+ ------
458
+ TypeError
459
+ If the data cannot be converted to a NumPy bytes scalar.
460
+ """
461
+
462
+ if self._check_scalar(data):
463
+ return self._cast_scalar_unchecked(data)
464
+ msg = (
465
+ f"Cannot convert object {data!r} with type {type(data)} to a scalar compatible with the "
466
+ f"data type {self}."
467
+ )
468
+ raise TypeError(msg)
469
+
470
+ def default_scalar(self) -> np.bytes_:
471
+ """
472
+ Return a default scalar value, which for this data type is an empty byte string.
473
+
474
+ Returns
475
+ -------
476
+ bytes : [`np.bytes_`][numpy.bytes_]
477
+ The default scalar value.
478
+ """
479
+ return np.bytes_(b"")
480
+
481
+ def to_json_scalar(self, data: object, *, zarr_format: ZarrFormat) -> str:
482
+ """
483
+ Convert a scalar to a JSON-serializable string representation.
484
+
485
+ This method encodes the given scalar as a NumPy bytes scalar and then
486
+ encodes the bytes as a base64-encoded string.
487
+
488
+ Parameters
489
+ ----------
490
+ data : object
491
+ The scalar to convert.
492
+ zarr_format : ZarrFormat
493
+ The zarr format version.
494
+
495
+ Returns
496
+ -------
497
+ str
498
+ A string representation of the scalar.
499
+ """
500
+ as_bytes = self.cast_scalar(data)
501
+ return base64.standard_b64encode(as_bytes).decode("ascii")
502
+
503
+ def from_json_scalar(self, data: JSON, *, zarr_format: ZarrFormat) -> np.bytes_:
504
+ """
505
+ Read a JSON-serializable value as [`np.bytes_`][numpy.bytes_].
506
+
507
+ Parameters
508
+ ----------
509
+ data : JSON
510
+ The JSON-serializable base64-encoded string.
511
+ zarr_format : ZarrFormat
512
+ The zarr format version.
513
+
514
+ Returns
515
+ -------
516
+ bytes : [`np.bytes_`][numpy.bytes_]
517
+ The NumPy bytes scalar obtained from decoding the base64 string.
518
+
519
+ Raises
520
+ ------
521
+ TypeError
522
+ If the input data is not a base64-encoded string.
523
+ """
524
+
525
+ if check_json_str(data):
526
+ return self.to_native_dtype().type(base64.standard_b64decode(data.encode("ascii")))
527
+ raise TypeError(
528
+ f"Invalid type: {data}. Expected a base64-encoded string."
529
+ ) # pragma: no cover
530
+
531
+ @property
532
+ def item_size(self) -> int:
533
+ """
534
+ The size of a single scalar in bytes.
535
+
536
+ Returns
537
+ -------
538
+ int
539
+ The size of a single scalar in bytes.
540
+ """
541
+ return self.length
542
+
543
+
544
+ @dataclass(frozen=True, kw_only=True)
545
+ class RawBytes(ZDType[np.dtypes.VoidDType[int], np.void], HasLength, HasItemSize):
546
+ """
547
+ A Zarr data type for arrays containing fixed-length sequences of raw bytes.
548
+
549
+ Wraps the NumPy ``void`` data type. Scalars for this data type are instances of [`np.void`][numpy.void].
550
+
551
+ This data type is parametrized by an integral length which specifies size in bytes of each
552
+ scalar belonging to this data type.
553
+
554
+ Attributes
555
+ ----------
556
+ dtype_cls: ClassVar[type[np.dtypes.VoidDType[int]]] = np.dtypes.VoidDtype
557
+ The NumPy data type wrapped by this ZDType.
558
+ _zarr_v3_name : ClassVar[Literal["raw_bytes"]]
559
+ length : int
560
+ The length of the bytes.
561
+
562
+ Notes
563
+ -----
564
+ Although the NumPy "Void" data type is used to create "structured" data types in NumPy, this
565
+ class does not support structured data types.
566
+
567
+ See the ``Structured`` data type for this functionality.
568
+
569
+ """
570
+
571
+ # np.dtypes.VoidDType is specified in an odd way in NumPy
572
+ # it cannot be used to create instances of the dtype
573
+ # so we have to tell mypy to ignore this here
574
+ dtype_cls = np.dtypes.VoidDType # type: ignore[assignment]
575
+ _zarr_v3_name: ClassVar[Literal["raw_bytes"]] = "raw_bytes"
576
+
577
+ def __post_init__(self) -> None:
578
+ """
579
+ We don't allow instances of this class with length less than 1 because there is no way such
580
+ a data type can contain actual data.
581
+ """
582
+ if self.length < 1:
583
+ raise ValueError(f"length must be >= 1, got {self.length}.")
584
+
585
+ @classmethod
586
+ def _check_native_dtype(
587
+ cls: type[Self], dtype: TBaseDType
588
+ ) -> TypeGuard[np.dtypes.VoidDType[int]]:
589
+ """
590
+ Check that the input is a NumPy void dtype with no fields.
591
+
592
+
593
+ Numpy void dtype comes in two forms:
594
+ * If the ``fields`` attribute is ``None``, then the dtype represents N raw bytes.
595
+ * If the ``fields`` attribute is not ``None``, then the dtype represents a structured dtype,
596
+
597
+ In this check we ensure that ``fields`` is ``None``.
598
+
599
+ Parameters
600
+ ----------
601
+ dtype : TDBaseDType
602
+ The dtype to check.
603
+
604
+ Returns
605
+ -------
606
+ Bool
607
+ True if the dtype is an instance of np.dtypes.VoidDType with no fields, False otherwise.
608
+ """
609
+ return cls.dtype_cls is type(dtype) and dtype.fields is None
610
+
611
+ @classmethod
612
+ def from_native_dtype(cls, dtype: TBaseDType) -> Self:
613
+ """
614
+ Create an instance of RawBytes from an instance of np.dtypes.VoidDType.
615
+
616
+ This method checks if the provided data type is compatible with RawBytes. The input
617
+ must be an instance of np.dtypes.VoidDType, and have no fields. If the input is compatible,
618
+ this method returns an instance of RawBytes with the specified length.
619
+
620
+
621
+ Parameters
622
+ ----------
623
+ dtype : TBaseDType
624
+ The native dtype to convert.
625
+
626
+ Returns
627
+ -------
628
+ RawBytes
629
+ An instance of RawBytes with the specified length.
630
+
631
+ Raises
632
+ ------
633
+ DataTypeValidationError
634
+ If the dtype is not compatible with RawBytes.
635
+ """
636
+
637
+ if cls._check_native_dtype(dtype):
638
+ return cls(length=dtype.itemsize)
639
+ raise DataTypeValidationError(
640
+ f"Invalid data type: {dtype}. Expected an instance of {cls.dtype_cls}"
641
+ )
642
+
643
+ def to_native_dtype(self) -> np.dtypes.VoidDType[int]:
644
+ """
645
+ Create a NumPy void dtype from this RawBytes ZDType.
646
+
647
+ Returns
648
+ -------
649
+ np.dtypes.VoidDType[int]
650
+ A NumPy data type object representing raw bytes with a specified length.
651
+ """
652
+ # Numpy does not allow creating a void type
653
+ # by invoking np.dtypes.VoidDType directly
654
+ return cast("np.dtypes.VoidDType[int]", np.dtype(f"V{self.length}"))
655
+
656
+ @classmethod
657
+ def _check_json_v2(cls, data: DTypeJSON) -> TypeGuard[RawBytesJSON_V2]:
658
+ """
659
+ Check that the input is a valid representation of this class in Zarr V2.
660
+
661
+ Parameters
662
+ ----------
663
+ data : DTypeJSON
664
+ The JSON data to check.
665
+
666
+ Returns
667
+ -------
668
+ True if the input is a valid representation of this class in Zarr V3, False otherwise.
669
+
670
+ """
671
+ return (
672
+ check_dtype_spec_v2(data)
673
+ and isinstance(data["name"], str)
674
+ and re.match(r"^\|V\d+$", data["name"]) is not None
675
+ and data["object_codec_id"] is None
676
+ )
677
+
678
+ @classmethod
679
+ def _check_json_v3(cls, data: DTypeJSON) -> TypeGuard[RawBytesJSON_V3]:
680
+ """
681
+ Check that the input is a valid JSON representation of this class in Zarr V3.
682
+
683
+ Parameters
684
+ ----------
685
+ data : DTypeJSON
686
+ The JSON data to check.
687
+
688
+ Returns
689
+ -------
690
+ TypeGuard[RawBytesJSON_V3]
691
+ True if the input is a valid representation of this class in Zarr V3, False
692
+ otherwise.
693
+ """
694
+ return (
695
+ isinstance(data, dict)
696
+ and set(data.keys()) == {"name", "configuration"}
697
+ and data["name"] == cls._zarr_v3_name
698
+ and isinstance(data["configuration"], dict)
699
+ and set(data["configuration"].keys()) == {"length_bytes"}
700
+ and isinstance(data["configuration"]["length_bytes"], int)
701
+ )
702
+
703
+ @classmethod
704
+ def _from_json_v2(cls, data: DTypeJSON) -> Self:
705
+ """
706
+ Create an instance of RawBytes from Zarr V2-flavored JSON.
707
+
708
+ This method checks if the input data is a valid representation of
709
+ RawBytes in Zarr V2. If so, it returns a new instance of
710
+ RawBytes with a ``length`` as specified in the input data.
711
+
712
+ Parameters
713
+ ----------
714
+ data : DTypeJSON
715
+ The JSON data to parse.
716
+
717
+ Returns
718
+ -------
719
+ Self
720
+ An instance of this data type.
721
+
722
+ Raises
723
+ ------
724
+ DataTypeValidationError
725
+ If the input data is not a valid representation of this class.
726
+ """
727
+ if cls._check_json_v2(data):
728
+ name = data["name"]
729
+ return cls(length=int(name[2:]))
730
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected a string like '|V1', '|V2', etc"
731
+ raise DataTypeValidationError(msg)
732
+
733
+ @classmethod
734
+ def _from_json_v3(cls, data: DTypeJSON) -> Self:
735
+ """
736
+ Create an instance of RawBytes from Zarr V3-flavored JSON.
737
+
738
+ This method checks if the input data is a valid representation of
739
+ RawBytes in Zarr V3. If so, it returns a new instance of
740
+ RawBytes with a ``length`` as specified in the input data.
741
+
742
+ Parameters
743
+ ----------
744
+ data : DTypeJSON
745
+ The JSON data to parse.
746
+
747
+ Returns
748
+ -------
749
+ RawBytes
750
+ An instance of RawBytes.
751
+
752
+ Raises
753
+ ------
754
+ DataTypeValidationError
755
+ If the input data is not a valid representation of this class.
756
+ """
757
+ if cls._check_json_v3(data):
758
+ return cls(length=data["configuration"]["length_bytes"])
759
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected the string {cls._zarr_v3_name!r}"
760
+ raise DataTypeValidationError(msg)
761
+
762
+ @overload
763
+ def to_json(self, zarr_format: Literal[2]) -> RawBytesJSON_V2: ...
764
+
765
+ @overload
766
+ def to_json(self, zarr_format: Literal[3]) -> RawBytesJSON_V3: ...
767
+
768
+ def to_json(self, zarr_format: ZarrFormat) -> RawBytesJSON_V2 | RawBytesJSON_V3:
769
+ """
770
+ Generate a JSON representation of this data type.
771
+
772
+ Parameters
773
+ ----------
774
+ zarr_format : ZarrFormat
775
+ The zarr format version.
776
+
777
+ Returns
778
+ -------
779
+ RawBytesJSON_V2 | RawBytesJSON_V3
780
+ The JSON-serializable representation of the data type.
781
+ """
782
+ if zarr_format == 2:
783
+ return {"name": self.to_native_dtype().str, "object_codec_id": None}
784
+ elif zarr_format == 3:
785
+ v3_unstable_dtype_warning(self)
786
+ return {"name": self._zarr_v3_name, "configuration": {"length_bytes": self.length}}
787
+ raise ValueError(f"zarr_format must be 2 or 3, got {zarr_format}") # pragma: no cover
788
+
789
+ def _check_scalar(self, data: object) -> TypeGuard[np.bytes_ | str | bytes | np.void]:
790
+ """
791
+ Check if the provided data can be cast to np.void.
792
+
793
+ This method is used to verify if the input data can be considered as a
794
+ scalar of bytes-like type, which includes np.bytes_, np.void, strings, and bytes objects.
795
+
796
+ Parameters
797
+ ----------
798
+ data : object
799
+ The data to check.
800
+
801
+ Returns
802
+ -------
803
+ TypeGuard[np.bytes_ | str | bytes | np.void]
804
+ True if the data is void-scalar-like, False otherwise.
805
+ """
806
+ return isinstance(data, np.bytes_ | str | bytes | np.void)
807
+
808
+ def _cast_scalar_unchecked(self, data: object) -> np.void:
809
+ """
810
+ Cast the provided scalar data to np.void.
811
+
812
+ Parameters
813
+ ----------
814
+ data : BytesLike
815
+ The data to cast.
816
+
817
+ Returns
818
+ -------
819
+ np.void
820
+ The casted data as a NumPy void scalar.
821
+
822
+ Notes
823
+ -----
824
+ This method does not perform any type checking.
825
+ The input data must be castable to np.void.
826
+ """
827
+ native_dtype = self.to_native_dtype()
828
+ # Without the second argument, NumPy will return a void scalar for dtype V1.
829
+ # The second argument ensures that, if native_dtype is something like V10,
830
+ # the result will actually be a V10 scalar.
831
+ return native_dtype.type(data, native_dtype)
832
+
833
+ def cast_scalar(self, data: object) -> np.void:
834
+ """
835
+ Attempt to cast a given object to a NumPy void scalar.
836
+
837
+ This method first checks if the provided data is a valid scalar that can be
838
+ converted to a NumPy void scalar. If the check succeeds, the unchecked casting
839
+ operation is performed. If the data is not valid, a TypeError is raised.
840
+
841
+ Parameters
842
+ ----------
843
+ data : object
844
+ The data to be cast to a NumPy void scalar.
845
+
846
+ Returns
847
+ -------
848
+ np.void
849
+ The data cast as a NumPy void scalar.
850
+
851
+ Raises
852
+ ------
853
+ TypeError
854
+ If the data cannot be converted to a NumPy void scalar.
855
+ """
856
+ if self._check_scalar(data):
857
+ return self._cast_scalar_unchecked(data)
858
+ msg = (
859
+ f"Cannot convert object {data!r} with type {type(data)} to a scalar compatible with the "
860
+ f"data type {self}."
861
+ )
862
+ raise TypeError(msg)
863
+
864
+ def default_scalar(self) -> np.void:
865
+ """
866
+ Return the default scalar value for this data type.
867
+
868
+ The default scalar is a NumPy void scalar of the same length as the data type,
869
+ filled with zero bytes.
870
+
871
+ Returns
872
+ -------
873
+ np.void
874
+ The default scalar value.
875
+ """
876
+ return self.to_native_dtype().type(("\x00" * self.length).encode("ascii"))
877
+
878
+ def to_json_scalar(self, data: object, *, zarr_format: ZarrFormat) -> str:
879
+ """
880
+ Convert a scalar to a JSON-serializable string representation.
881
+
882
+ This method converts the given scalar to bytes and then
883
+ encodes the bytes as a base64-encoded string.
884
+
885
+ Parameters
886
+ ----------
887
+ data : object
888
+ The scalar to convert.
889
+ zarr_format : ZarrFormat
890
+ The zarr format version.
891
+
892
+ Returns
893
+ -------
894
+ str
895
+ A string representation of the scalar.
896
+ """
897
+ as_bytes = self.cast_scalar(data)
898
+ return base64.standard_b64encode(as_bytes.tobytes()).decode("ascii")
899
+
900
+ def from_json_scalar(self, data: JSON, *, zarr_format: ZarrFormat) -> np.void:
901
+ """
902
+ Read a JSON-serializable value as an np.void.
903
+
904
+ Parameters
905
+ ----------
906
+ data : JSON
907
+ The JSON-serializable value.
908
+ zarr_format : ZarrFormat
909
+ The zarr format version.
910
+
911
+ Returns
912
+ -------
913
+ np.void
914
+ The NumPy void scalar.
915
+
916
+ Raises
917
+ ------
918
+ TypeError
919
+ If the data is not a string, or if the string is not a valid base64 encoding.
920
+ """
921
+ if check_json_str(data):
922
+ return self.to_native_dtype().type(base64.standard_b64decode(data))
923
+ raise TypeError(f"Invalid type: {data}. Expected a string.") # pragma: no cover
924
+
925
+ @property
926
+ def item_size(self) -> int:
927
+ """
928
+ The size of a single scalar in bytes.
929
+
930
+ Returns
931
+ -------
932
+ int
933
+ The size of a single scalar in bytes.
934
+ """
935
+ return self.length
936
+
937
+
938
+ @dataclass(frozen=True, kw_only=True)
939
+ class VariableLengthBytes(ZDType[np.dtypes.ObjectDType, bytes], HasObjectCodec):
940
+ """
941
+ A Zarr data type for arrays containing variable-length sequences of bytes.
942
+
943
+ Wraps the NumPy "object" data type. Scalars for this data type are instances of ``bytes``.
944
+
945
+ Attributes
946
+ ----------
947
+ dtype_cls: ClassVar[type[np.dtypes.ObjectDType]] = np.dtypes.ObjectDType
948
+ The NumPy data type wrapped by this ZDType.
949
+ _zarr_v3_name: ClassVar[Literal["variable_length_bytes"]] = "variable_length_bytes"
950
+ The name of this data type in Zarr V3.
951
+ object_codec_id: ClassVar[Literal["vlen-bytes"]] = "vlen-bytes"
952
+ The object codec ID for this data type.
953
+
954
+ Notes
955
+ -----
956
+ Because this data type uses the NumPy "object" data type, it does not guarantee a compact memory
957
+ representation of array data. Therefore a "vlen-bytes" codec is needed to ensure that the array
958
+ data can be persisted to storage.
959
+ """
960
+
961
+ dtype_cls = np.dtypes.ObjectDType
962
+ _zarr_v3_name: ClassVar[Literal["variable_length_bytes"]] = "variable_length_bytes"
963
+ object_codec_id: ClassVar[Literal["vlen-bytes"]] = "vlen-bytes"
964
+
965
+ @classmethod
966
+ def from_native_dtype(cls, dtype: TBaseDType) -> Self:
967
+ """
968
+ Create an instance of VariableLengthBytes from an instance of np.dtypes.ObjectDType.
969
+
970
+ This method checks if the provided data type is an instance of np.dtypes.ObjectDType.
971
+ If so, it returns an instance of VariableLengthBytes.
972
+
973
+ Parameters
974
+ ----------
975
+ dtype : TBaseDType
976
+ The native dtype to convert.
977
+
978
+ Returns
979
+ -------
980
+ VariableLengthBytes
981
+ An instance of VariableLengthBytes.
982
+
983
+ Raises
984
+ ------
985
+ DataTypeValidationError
986
+ If the dtype is not compatible with VariableLengthBytes.
987
+ """
988
+ if cls._check_native_dtype(dtype):
989
+ return cls()
990
+ raise DataTypeValidationError(
991
+ f"Invalid data type: {dtype}. Expected an instance of {cls.dtype_cls}"
992
+ )
993
+
994
+ def to_native_dtype(self) -> np.dtypes.ObjectDType:
995
+ """
996
+ Create a NumPy object dtype from this VariableLengthBytes ZDType.
997
+
998
+ Returns
999
+ -------
1000
+ np.dtypes.ObjectDType
1001
+ A NumPy data type object representing variable-length bytes.
1002
+ """
1003
+ return self.dtype_cls()
1004
+
1005
+ @classmethod
1006
+ def _check_json_v2(
1007
+ cls,
1008
+ data: DTypeJSON,
1009
+ ) -> TypeGuard[VariableLengthBytesJSON_V2]:
1010
+ """
1011
+ Check that the input is a valid JSON representation of a NumPy O dtype, and that the
1012
+ object codec id is appropriate for variable-length bytes strings.
1013
+
1014
+ Parameters
1015
+ ----------
1016
+ data : DTypeJSON
1017
+ The JSON data to check.
1018
+
1019
+ Returns
1020
+ -------
1021
+ True if the input is a valid representation of this class in Zarr V2, False
1022
+ otherwise.
1023
+ """
1024
+ # Check that the input is a valid JSON representation of a Zarr v2 data type spec.
1025
+ if not check_dtype_spec_v2(data):
1026
+ return False
1027
+
1028
+ # Check that the object codec id is appropriate for variable-length bytes strings.
1029
+ if data["name"] != "|O":
1030
+ return False
1031
+ return data["object_codec_id"] == cls.object_codec_id
1032
+
1033
+ @classmethod
1034
+ def _check_json_v3(cls, data: DTypeJSON) -> TypeGuard[Literal["variable_length_bytes"]]:
1035
+ """
1036
+ Check that the input is a valid JSON representation of this class in Zarr V3.
1037
+
1038
+ Parameters
1039
+ ----------
1040
+ data : DTypeJSON
1041
+ The JSON data to check.
1042
+
1043
+ Returns
1044
+ -------
1045
+ TypeGuard[Literal["variable_length_bytes"]]
1046
+ True if the input is a valid representation of this class in Zarr V3, False otherwise.
1047
+ """
1048
+
1049
+ return data in (cls._zarr_v3_name, "bytes")
1050
+
1051
+ @classmethod
1052
+ def _from_json_v2(cls, data: DTypeJSON) -> Self:
1053
+ """
1054
+ Create an instance of this VariableLengthBytes from Zarr V2-flavored JSON.
1055
+
1056
+ This method checks if the input data is a valid representation of this class
1057
+ in Zarr V2. If so, it returns a new instance this class.
1058
+
1059
+ Parameters
1060
+ ----------
1061
+ data : DTypeJSON
1062
+ The JSON data to parse.
1063
+
1064
+ Returns
1065
+ -------
1066
+ Self
1067
+ An instance of this data type.
1068
+
1069
+ Raises
1070
+ ------
1071
+ DataTypeValidationError
1072
+ If the input data is not a valid representation of this class class.
1073
+ """
1074
+
1075
+ if cls._check_json_v2(data):
1076
+ return cls()
1077
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected the string '|O' and an object_codec_id of {cls.object_codec_id}"
1078
+ raise DataTypeValidationError(msg)
1079
+
1080
+ @classmethod
1081
+ def _from_json_v3(cls, data: DTypeJSON) -> Self:
1082
+ """
1083
+ Create an instance of VariableLengthBytes from Zarr V3-flavored JSON.
1084
+
1085
+ This method checks if the input data is a valid representation of
1086
+ VariableLengthBytes in Zarr V3. If so, it returns a new instance of
1087
+ VariableLengthBytes.
1088
+
1089
+ Parameters
1090
+ ----------
1091
+ data : DTypeJSON
1092
+ The JSON data to parse.
1093
+
1094
+ Returns
1095
+ -------
1096
+ VariableLengthBytes
1097
+ An instance of VariableLengthBytes.
1098
+
1099
+ Raises
1100
+ ------
1101
+ DataTypeValidationError
1102
+ If the input data is not a valid representation of this class.
1103
+ """
1104
+
1105
+ if cls._check_json_v3(data):
1106
+ return cls()
1107
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected the string {cls._zarr_v3_name!r}"
1108
+ raise DataTypeValidationError(msg)
1109
+
1110
+ @overload
1111
+ def to_json(self, zarr_format: Literal[2]) -> VariableLengthBytesJSON_V2: ...
1112
+
1113
+ @overload
1114
+ def to_json(self, zarr_format: Literal[3]) -> Literal["variable_length_bytes"]: ...
1115
+
1116
+ def to_json(
1117
+ self, zarr_format: ZarrFormat
1118
+ ) -> VariableLengthBytesJSON_V2 | Literal["variable_length_bytes"]:
1119
+ """
1120
+ Convert the variable-length bytes data type to a JSON-serializable form.
1121
+
1122
+ Parameters
1123
+ ----------
1124
+ zarr_format : ZarrFormat
1125
+ The zarr format version. Accepted values are 2 and 3.
1126
+
1127
+ Returns
1128
+ -------
1129
+ ``DTypeConfig_V2[Literal["|O"], Literal["vlen-bytes"]] | Literal["variable_length_bytes"]``
1130
+ The JSON-serializable representation of the variable-length bytes data type.
1131
+ For zarr_format 2, returns a dictionary with "name" and "object_codec_id".
1132
+ For zarr_format 3, returns a string identifier "variable_length_bytes".
1133
+
1134
+ Raises
1135
+ ------
1136
+ ValueError
1137
+ If zarr_format is not 2 or 3.
1138
+ """
1139
+
1140
+ if zarr_format == 2:
1141
+ return {"name": "|O", "object_codec_id": self.object_codec_id}
1142
+ elif zarr_format == 3:
1143
+ v3_unstable_dtype_warning(self)
1144
+ return self._zarr_v3_name
1145
+ raise ValueError(f"zarr_format must be 2 or 3, got {zarr_format}") # pragma: no cover
1146
+
1147
+ def default_scalar(self) -> bytes:
1148
+ """
1149
+ Return the default scalar value for the variable-length bytes data type.
1150
+
1151
+ Returns
1152
+ -------
1153
+ bytes
1154
+ The default scalar value, which is an empty byte string.
1155
+ """
1156
+
1157
+ return b""
1158
+
1159
+ def to_json_scalar(self, data: object, *, zarr_format: ZarrFormat) -> str:
1160
+ """
1161
+ Convert a scalar to a JSON-serializable string representation.
1162
+
1163
+ This method encodes the given scalar as bytes and then
1164
+ encodes the bytes as a base64-encoded string.
1165
+
1166
+ Parameters
1167
+ ----------
1168
+ data : object
1169
+ The scalar to convert.
1170
+ zarr_format : ZarrFormat
1171
+ The zarr format version.
1172
+
1173
+ Returns
1174
+ -------
1175
+ str
1176
+ A string representation of the scalar.
1177
+ """
1178
+ return base64.standard_b64encode(data).decode("ascii") # type: ignore[arg-type]
1179
+
1180
+ def from_json_scalar(self, data: JSON, *, zarr_format: ZarrFormat) -> bytes:
1181
+ """
1182
+ Decode a base64-encoded JSON string to bytes.
1183
+
1184
+ Parameters
1185
+ ----------
1186
+ data : JSON
1187
+ The JSON-serializable base64-encoded string.
1188
+ zarr_format : ZarrFormat
1189
+ The zarr format version.
1190
+
1191
+ Returns
1192
+ -------
1193
+ bytes
1194
+ The decoded bytes from the base64 string.
1195
+
1196
+ Raises
1197
+ ------
1198
+ TypeError
1199
+ If the input data is not a base64-encoded string.
1200
+ """
1201
+
1202
+ if check_json_str(data):
1203
+ return base64.standard_b64decode(data.encode("ascii"))
1204
+ raise TypeError(f"Invalid type: {data}. Expected a string.") # pragma: no cover
1205
+
1206
+ def _check_scalar(self, data: object) -> TypeGuard[BytesLike]:
1207
+ """
1208
+ Check if the provided data is of type BytesLike.
1209
+
1210
+ This method is used to verify if the input data can be considered as a
1211
+ scalar of bytes-like type, which includes NumPy bytes, strings, bytes,
1212
+ and integers.
1213
+
1214
+ Parameters
1215
+ ----------
1216
+ data : object
1217
+ The data to check.
1218
+
1219
+ Returns
1220
+ -------
1221
+ TypeGuard[BytesLike]
1222
+ True if the data is bytes-like, False otherwise.
1223
+ """
1224
+ return isinstance(data, BytesLike)
1225
+
1226
+ def _cast_scalar_unchecked(self, data: BytesLike) -> bytes:
1227
+ """
1228
+ Cast the provided scalar data to bytes.
1229
+
1230
+ Parameters
1231
+ ----------
1232
+ data : BytesLike
1233
+ The data to cast.
1234
+
1235
+ Returns
1236
+ -------
1237
+ bytes
1238
+ The casted data as bytes.
1239
+
1240
+ Notes
1241
+ -----
1242
+ This method does not perform any type checking.
1243
+ The input data must be bytes-like.
1244
+ """
1245
+ if isinstance(data, str):
1246
+ return bytes(data, encoding="utf-8")
1247
+ return bytes(data)
1248
+
1249
+ def cast_scalar(self, data: object) -> bytes:
1250
+ """
1251
+ Attempt to cast a given object to a bytes scalar.
1252
+
1253
+ This method first checks if the provided data is a valid scalar that can be
1254
+ converted to a bytes scalar. If the check succeeds, the unchecked casting
1255
+ operation is performed. If the data is not valid, a TypeError is raised.
1256
+
1257
+ Parameters
1258
+ ----------
1259
+ data : object
1260
+ The data to be cast to a bytes scalar.
1261
+
1262
+ Returns
1263
+ -------
1264
+ bytes
1265
+ The data cast as a bytes scalar.
1266
+
1267
+ Raises
1268
+ ------
1269
+ TypeError
1270
+ If the data cannot be converted to a bytes scalar.
1271
+ """
1272
+
1273
+ if self._check_scalar(data):
1274
+ return self._cast_scalar_unchecked(data)
1275
+ msg = (
1276
+ f"Cannot convert object {data!r} with type {type(data)} to a scalar compatible with the "
1277
+ f"data type {self}."
1278
+ )
1279
+ raise TypeError(msg)
.venv/lib/python3.11/site-packages/zarr/core/dtype/npy/common.py ADDED
@@ -0,0 +1,581 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import base64
4
+ import struct
5
+ import sys
6
+ from collections.abc import Sequence
7
+ from typing import (
8
+ TYPE_CHECKING,
9
+ Any,
10
+ Final,
11
+ Literal,
12
+ NewType,
13
+ SupportsComplex,
14
+ SupportsFloat,
15
+ SupportsIndex,
16
+ SupportsInt,
17
+ TypeGuard,
18
+ TypeVar,
19
+ )
20
+
21
+ import numpy as np
22
+
23
+ from zarr.core.dtype.common import (
24
+ ENDIANNESS_STR,
25
+ SPECIAL_FLOAT_STRINGS,
26
+ EndiannessStr,
27
+ JSONFloatV2,
28
+ JSONFloatV3,
29
+ )
30
+
31
+ if TYPE_CHECKING:
32
+ from zarr.core.common import JSON, ZarrFormat
33
+
34
+ IntLike = SupportsInt | SupportsIndex | bytes | str
35
+ FloatLike = SupportsIndex | SupportsFloat | bytes | str
36
+ ComplexLike = SupportsFloat | SupportsIndex | SupportsComplex | bytes | str | None
37
+ DateTimeUnit = Literal[
38
+ "Y", "M", "W", "D", "h", "m", "s", "ms", "us", "μs", "ns", "ps", "fs", "as", "generic"
39
+ ]
40
+ DATETIME_UNIT: Final = (
41
+ "Y",
42
+ "M",
43
+ "W",
44
+ "D",
45
+ "h",
46
+ "m",
47
+ "s",
48
+ "ms",
49
+ "us",
50
+ "μs",
51
+ "ns",
52
+ "ps",
53
+ "fs",
54
+ "as",
55
+ "generic",
56
+ )
57
+
58
+ IntishFloat = NewType("IntishFloat", float)
59
+ """A type for floats that represent integers, like 1.0 (but not 1.1)."""
60
+
61
+ IntishStr = NewType("IntishStr", str)
62
+ """A type for strings that represent integers, like "0" or "42"."""
63
+
64
+ FloatishStr = NewType("FloatishStr", str)
65
+ """A type for strings that represent floats, like "3.14" or "-2.5"."""
66
+
67
+ NumpyEndiannessStr = Literal[">", "<", "="]
68
+ NUMPY_ENDIANNESS_STR: Final = ">", "<", "="
69
+
70
+ TFloatDType_co = TypeVar(
71
+ "TFloatDType_co",
72
+ bound=np.dtypes.Float16DType | np.dtypes.Float32DType | np.dtypes.Float64DType,
73
+ covariant=True,
74
+ )
75
+ TFloatScalar_co = TypeVar(
76
+ "TFloatScalar_co", bound=np.float16 | np.float32 | np.float64, covariant=True
77
+ )
78
+
79
+ TComplexDType_co = TypeVar(
80
+ "TComplexDType_co", bound=np.dtypes.Complex64DType | np.dtypes.Complex128DType, covariant=True
81
+ )
82
+ TComplexScalar_co = TypeVar("TComplexScalar_co", bound=np.complex64 | np.complex128, covariant=True)
83
+
84
+
85
+ def endianness_from_numpy_str(endianness: NumpyEndiannessStr) -> EndiannessStr:
86
+ """
87
+ Convert a numpy endianness string literal to a human-readable literal value.
88
+
89
+ Parameters
90
+ ----------
91
+ endianness : Literal[">", "<", "="]
92
+ The numpy string representation of the endianness.
93
+
94
+ Returns
95
+ -------
96
+ Endianness
97
+ The human-readable representation of the endianness.
98
+
99
+ Raises
100
+ ------
101
+ ValueError
102
+ If the endianness is invalid.
103
+ """
104
+ match endianness:
105
+ case "=":
106
+ # Use the local system endianness
107
+ return sys.byteorder
108
+ case "<":
109
+ return "little"
110
+ case ">":
111
+ return "big"
112
+ raise ValueError(f"Invalid endianness: {endianness!r}. Expected one of {NUMPY_ENDIANNESS_STR}")
113
+
114
+
115
+ def endianness_to_numpy_str(endianness: EndiannessStr) -> NumpyEndiannessStr:
116
+ """
117
+ Convert an endianness literal to its numpy string representation.
118
+
119
+ Parameters
120
+ ----------
121
+ endianness : Endianness
122
+ The endianness to convert.
123
+
124
+ Returns
125
+ -------
126
+ Literal[">", "<"]
127
+ The numpy string representation of the endianness.
128
+
129
+ Raises
130
+ ------
131
+ ValueError
132
+ If the endianness is invalid.
133
+ """
134
+ match endianness:
135
+ case "little":
136
+ return "<"
137
+ case "big":
138
+ return ">"
139
+ raise ValueError(
140
+ f"Invalid endianness: {endianness!r}. Expected one of {ENDIANNESS_STR} or None"
141
+ )
142
+
143
+
144
+ def get_endianness_from_numpy_dtype(dtype: np.dtype[np.generic]) -> EndiannessStr:
145
+ """
146
+ Gets the endianness from a numpy dtype that has an endianness. This function will
147
+ raise a ValueError if the numpy data type does not have a concrete endianness.
148
+ """
149
+ endianness = dtype.byteorder
150
+ if dtype.byteorder in NUMPY_ENDIANNESS_STR:
151
+ return endianness_from_numpy_str(endianness) # type: ignore [arg-type]
152
+ raise ValueError(f"The dtype {dtype} has an unsupported endianness: {endianness}")
153
+
154
+
155
+ def float_from_json_v2(data: JSONFloatV2) -> float:
156
+ """
157
+ Convert a JSON float to a float (Zarr v2).
158
+
159
+ Parameters
160
+ ----------
161
+ data : JSONFloat
162
+ The JSON float to convert.
163
+
164
+ Returns
165
+ -------
166
+ float
167
+ The float value.
168
+ """
169
+ match data:
170
+ case "NaN":
171
+ return float("nan")
172
+ case "Infinity":
173
+ return float("inf")
174
+ case "-Infinity":
175
+ return float("-inf")
176
+ case _:
177
+ return float(data)
178
+
179
+
180
+ def float_from_json_v3(data: JSONFloatV3) -> float:
181
+ """
182
+ Convert a JSON float to a float (v3).
183
+
184
+ Parameters
185
+ ----------
186
+ data : JSONFloat
187
+ The JSON float to convert.
188
+
189
+ Returns
190
+ -------
191
+ float
192
+ The float value.
193
+
194
+ Notes
195
+ -----
196
+ Zarr V3 allows floats to be stored as hex strings. To quote the spec:
197
+ "...for float32, "NaN" is equivalent to "0x7fc00000".
198
+ This representation is the only way to specify a NaN value other than the specific NaN value
199
+ denoted by "NaN"."
200
+ """
201
+
202
+ if isinstance(data, str):
203
+ if data in SPECIAL_FLOAT_STRINGS:
204
+ return float_from_json_v2(data) # type: ignore[arg-type]
205
+ if not data.startswith("0x"):
206
+ msg = (
207
+ f"Invalid float value: {data!r}. Expected a string starting with the hex prefix"
208
+ " '0x', or one of 'NaN', 'Infinity', or '-Infinity'."
209
+ )
210
+ raise ValueError(msg)
211
+ if len(data[2:]) == 4:
212
+ dtype_code = ">e"
213
+ elif len(data[2:]) == 8:
214
+ dtype_code = ">f"
215
+ elif len(data[2:]) == 16:
216
+ dtype_code = ">d"
217
+ else:
218
+ msg = (
219
+ f"Invalid hexadecimal float value: {data!r}. "
220
+ "Expected the '0x' prefix to be followed by 4, 8, or 16 numeral characters"
221
+ )
222
+ raise ValueError(msg)
223
+ return float(struct.unpack(dtype_code, bytes.fromhex(data[2:]))[0])
224
+ return float_from_json_v2(data)
225
+
226
+
227
+ def bytes_from_json(data: str, *, zarr_format: ZarrFormat) -> bytes:
228
+ """
229
+ Convert a JSON string to bytes
230
+
231
+ Parameters
232
+ ----------
233
+ data : str
234
+ The JSON string to convert.
235
+ zarr_format : ZarrFormat
236
+ The zarr format version.
237
+
238
+ Returns
239
+ -------
240
+ bytes
241
+ The bytes.
242
+ """
243
+ if zarr_format == 2:
244
+ return base64.b64decode(data.encode("ascii"))
245
+ # TODO: differentiate these as needed. This is a spec question.
246
+ if zarr_format == 3:
247
+ return base64.b64decode(data.encode("ascii"))
248
+ raise ValueError(f"Invalid zarr format: {zarr_format}. Expected 2 or 3.") # pragma: no cover
249
+
250
+
251
+ def bytes_to_json(data: bytes, zarr_format: ZarrFormat) -> str:
252
+ """
253
+ Convert bytes to JSON.
254
+
255
+ Parameters
256
+ ----------
257
+ data : bytes
258
+ The bytes to store.
259
+ zarr_format : ZarrFormat
260
+ The zarr format version.
261
+
262
+ Returns
263
+ -------
264
+ str
265
+ The bytes encoded as ascii using the base64 alphabet.
266
+ """
267
+ # TODO: decide if we are going to make this implementation zarr format-specific
268
+ return base64.b64encode(data).decode("ascii")
269
+
270
+
271
+ def float_to_json_v2(data: float | np.floating[Any]) -> JSONFloatV2:
272
+ """
273
+ Convert a float to JSON (v2).
274
+
275
+ Parameters
276
+ ----------
277
+ data : float or np.floating
278
+ The float value to convert.
279
+
280
+ Returns
281
+ -------
282
+ JSONFloat
283
+ The JSON representation of the float.
284
+ """
285
+ if np.isnan(data):
286
+ return "NaN"
287
+ elif np.isinf(data):
288
+ return "Infinity" if data > 0 else "-Infinity"
289
+ return float(data)
290
+
291
+
292
+ def float_to_json_v3(data: float | np.floating[Any]) -> JSONFloatV3:
293
+ """
294
+ Convert a float to JSON (v3).
295
+
296
+ Parameters
297
+ ----------
298
+ data : float or np.floating
299
+ The float value to convert.
300
+
301
+ Returns
302
+ -------
303
+ JSONFloat
304
+ The JSON representation of the float.
305
+ """
306
+ # v3 can in principle handle distinct NaN values, but numpy does not represent these explicitly
307
+ # so we just reuse the v2 routine here
308
+ return float_to_json_v2(data)
309
+
310
+
311
+ def complex_float_to_json_v3(
312
+ data: complex | np.complexfloating[Any, Any],
313
+ ) -> tuple[JSONFloatV3, JSONFloatV3]:
314
+ """
315
+ Convert a complex number to JSON as defined by the Zarr V3 spec.
316
+
317
+ Parameters
318
+ ----------
319
+ data : complex or np.complexfloating
320
+ The complex value to convert.
321
+
322
+ Returns
323
+ -------
324
+ tuple[JSONFloat, JSONFloat]
325
+ The JSON representation of the complex number.
326
+ """
327
+ return float_to_json_v3(data.real), float_to_json_v3(data.imag)
328
+
329
+
330
+ def complex_float_to_json_v2(
331
+ data: complex | np.complexfloating[Any, Any],
332
+ ) -> tuple[JSONFloatV2, JSONFloatV2]:
333
+ """
334
+ Convert a complex number to JSON as defined by the Zarr V2 spec.
335
+
336
+ Parameters
337
+ ----------
338
+ data : complex | np.complexfloating
339
+ The complex value to convert.
340
+
341
+ Returns
342
+ -------
343
+ tuple[JSONFloat, JSONFloat]
344
+ The JSON representation of the complex number.
345
+ """
346
+ return float_to_json_v2(data.real), float_to_json_v2(data.imag)
347
+
348
+
349
+ def complex_float_from_json_v2(data: tuple[JSONFloatV2, JSONFloatV2]) -> complex:
350
+ """
351
+ Convert a JSON complex float to a complex number (v2).
352
+
353
+ Parameters
354
+ ----------
355
+ data : tuple[JSONFloat, JSONFloat]
356
+ The JSON complex float to convert.
357
+
358
+ Returns
359
+ -------
360
+ np.complexfloating
361
+ The complex number.
362
+ """
363
+ return complex(float_from_json_v2(data[0]), float_from_json_v2(data[1]))
364
+
365
+
366
+ def complex_float_from_json_v3(data: tuple[JSONFloatV3, JSONFloatV3]) -> complex:
367
+ """
368
+ Convert a JSON complex float to a complex number (v3).
369
+
370
+ Parameters
371
+ ----------
372
+ data : tuple[JSONFloat, JSONFloat]
373
+ The JSON complex float to convert.
374
+
375
+ Returns
376
+ -------
377
+ np.complexfloating
378
+ The complex number.
379
+ """
380
+ return complex(float_from_json_v3(data[0]), float_from_json_v3(data[1]))
381
+
382
+
383
+ def check_json_float_v2(data: JSON) -> TypeGuard[JSONFloatV2]:
384
+ """
385
+ Check if a JSON value represents a float (v2).
386
+
387
+ Parameters
388
+ ----------
389
+ data : JSON
390
+ The JSON value to check.
391
+
392
+ Returns
393
+ -------
394
+ Bool
395
+ True if the data is a float, False otherwise.
396
+ """
397
+ return data in ("NaN", "Infinity", "-Infinity") or isinstance(data, float | int)
398
+
399
+
400
+ def check_json_float_v3(data: JSON) -> TypeGuard[JSONFloatV3]:
401
+ """
402
+ Check if a JSON value represents a float (v3).
403
+
404
+ Parameters
405
+ ----------
406
+ data : JSON
407
+ The JSON value to check.
408
+
409
+ Returns
410
+ -------
411
+ Bool
412
+ True if the data is a float, False otherwise.
413
+ """
414
+ return check_json_float_v2(data) or (isinstance(data, str) and data.startswith("0x"))
415
+
416
+
417
+ def check_json_complex_float_v2(data: JSON) -> TypeGuard[tuple[JSONFloatV2, JSONFloatV2]]:
418
+ """
419
+ Check if a JSON value represents a complex float, as per the behavior of zarr-python 2.x
420
+
421
+ Parameters
422
+ ----------
423
+ data : JSON
424
+ The JSON value to check.
425
+
426
+ Returns
427
+ -------
428
+ Bool
429
+ True if the data is a complex float, False otherwise.
430
+ """
431
+ return (
432
+ not isinstance(data, str)
433
+ and isinstance(data, Sequence)
434
+ and len(data) == 2
435
+ and check_json_float_v2(data[0])
436
+ and check_json_float_v2(data[1])
437
+ )
438
+
439
+
440
+ def check_json_complex_float_v3(data: JSON) -> TypeGuard[tuple[JSONFloatV3, JSONFloatV3]]:
441
+ """
442
+ Check if a JSON value represents a complex float, as per the zarr v3 spec
443
+
444
+ Parameters
445
+ ----------
446
+ data : JSON
447
+ The JSON value to check.
448
+
449
+ Returns
450
+ -------
451
+ Bool
452
+ True if the data is a complex float, False otherwise.
453
+ """
454
+ return (
455
+ not isinstance(data, str)
456
+ and isinstance(data, Sequence)
457
+ and len(data) == 2
458
+ and check_json_float_v3(data[0])
459
+ and check_json_float_v3(data[1])
460
+ )
461
+
462
+
463
+ def check_json_int(data: JSON) -> TypeGuard[int]:
464
+ """
465
+ Check if a JSON value is an integer.
466
+
467
+ Parameters
468
+ ----------
469
+ data : JSON
470
+ The JSON value to check.
471
+
472
+ Returns
473
+ -------
474
+ Bool
475
+ True if the data is an integer, False otherwise.
476
+ """
477
+ return bool(isinstance(data, int))
478
+
479
+
480
+ def check_json_intish_float(data: JSON) -> TypeGuard[IntishFloat]:
481
+ """
482
+ Check if a JSON value is an "intish float", i.e. a float that represents an integer, like 0.0.
483
+
484
+ Parameters
485
+ ----------
486
+ data : JSON
487
+ The JSON value to check.
488
+
489
+ Returns
490
+ -------
491
+ Bool
492
+ True if the data is an intish float, False otherwise.
493
+ """
494
+ return isinstance(data, float) and data.is_integer()
495
+
496
+
497
+ def check_json_intish_str(data: JSON) -> TypeGuard[IntishStr]:
498
+ """
499
+ Check if a JSON value is a string that represents an integer, like "0", "42", or "-5".
500
+
501
+ Parameters
502
+ ----------
503
+ data : JSON
504
+ The JSON value to check.
505
+
506
+ Returns
507
+ -------
508
+ bool
509
+ True if the data is a string representing an integer, False otherwise.
510
+ """
511
+ if not isinstance(data, str):
512
+ return False
513
+
514
+ try:
515
+ int(data)
516
+ except ValueError:
517
+ return False
518
+ else:
519
+ return True
520
+
521
+
522
+ def check_json_floatish_str(data: JSON) -> TypeGuard[FloatishStr]:
523
+ """
524
+ Check if a JSON value is a string that represents a float, like "3.14", "-2.5", or "0.0".
525
+
526
+ Note: This function is intended to be used AFTER check_json_float_v2/v3, so it only
527
+ handles regular string representations that those functions don't cover.
528
+
529
+ Parameters
530
+ ----------
531
+ data : JSON
532
+ The JSON value to check.
533
+
534
+ Returns
535
+ -------
536
+ bool
537
+ True if the data is a string representing a regular float, False otherwise.
538
+ """
539
+ if not isinstance(data, str):
540
+ return False
541
+
542
+ try:
543
+ float(data)
544
+ except ValueError:
545
+ return False
546
+ else:
547
+ return True
548
+
549
+
550
+ def check_json_str(data: JSON) -> TypeGuard[str]:
551
+ """
552
+ Check if a JSON value is a string.
553
+
554
+ Parameters
555
+ ----------
556
+ data : JSON
557
+ The JSON value to check.
558
+
559
+ Returns
560
+ -------
561
+ Bool
562
+ True if the data is a string, False otherwise.
563
+ """
564
+ return bool(isinstance(data, str))
565
+
566
+
567
+ def check_json_bool(data: JSON) -> TypeGuard[bool]:
568
+ """
569
+ Check if a JSON value is a boolean.
570
+
571
+ Parameters
572
+ ----------
573
+ data : JSON
574
+ The JSON value to check.
575
+
576
+ Returns
577
+ -------
578
+ Bool
579
+ True if the data is a boolean, False otherwise.
580
+ """
581
+ return isinstance(data, bool)
.venv/lib/python3.11/site-packages/zarr/core/dtype/npy/complex.py ADDED
@@ -0,0 +1,418 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import (
5
+ TYPE_CHECKING,
6
+ ClassVar,
7
+ Literal,
8
+ Self,
9
+ TypeGuard,
10
+ overload,
11
+ )
12
+
13
+ import numpy as np
14
+
15
+ from zarr.core.dtype.common import (
16
+ DataTypeValidationError,
17
+ DTypeConfig_V2,
18
+ DTypeJSON,
19
+ HasEndianness,
20
+ HasItemSize,
21
+ check_dtype_spec_v2,
22
+ )
23
+ from zarr.core.dtype.npy.common import (
24
+ ComplexLike,
25
+ TComplexDType_co,
26
+ TComplexScalar_co,
27
+ check_json_complex_float_v2,
28
+ check_json_complex_float_v3,
29
+ complex_float_from_json_v2,
30
+ complex_float_from_json_v3,
31
+ complex_float_to_json_v2,
32
+ complex_float_to_json_v3,
33
+ endianness_to_numpy_str,
34
+ get_endianness_from_numpy_dtype,
35
+ )
36
+ from zarr.core.dtype.wrapper import TBaseDType, ZDType
37
+
38
+ if TYPE_CHECKING:
39
+ from zarr.core.common import JSON, ZarrFormat
40
+
41
+
42
+ @dataclass(frozen=True)
43
+ class BaseComplex(ZDType[TComplexDType_co, TComplexScalar_co], HasEndianness, HasItemSize):
44
+ """
45
+ A base class for Zarr data types that wrap NumPy complex float data types.
46
+ """
47
+
48
+ # This attribute holds the possible zarr v2 JSON names for the data type
49
+ _zarr_v2_names: ClassVar[tuple[str, ...]]
50
+
51
+ @classmethod
52
+ def from_native_dtype(cls, dtype: TBaseDType) -> Self:
53
+ """
54
+ Create an instance of this data type from a NumPy complex dtype.
55
+
56
+ Parameters
57
+ ----------
58
+ dtype : TBaseDType
59
+ The native dtype to convert.
60
+
61
+ Returns
62
+ -------
63
+ Self
64
+ An instance of this data type.
65
+
66
+ Raises
67
+ ------
68
+ DataTypeValidationError
69
+ If the dtype is not compatible with this data type.
70
+ """
71
+ if cls._check_native_dtype(dtype):
72
+ return cls(endianness=get_endianness_from_numpy_dtype(dtype))
73
+ raise DataTypeValidationError(
74
+ f"Invalid data type: {dtype}. Expected an instance of {cls.dtype_cls}"
75
+ )
76
+
77
+ def to_native_dtype(self) -> TComplexDType_co:
78
+ """
79
+ Convert this class to a NumPy complex dtype with the appropriate byte order.
80
+
81
+ Returns
82
+ -------
83
+ TComplexDType_co
84
+ A NumPy data type object representing the complex data type with the specified byte order.
85
+ """
86
+
87
+ byte_order = endianness_to_numpy_str(self.endianness)
88
+ return self.dtype_cls().newbyteorder(byte_order) # type: ignore[return-value]
89
+
90
+ @classmethod
91
+ def _check_json_v2(cls, data: DTypeJSON) -> TypeGuard[DTypeConfig_V2[str, None]]:
92
+ """
93
+ Check that the input is a valid JSON representation of this data type.
94
+
95
+ The input data must be a mapping that contains a "name" key that is one of
96
+ the strings from cls._zarr_v2_names and an "object_codec_id" key that is None.
97
+
98
+ Parameters
99
+ ----------
100
+ data : DTypeJSON
101
+ The JSON data to check.
102
+
103
+ Returns
104
+ -------
105
+ bool
106
+ True if the input is a valid JSON representation, False otherwise.
107
+ """
108
+ return (
109
+ check_dtype_spec_v2(data)
110
+ and data["name"] in cls._zarr_v2_names
111
+ and data["object_codec_id"] is None
112
+ )
113
+
114
+ @classmethod
115
+ def _check_json_v3(cls, data: DTypeJSON) -> TypeGuard[str]:
116
+ """
117
+ Check that the input is a valid JSON representation of this data type in Zarr V3.
118
+
119
+ This method verifies that the provided data matches the expected Zarr V3
120
+ representation, which is the string specified by the class-level attribute _zarr_v3_name.
121
+
122
+ Parameters
123
+ ----------
124
+ data : DTypeJSON
125
+ The JSON data to check.
126
+
127
+ Returns
128
+ -------
129
+ TypeGuard[str]
130
+ True if the input is a valid representation of this class in Zarr V3, False otherwise.
131
+ """
132
+
133
+ return data == cls._zarr_v3_name
134
+
135
+ @classmethod
136
+ def _from_json_v2(cls, data: DTypeJSON) -> Self:
137
+ """
138
+ Create an instance of this class from Zarr V2-flavored JSON.
139
+
140
+ Parameters
141
+ ----------
142
+ data : DTypeJSON
143
+ The JSON data.
144
+
145
+ Returns
146
+ -------
147
+ Self
148
+ An instance of this class.
149
+
150
+ Raises
151
+ ------
152
+ DataTypeValidationError
153
+ If the input JSON is not a valid representation of this class.
154
+ """
155
+ if cls._check_json_v2(data):
156
+ # Going via numpy ensures that we get the endianness correct without
157
+ # annoying string parsing.
158
+ name = data["name"]
159
+ return cls.from_native_dtype(np.dtype(name))
160
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected one of the strings {cls._zarr_v2_names}."
161
+ raise DataTypeValidationError(msg)
162
+
163
+ @classmethod
164
+ def _from_json_v3(cls, data: DTypeJSON) -> Self:
165
+ """
166
+ Create an instance of this class from Zarr V3-flavored JSON.
167
+
168
+ Parameters
169
+ ----------
170
+ data : DTypeJSON
171
+ The JSON data.
172
+
173
+ Returns
174
+ -------
175
+ Self
176
+ An instance of this data type.
177
+
178
+ Raises
179
+ ------
180
+ DataTypeValidationError
181
+ If the input JSON is not a valid representation of this class.
182
+ """
183
+ if cls._check_json_v3(data):
184
+ return cls()
185
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected {cls._zarr_v3_name}."
186
+ raise DataTypeValidationError(msg)
187
+
188
+ @overload
189
+ def to_json(self, zarr_format: Literal[2]) -> DTypeConfig_V2[str, None]: ...
190
+
191
+ @overload
192
+ def to_json(self, zarr_format: Literal[3]) -> str: ...
193
+
194
+ def to_json(self, zarr_format: ZarrFormat) -> DTypeConfig_V2[str, None] | str:
195
+ """
196
+ Serialize this object to a JSON-serializable representation.
197
+
198
+ Parameters
199
+ ----------
200
+ zarr_format : ZarrFormat
201
+ The Zarr format version. Supported values are 2 and 3.
202
+
203
+ Returns
204
+ -------
205
+ DTypeConfig_V2[str, None] | str
206
+ If ``zarr_format`` is 2, a dictionary with ``"name"`` and ``"object_codec_id"`` keys is
207
+ returned.
208
+ If ``zarr_format`` is 3, a string representation of the complex data type is returned.
209
+
210
+ Raises
211
+ ------
212
+ ValueError
213
+ If `zarr_format` is not 2 or 3.
214
+ """
215
+
216
+ if zarr_format == 2:
217
+ return {"name": self.to_native_dtype().str, "object_codec_id": None}
218
+ elif zarr_format == 3:
219
+ return self._zarr_v3_name
220
+ raise ValueError(f"zarr_format must be 2 or 3, got {zarr_format}") # pragma: no cover
221
+
222
+ def _check_scalar(self, data: object) -> TypeGuard[ComplexLike]:
223
+ """
224
+ Check that the input is a scalar complex value.
225
+
226
+ Parameters
227
+ ----------
228
+ data : object
229
+ The value to check.
230
+
231
+ Returns
232
+ -------
233
+ TypeGuard[ComplexLike]
234
+ True if the input is a scalar complex value, False otherwise.
235
+ """
236
+ return isinstance(data, ComplexLike)
237
+
238
+ def _cast_scalar_unchecked(self, data: ComplexLike) -> TComplexScalar_co:
239
+ """
240
+ Cast the provided scalar data to the native scalar type of this class.
241
+
242
+ Parameters
243
+ ----------
244
+ data : ComplexLike
245
+ The data to cast.
246
+
247
+ Returns
248
+ -------
249
+ TComplexScalar_co
250
+ The casted data as a numpy complex scalar.
251
+
252
+ Notes
253
+ -----
254
+ This method does not perform any type checking.
255
+ The input data must be a scalar complex value.
256
+ """
257
+ return self.to_native_dtype().type(data) # type: ignore[return-value]
258
+
259
+ def cast_scalar(self, data: object) -> TComplexScalar_co:
260
+ """
261
+ Attempt to cast a given object to a numpy complex scalar.
262
+
263
+ Parameters
264
+ ----------
265
+ data : object
266
+ The data to be cast to a numpy complex scalar.
267
+
268
+ Returns
269
+ -------
270
+ TComplexScalar_co
271
+ The data cast as a numpy complex scalar.
272
+
273
+ Raises
274
+ ------
275
+ TypeError
276
+ If the data cannot be converted to a numpy complex scalar.
277
+ """
278
+ if self._check_scalar(data):
279
+ return self._cast_scalar_unchecked(data)
280
+ msg = (
281
+ f"Cannot convert object {data!r} with type {type(data)} to a scalar compatible with the "
282
+ f"data type {self}."
283
+ )
284
+ raise TypeError(msg)
285
+
286
+ def default_scalar(self) -> TComplexScalar_co:
287
+ """
288
+ Get the default value, which is 0 cast to this dtype
289
+
290
+ Returns
291
+ -------
292
+ Int scalar
293
+ The default value.
294
+ """
295
+ return self._cast_scalar_unchecked(0)
296
+
297
+ def from_json_scalar(self, data: JSON, *, zarr_format: ZarrFormat) -> TComplexScalar_co:
298
+ """
299
+ Read a JSON-serializable value as a numpy float.
300
+
301
+ Parameters
302
+ ----------
303
+ data : JSON
304
+ The JSON-serializable value.
305
+ zarr_format : ZarrFormat
306
+ The zarr format version.
307
+
308
+ Returns
309
+ -------
310
+ TScalar_co
311
+ The numpy float.
312
+ """
313
+ if zarr_format == 2:
314
+ if check_json_complex_float_v2(data):
315
+ return self._cast_scalar_unchecked(complex_float_from_json_v2(data))
316
+ raise TypeError(
317
+ f"Invalid type: {data}. Expected a float or a special string encoding of a float."
318
+ )
319
+ elif zarr_format == 3:
320
+ if check_json_complex_float_v3(data):
321
+ return self._cast_scalar_unchecked(complex_float_from_json_v3(data))
322
+ raise TypeError(
323
+ f"Invalid type: {data}. Expected a float or a special string encoding of a float."
324
+ )
325
+ raise ValueError(f"zarr_format must be 2 or 3, got {zarr_format}") # pragma: no cover
326
+
327
+ def to_json_scalar(self, data: object, *, zarr_format: ZarrFormat) -> JSON:
328
+ """
329
+ Convert an object to a JSON-serializable float.
330
+
331
+ Parameters
332
+ ----------
333
+ data : _BaseScalar
334
+ The value to convert.
335
+ zarr_format : ZarrFormat
336
+ The zarr format version.
337
+
338
+ Returns
339
+ -------
340
+ JSON
341
+ The JSON-serializable form of the complex number, which is a list of two floats,
342
+ each of which is encoding according to a zarr-format-specific encoding.
343
+ """
344
+ if zarr_format == 2:
345
+ return complex_float_to_json_v2(self.cast_scalar(data))
346
+ elif zarr_format == 3:
347
+ return complex_float_to_json_v3(self.cast_scalar(data))
348
+ raise ValueError(f"zarr_format must be 2 or 3, got {zarr_format}") # pragma: no cover
349
+
350
+
351
+ @dataclass(frozen=True, kw_only=True)
352
+ class Complex64(BaseComplex[np.dtypes.Complex64DType, np.complex64]):
353
+ """
354
+ A Zarr data type for arrays containing 64 bit complex floats.
355
+
356
+ Wraps the [`np.dtypes.Complex64DType`][numpy.dtypes.Complex64DType] data type. Scalars for this data type
357
+ are instances of [`np.complex64`][numpy.complex64].
358
+
359
+ Attributes
360
+ ----------
361
+ dtype_cls : Type[np.dtypes.Complex64DType]
362
+ The numpy dtype class for this data type.
363
+ _zarr_v3_name : ClassVar[Literal["complex64"]]
364
+ The name of this data type in Zarr V3.
365
+ _zarr_v2_names : ClassVar[tuple[Literal[">c8"], Literal["<c8"]]]
366
+ The names of this data type in Zarr V2.
367
+ """
368
+
369
+ dtype_cls = np.dtypes.Complex64DType
370
+ _zarr_v3_name: ClassVar[Literal["complex64"]] = "complex64"
371
+ _zarr_v2_names: ClassVar[tuple[Literal[">c8"], Literal["<c8"]]] = (">c8", "<c8")
372
+
373
+ @property
374
+ def item_size(self) -> int:
375
+ """
376
+ The size of a single scalar in bytes.
377
+
378
+ Returns
379
+ -------
380
+ int
381
+ The size of a single scalar in bytes.
382
+ """
383
+ return 8
384
+
385
+
386
+ @dataclass(frozen=True, kw_only=True)
387
+ class Complex128(BaseComplex[np.dtypes.Complex128DType, np.complex128], HasEndianness):
388
+ """
389
+ A Zarr data type for arrays containing 64 bit complex floats.
390
+
391
+ Wraps the [`np.dtypes.Complex128DType`][numpy.dtypes.Complex128DType] data type. Scalars for this data type
392
+ are instances of [`np.complex128`][numpy.complex128].
393
+
394
+ Attributes
395
+ ----------
396
+ dtype_cls : Type[np.dtypes.Complex128DType]
397
+ The numpy dtype class for this data type.
398
+ _zarr_v3_name : ClassVar[Literal["complex128"]]
399
+ The name of this data type in Zarr V3.
400
+ _zarr_v2_names : ClassVar[tuple[Literal[">c16"], Literal["<c16"]]]
401
+ The names of this data type in Zarr V2.
402
+ """
403
+
404
+ dtype_cls = np.dtypes.Complex128DType
405
+ _zarr_v3_name: ClassVar[Literal["complex128"]] = "complex128"
406
+ _zarr_v2_names: ClassVar[tuple[Literal[">c16"], Literal["<c16"]]] = (">c16", "<c16")
407
+
408
+ @property
409
+ def item_size(self) -> int:
410
+ """
411
+ The size of a single scalar in bytes.
412
+
413
+ Returns
414
+ -------
415
+ int
416
+ The size of a single scalar in bytes.
417
+ """
418
+ return 16
.venv/lib/python3.11/site-packages/zarr/core/dtype/npy/float.py ADDED
@@ -0,0 +1,435 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import TYPE_CHECKING, ClassVar, Literal, Self, TypeGuard, overload
5
+
6
+ import numpy as np
7
+
8
+ from zarr.core.dtype.common import (
9
+ DataTypeValidationError,
10
+ DTypeConfig_V2,
11
+ DTypeJSON,
12
+ HasEndianness,
13
+ HasItemSize,
14
+ check_dtype_spec_v2,
15
+ )
16
+ from zarr.core.dtype.npy.common import (
17
+ FloatLike,
18
+ TFloatDType_co,
19
+ TFloatScalar_co,
20
+ check_json_float_v2,
21
+ check_json_float_v3,
22
+ check_json_floatish_str,
23
+ endianness_to_numpy_str,
24
+ float_from_json_v2,
25
+ float_from_json_v3,
26
+ float_to_json_v2,
27
+ float_to_json_v3,
28
+ get_endianness_from_numpy_dtype,
29
+ )
30
+ from zarr.core.dtype.wrapper import TBaseDType, ZDType
31
+
32
+ if TYPE_CHECKING:
33
+ from zarr.core.common import JSON, ZarrFormat
34
+
35
+
36
+ @dataclass(frozen=True)
37
+ class BaseFloat(ZDType[TFloatDType_co, TFloatScalar_co], HasEndianness, HasItemSize):
38
+ """
39
+ A base class for Zarr data types that wrap NumPy float data types.
40
+ """
41
+
42
+ # This attribute holds the possible zarr v2 JSON names for the data type
43
+ _zarr_v2_names: ClassVar[tuple[str, ...]]
44
+
45
+ @classmethod
46
+ def from_native_dtype(cls, dtype: TBaseDType) -> Self:
47
+ """
48
+ Create an instance of this ZDType from a NumPy data type.
49
+
50
+ Parameters
51
+ ----------
52
+ dtype : TBaseDType
53
+ The NumPy data type.
54
+
55
+ Returns
56
+ -------
57
+ Self
58
+ An instance of this data type.
59
+ """
60
+ if cls._check_native_dtype(dtype):
61
+ return cls(endianness=get_endianness_from_numpy_dtype(dtype))
62
+ raise DataTypeValidationError(
63
+ f"Invalid data type: {dtype}. Expected an instance of {cls.dtype_cls}"
64
+ )
65
+
66
+ def to_native_dtype(self) -> TFloatDType_co:
67
+ """
68
+ Convert the wrapped data type to a NumPy data type.
69
+
70
+ Returns
71
+ -------
72
+ TFloatDType_co
73
+ The NumPy data type.
74
+ """
75
+ byte_order = endianness_to_numpy_str(self.endianness)
76
+ return self.dtype_cls().newbyteorder(byte_order) # type: ignore[return-value]
77
+
78
+ @classmethod
79
+ def _check_json_v2(cls, data: DTypeJSON) -> TypeGuard[DTypeConfig_V2[str, None]]:
80
+ """
81
+ Check that the input is a valid JSON representation of this data type.
82
+
83
+ Parameters
84
+ ----------
85
+ data : DTypeJSON
86
+ The JSON data.
87
+
88
+ Returns
89
+ -------
90
+ TypeGuard[DTypeConfig_V2[str, None]]
91
+ True if the input is a valid JSON representation of this data type, False otherwise.
92
+ """
93
+ return (
94
+ check_dtype_spec_v2(data)
95
+ and data["name"] in cls._zarr_v2_names
96
+ and data["object_codec_id"] is None
97
+ )
98
+
99
+ @classmethod
100
+ def _check_json_v3(cls, data: DTypeJSON) -> TypeGuard[str]:
101
+ """
102
+ Check that the input is a valid JSON representation of this class in Zarr V3.
103
+
104
+ Parameters
105
+ ----------
106
+ data : DTypeJSON
107
+ The JSON data.
108
+
109
+ Returns
110
+ -------
111
+ TypeGuard[str]
112
+ True if the input is a valid JSON representation of this class, False otherwise.
113
+ """
114
+ return data == cls._zarr_v3_name
115
+
116
+ @classmethod
117
+ def _from_json_v2(cls, data: DTypeJSON) -> Self:
118
+ """
119
+ Create an instance of this ZDType from Zarr v2-flavored JSON.
120
+
121
+ Parameters
122
+ ----------
123
+ data : DTypeJSON
124
+ The JSON data.
125
+
126
+ Returns
127
+ -------
128
+ Self
129
+ An instance of this data type.
130
+ """
131
+ if cls._check_json_v2(data):
132
+ # Going via NumPy ensures that we get the endianness correct without
133
+ # annoying string parsing.
134
+ name = data["name"]
135
+ return cls.from_native_dtype(np.dtype(name))
136
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected one of the strings {cls._zarr_v2_names}."
137
+ raise DataTypeValidationError(msg)
138
+
139
+ @classmethod
140
+ def _from_json_v3(cls, data: DTypeJSON) -> Self:
141
+ """
142
+ Create an instance of this ZDType from Zarr v3-flavored JSON.
143
+
144
+ Parameters
145
+ ----------
146
+ data : DTypeJSON
147
+ The JSON data.
148
+
149
+ Returns
150
+ -------
151
+ Self
152
+ An instance of this data type.
153
+ """
154
+ if cls._check_json_v3(data):
155
+ return cls()
156
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected {cls._zarr_v3_name}."
157
+ raise DataTypeValidationError(msg)
158
+
159
+ @overload
160
+ def to_json(self, zarr_format: Literal[2]) -> DTypeConfig_V2[str, None]: ...
161
+
162
+ @overload
163
+ def to_json(self, zarr_format: Literal[3]) -> str: ...
164
+
165
+ def to_json(self, zarr_format: ZarrFormat) -> DTypeConfig_V2[str, None] | str:
166
+ """
167
+ Convert the wrapped data type to a JSON-serializable form.
168
+
169
+ Parameters
170
+ ----------
171
+ zarr_format : ZarrFormat
172
+ The zarr format version.
173
+
174
+ Returns
175
+ -------
176
+ DTypeConfig_V2[str, None] or str
177
+ The JSON-serializable representation of the wrapped data type.
178
+
179
+ Raises
180
+ ------
181
+ ValueError
182
+ If zarr_format is not 2 or 3.
183
+ """
184
+ if zarr_format == 2:
185
+ return {"name": self.to_native_dtype().str, "object_codec_id": None}
186
+ elif zarr_format == 3:
187
+ return self._zarr_v3_name
188
+ raise ValueError(f"zarr_format must be 2 or 3, got {zarr_format}") # pragma: no cover
189
+
190
+ def _check_scalar(self, data: object) -> TypeGuard[FloatLike]:
191
+ """
192
+ Check that the input is a valid scalar value.
193
+
194
+ Parameters
195
+ ----------
196
+ data : object
197
+ The input to check.
198
+
199
+ Returns
200
+ -------
201
+ TypeGuard[FloatLike]
202
+ True if the input is a valid scalar value, False otherwise.
203
+ """
204
+ if isinstance(data, str):
205
+ # Only accept strings that are valid float representations (e.g. "NaN", "inf").
206
+ # Plain strings that cannot be converted should return False so that cast_scalar
207
+ # raises TypeError rather than a confusing ValueError.
208
+ try:
209
+ self.to_native_dtype().type(data)
210
+ except (ValueError, OverflowError):
211
+ return False
212
+ else:
213
+ return True
214
+ return isinstance(data, FloatLike)
215
+
216
+ def _cast_scalar_unchecked(self, data: FloatLike) -> TFloatScalar_co:
217
+ """
218
+ Cast a scalar value to a NumPy float scalar.
219
+
220
+ Parameters
221
+ ----------
222
+ data : FloatLike
223
+ The scalar value to cast.
224
+
225
+ Returns
226
+ -------
227
+ TFloatScalar_co
228
+ The NumPy float scalar.
229
+ """
230
+ return self.to_native_dtype().type(data) # type: ignore[return-value]
231
+
232
+ def cast_scalar(self, data: object) -> TFloatScalar_co:
233
+ """
234
+ Cast a scalar value to a NumPy float scalar.
235
+
236
+ Parameters
237
+ ----------
238
+ data : object
239
+ The scalar value to cast.
240
+
241
+ Returns
242
+ -------
243
+ TFloatScalar_co
244
+ The NumPy float scalar.
245
+ """
246
+ if self._check_scalar(data):
247
+ return self._cast_scalar_unchecked(data)
248
+ msg = (
249
+ f"Cannot convert object {data!r} with type {type(data)} to a scalar compatible with the "
250
+ f"data type {self}."
251
+ )
252
+ raise TypeError(msg)
253
+
254
+ def default_scalar(self) -> TFloatScalar_co:
255
+ """
256
+ Get the default value, which is 0 cast to this zdtype.
257
+
258
+ Returns
259
+ -------
260
+ TFloatScalar_co
261
+ The default value.
262
+ """
263
+ return self._cast_scalar_unchecked(0)
264
+
265
+ def from_json_scalar(self, data: JSON, *, zarr_format: ZarrFormat) -> TFloatScalar_co:
266
+ """
267
+ Read a JSON-serializable value as a NumPy float scalar.
268
+
269
+ Parameters
270
+ ----------
271
+ data : JSON
272
+ The JSON-serializable value.
273
+ zarr_format : ZarrFormat
274
+ The zarr format version.
275
+
276
+ Returns
277
+ -------
278
+ TFloatScalar_co
279
+ The NumPy float scalar.
280
+ """
281
+ if zarr_format == 2:
282
+ if check_json_float_v2(data):
283
+ return self._cast_scalar_unchecked(float_from_json_v2(data))
284
+ elif check_json_floatish_str(data):
285
+ return self._cast_scalar_unchecked(float(data))
286
+ else:
287
+ raise TypeError(
288
+ f"Invalid type: {data}. Expected a float or a special string encoding of a float."
289
+ )
290
+ elif zarr_format == 3:
291
+ if check_json_float_v3(data):
292
+ return self._cast_scalar_unchecked(float_from_json_v3(data))
293
+ elif check_json_floatish_str(data):
294
+ return self._cast_scalar_unchecked(float(data))
295
+ else:
296
+ raise TypeError(
297
+ f"Invalid type: {data}. Expected a float or a special string encoding of a float."
298
+ )
299
+ else:
300
+ raise ValueError(f"zarr_format must be 2 or 3, got {zarr_format}") # pragma: no cover
301
+
302
+ def to_json_scalar(self, data: object, *, zarr_format: ZarrFormat) -> float | str:
303
+ """
304
+ Convert an object to a JSON-serializable float.
305
+
306
+ Parameters
307
+ ----------
308
+ data : _BaseScalar
309
+ The value to convert.
310
+ zarr_format : ZarrFormat
311
+ The zarr format version.
312
+
313
+ Returns
314
+ -------
315
+ JSON
316
+ The JSON-serializable form of the float, which is potentially a number or a string.
317
+ See the zarr specifications for details on the JSON encoding for floats.
318
+ """
319
+ if zarr_format == 2:
320
+ return float_to_json_v2(self.cast_scalar(data))
321
+ elif zarr_format == 3:
322
+ return float_to_json_v3(self.cast_scalar(data))
323
+ else:
324
+ raise ValueError(f"zarr_format must be 2 or 3, got {zarr_format}") # pragma: no cover
325
+
326
+
327
+ @dataclass(frozen=True, kw_only=True)
328
+ class Float16(BaseFloat[np.dtypes.Float16DType, np.float16]):
329
+ """
330
+ A Zarr data type for arrays containing 16-bit floating point numbers.
331
+
332
+ Wraps the [`np.dtypes.Float16DType`][numpy.dtypes.Float16DType] data type. Scalars for this data type are instances
333
+ of [`np.float16`][numpy.float16].
334
+
335
+ Attributes
336
+ ----------
337
+ dtype_cls : Type[np.dtypes.Float16DType]
338
+ The NumPy dtype class for this data type.
339
+
340
+ References
341
+ ----------
342
+ This class implements the float16 data type defined in Zarr V2 and V3.
343
+
344
+ See the [Zarr V2](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v2/v2.0.rst#data-type-encoding) and [Zarr V3](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v3/data-types/index.rst) specification documents for details.
345
+ """
346
+
347
+ dtype_cls = np.dtypes.Float16DType
348
+ _zarr_v3_name = "float16"
349
+ _zarr_v2_names: ClassVar[tuple[Literal[">f2"], Literal["<f2"]]] = (">f2", "<f2")
350
+
351
+ @property
352
+ def item_size(self) -> int:
353
+ """
354
+ The size of a single scalar in bytes.
355
+
356
+ Returns
357
+ -------
358
+ int
359
+ The size of a single scalar in bytes.
360
+ """
361
+ return 2
362
+
363
+
364
+ @dataclass(frozen=True, kw_only=True)
365
+ class Float32(BaseFloat[np.dtypes.Float32DType, np.float32]):
366
+ """
367
+ A Zarr data type for arrays containing 32-bit floating point numbers.
368
+
369
+ Wraps the [`np.dtypes.Float32DType`][numpy.dtypes.Float32DType] data type. Scalars for this data type are instances
370
+ of [`np.float32`][numpy.float32].
371
+
372
+ Attributes
373
+ ----------
374
+ dtype_cls : Type[np.dtypes.Float32DType]
375
+ The NumPy dtype class for this data type.
376
+
377
+ References
378
+ ----------
379
+ This class implements the float32 data type defined in Zarr V2 and V3.
380
+
381
+ See the [Zarr V2](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v2/v2.0.rst#data-type-encoding) and [Zarr V3](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v3/data-types/index.rst) specification documents for details.
382
+ """
383
+
384
+ dtype_cls = np.dtypes.Float32DType
385
+ _zarr_v3_name = "float32"
386
+ _zarr_v2_names: ClassVar[tuple[Literal[">f4"], Literal["<f4"]]] = (">f4", "<f4")
387
+
388
+ @property
389
+ def item_size(self) -> int:
390
+ """
391
+ The size of a single scalar in bytes.
392
+
393
+ Returns
394
+ -------
395
+ int
396
+ The size of a single scalar in bytes.
397
+ """
398
+ return 4
399
+
400
+
401
+ @dataclass(frozen=True, kw_only=True)
402
+ class Float64(BaseFloat[np.dtypes.Float64DType, np.float64]):
403
+ """
404
+ A Zarr data type for arrays containing 64-bit floating point numbers.
405
+
406
+ Wraps the [`np.dtypes.Float64DType`][numpy.dtypes.Float64DType] data type. Scalars for this data type are instances
407
+ of [`np.float64`][numpy.float64].
408
+
409
+ Attributes
410
+ ----------
411
+ dtype_cls : Type[np.dtypes.Float64DType]
412
+ The NumPy dtype class for this data type.
413
+
414
+ References
415
+ ----------
416
+ This class implements the float64 data type defined in Zarr V2 and V3.
417
+
418
+ See the [Zarr V2](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v2/v2.0.rst#data-type-encoding) and [Zarr V3](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v3/data-types/index.rst) specification documents for details.
419
+ """
420
+
421
+ dtype_cls = np.dtypes.Float64DType
422
+ _zarr_v3_name = "float64"
423
+ _zarr_v2_names: ClassVar[tuple[Literal[">f8"], Literal["<f8"]]] = (">f8", "<f8")
424
+
425
+ @property
426
+ def item_size(self) -> int:
427
+ """
428
+ The size of a single scalar in bytes.
429
+
430
+ Returns
431
+ -------
432
+ int
433
+ The size of a single scalar in bytes.
434
+ """
435
+ return 8
.venv/lib/python3.11/site-packages/zarr/core/dtype/npy/int.py ADDED
@@ -0,0 +1,1521 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import (
5
+ TYPE_CHECKING,
6
+ ClassVar,
7
+ Literal,
8
+ Self,
9
+ SupportsIndex,
10
+ SupportsInt,
11
+ TypeGuard,
12
+ TypeVar,
13
+ overload,
14
+ )
15
+
16
+ import numpy as np
17
+
18
+ from zarr.core.dtype.common import (
19
+ DataTypeValidationError,
20
+ DTypeConfig_V2,
21
+ DTypeJSON,
22
+ HasEndianness,
23
+ HasItemSize,
24
+ check_dtype_spec_v2,
25
+ )
26
+ from zarr.core.dtype.npy.common import (
27
+ check_json_int,
28
+ check_json_intish_float,
29
+ check_json_intish_str,
30
+ endianness_to_numpy_str,
31
+ get_endianness_from_numpy_dtype,
32
+ )
33
+ from zarr.core.dtype.wrapper import TBaseDType, ZDType
34
+
35
+ if TYPE_CHECKING:
36
+ from zarr.core.common import JSON, ZarrFormat
37
+
38
+ _NumpyIntDType = (
39
+ np.dtypes.Int8DType
40
+ | np.dtypes.Int16DType
41
+ | np.dtypes.Int32DType
42
+ | np.dtypes.Int64DType
43
+ | np.dtypes.UInt8DType
44
+ | np.dtypes.UInt16DType
45
+ | np.dtypes.UInt32DType
46
+ | np.dtypes.UInt64DType
47
+ )
48
+ _NumpyIntScalar = (
49
+ np.int8 | np.int16 | np.int32 | np.int64 | np.uint8 | np.uint16 | np.uint32 | np.uint64
50
+ )
51
+ TIntDType_co = TypeVar("TIntDType_co", bound=_NumpyIntDType, covariant=True)
52
+ TIntScalar_co = TypeVar("TIntScalar_co", bound=_NumpyIntScalar, covariant=True)
53
+ IntLike = SupportsInt | SupportsIndex | bytes | str
54
+
55
+
56
+ @dataclass(frozen=True)
57
+ class BaseInt(ZDType[TIntDType_co, TIntScalar_co], HasItemSize):
58
+ """
59
+ A base class for integer data types in Zarr.
60
+
61
+ This class provides methods for serialization and deserialization of integer types
62
+ in both Zarr v2 and v3 formats, as well as methods for checking and casting scalars.
63
+ """
64
+
65
+ _zarr_v2_names: ClassVar[tuple[str, ...]]
66
+
67
+ @classmethod
68
+ def _check_json_v2(cls, data: object) -> TypeGuard[DTypeConfig_V2[str, None]]:
69
+ """
70
+ Check that the input is a valid JSON representation of this integer data type in Zarr V2.
71
+
72
+ This method verifies that the provided data matches the expected Zarr V2 representation
73
+ for this data type. The input data must be a mapping that contains a "name" key that is
74
+ one of the strings from cls._zarr_v2_names and an "object_codec_id" key that is None.
75
+
76
+ Parameters
77
+ ----------
78
+ data : object
79
+ The JSON data to check.
80
+
81
+ Returns
82
+ -------
83
+ TypeGuard[DTypeConfig_V2[str, None]]
84
+ True if the input is a valid representation of this class in Zarr V2,
85
+ False otherwise.
86
+ """
87
+
88
+ return (
89
+ check_dtype_spec_v2(data)
90
+ and data["name"] in cls._zarr_v2_names
91
+ and data["object_codec_id"] is None
92
+ )
93
+
94
+ @classmethod
95
+ def _check_json_v3(cls, data: object) -> TypeGuard[str]:
96
+ """
97
+ Check that the input is a valid JSON representation of this class in Zarr V3.
98
+
99
+ Parameters
100
+ ----------
101
+ data : object
102
+ The JSON data to check.
103
+
104
+ Returns
105
+ -------
106
+ TypeGuard[str]
107
+ True if the input is a valid representation of this class in Zarr v3,
108
+ False otherwise.
109
+ """
110
+ return data == cls._zarr_v3_name
111
+
112
+ def _check_scalar(self, data: object) -> TypeGuard[IntLike]:
113
+ """
114
+ Check if the input object is of an IntLike type.
115
+
116
+ This method verifies whether the provided data can be considered as an integer-like
117
+ value, which includes objects supporting integer conversion.
118
+
119
+ Parameters
120
+ ----------
121
+ data : object
122
+ The data to check.
123
+
124
+ Returns
125
+ -------
126
+ TypeGuard[IntLike]
127
+ True if the data is IntLike, False otherwise.
128
+ """
129
+
130
+ return isinstance(data, IntLike)
131
+
132
+ def _cast_scalar_unchecked(self, data: IntLike) -> TIntScalar_co:
133
+ """
134
+ Casts a given scalar value to the native integer scalar type without type checking.
135
+
136
+ Parameters
137
+ ----------
138
+ data : IntLike
139
+ The scalar value to cast.
140
+
141
+ Returns
142
+ -------
143
+ TIntScalar_co
144
+ The casted integer scalar of the native dtype.
145
+ """
146
+
147
+ return self.to_native_dtype().type(data) # type: ignore[return-value]
148
+
149
+ def cast_scalar(self, data: object) -> TIntScalar_co:
150
+ """
151
+ Attempt to cast a given object to a NumPy integer scalar.
152
+
153
+ Parameters
154
+ ----------
155
+ data : object
156
+ The data to be cast to a NumPy integer scalar.
157
+
158
+ Returns
159
+ -------
160
+ TIntScalar_co
161
+ The data cast as a NumPy integer scalar.
162
+
163
+ Raises
164
+ ------
165
+ TypeError
166
+ If the data cannot be converted to a NumPy integer scalar.
167
+ """
168
+
169
+ if self._check_scalar(data):
170
+ return self._cast_scalar_unchecked(data)
171
+ msg = (
172
+ f"Cannot convert object {data!r} with type {type(data)} to a scalar compatible with the "
173
+ f"data type {self}."
174
+ )
175
+ raise TypeError(msg)
176
+
177
+ def default_scalar(self) -> TIntScalar_co:
178
+ """
179
+ Get the default value, which is 0 cast to this dtype.
180
+
181
+ Returns
182
+ -------
183
+ TIntScalar_co
184
+ The default value.
185
+ """
186
+ return self._cast_scalar_unchecked(0)
187
+
188
+ def from_json_scalar(self, data: JSON, *, zarr_format: ZarrFormat) -> TIntScalar_co:
189
+ """
190
+ Read a JSON-serializable value as a NumPy int scalar.
191
+
192
+ Parameters
193
+ ----------
194
+ data : JSON
195
+ The JSON-serializable value.
196
+ zarr_format : ZarrFormat
197
+ The Zarr format version.
198
+
199
+ Returns
200
+ -------
201
+ TIntScalar_co
202
+ The NumPy int scalar.
203
+
204
+ Raises
205
+ ------
206
+ TypeError
207
+ If the input is not a valid integer type.
208
+ """
209
+ if check_json_int(data):
210
+ return self._cast_scalar_unchecked(data)
211
+ if check_json_intish_float(data):
212
+ return self._cast_scalar_unchecked(int(data))
213
+
214
+ if check_json_intish_str(data):
215
+ return self._cast_scalar_unchecked(int(data))
216
+
217
+ raise TypeError(f"Invalid type: {data}. Expected an integer.")
218
+
219
+ def to_json_scalar(self, data: object, *, zarr_format: ZarrFormat) -> int:
220
+ """
221
+ Convert an object to a JSON serializable scalar. For the integer data types,
222
+ the JSON form is a plain integer.
223
+
224
+ Parameters
225
+ ----------
226
+ data : object
227
+ The value to convert.
228
+ zarr_format : ZarrFormat
229
+ The Zarr format version.
230
+
231
+ Returns
232
+ -------
233
+ int
234
+ The JSON-serializable form of the scalar.
235
+ """
236
+ return int(self.cast_scalar(data))
237
+
238
+
239
+ @dataclass(frozen=True, kw_only=True)
240
+ class Int8(BaseInt[np.dtypes.Int8DType, np.int8]):
241
+ """
242
+ A Zarr data type for arrays containing 8-bit signed integers.
243
+
244
+ Wraps the [`np.dtypes.Int8DType`][numpy.dtypes.Int8DType] data type. Scalars for this data type are
245
+ instances of [`np.int8`][numpy.int8].
246
+
247
+ Attributes
248
+ ----------
249
+ dtype_cls : np.dtypes.Int8DType
250
+ The class of the underlying NumPy dtype.
251
+
252
+ References
253
+ ----------
254
+ This class implements the 8-bit signed integer data type defined in Zarr V2 and V3.
255
+
256
+ See the [Zarr V2](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v2/v2.0.rst#data-type-encoding) and [Zarr V3](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v3/data-types/index.rst) specification documents for details.
257
+ """
258
+
259
+ dtype_cls = np.dtypes.Int8DType
260
+ _zarr_v3_name: ClassVar[Literal["int8"]] = "int8"
261
+ _zarr_v2_names: ClassVar[tuple[Literal["|i1"]]] = ("|i1",)
262
+
263
+ @classmethod
264
+ def from_native_dtype(cls, dtype: TBaseDType) -> Self:
265
+ """
266
+ Create an Int8 from an np.dtype('int8') instance.
267
+
268
+ Parameters
269
+ ----------
270
+ dtype : TBaseDType
271
+ The np.dtype('int8') instance.
272
+
273
+ Returns
274
+ -------
275
+ Self
276
+ An instance of this data type.
277
+
278
+ Raises
279
+ ------
280
+ DataTypeValidationError
281
+ If the input data type is not a valid representation of this class Int8.
282
+ """
283
+ if cls._check_native_dtype(dtype):
284
+ return cls()
285
+ raise DataTypeValidationError(
286
+ f"Invalid data type: {dtype}. Expected an instance of {cls.dtype_cls}"
287
+ )
288
+
289
+ def to_native_dtype(self: Self) -> np.dtypes.Int8DType:
290
+ """
291
+ Convert the Int8 instance to an np.dtype('int8') instance.
292
+
293
+ Returns
294
+ -------
295
+ np.dtypes.Int8DType
296
+ The np.dtype('int8') instance.
297
+ """
298
+ return self.dtype_cls()
299
+
300
+ @classmethod
301
+ def _from_json_v2(cls, data: DTypeJSON) -> Self:
302
+ """
303
+ Create an Int8 from Zarr V2-flavored JSON.
304
+
305
+ Parameters
306
+ ----------
307
+ data : DTypeJSON
308
+ The JSON data.
309
+
310
+ Returns
311
+ -------
312
+ Self
313
+ An instance of this data type.
314
+
315
+ Raises
316
+ ------
317
+ DataTypeValidationError
318
+ If the input JSON is not a valid representation of this class Int8.
319
+ """
320
+ if cls._check_json_v2(data):
321
+ return cls()
322
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected the string {cls._zarr_v2_names[0]!r}"
323
+ raise DataTypeValidationError(msg)
324
+
325
+ @classmethod
326
+ def _from_json_v3(cls, data: DTypeJSON) -> Self:
327
+ """
328
+ Create an Int8 from Zarr V3-flavored JSON.
329
+
330
+ Parameters
331
+ ----------
332
+ data : DTypeJSON
333
+ The JSON data.
334
+
335
+ Returns
336
+ -------
337
+ Self
338
+ An instance of this data type.
339
+
340
+ Raises
341
+ ------
342
+ DataTypeValidationError
343
+ If the input JSON is not a valid representation of this class Int8.
344
+ """
345
+ if cls._check_json_v3(data):
346
+ return cls()
347
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected the string {cls._zarr_v3_name!r}"
348
+ raise DataTypeValidationError(msg)
349
+
350
+ @overload
351
+ def to_json(self, zarr_format: Literal[2]) -> DTypeConfig_V2[Literal["|i1"], None]: ...
352
+
353
+ @overload
354
+ def to_json(self, zarr_format: Literal[3]) -> Literal["int8"]: ...
355
+
356
+ def to_json(
357
+ self, zarr_format: ZarrFormat
358
+ ) -> DTypeConfig_V2[Literal["|i1"], None] | Literal["int8"]:
359
+ """
360
+ Convert the data type to a JSON-serializable form.
361
+
362
+ Parameters
363
+ ----------
364
+ zarr_format : ZarrFormat
365
+ The Zarr format version.
366
+
367
+ Returns
368
+ -------
369
+ ``DTypeConfig_V2[Literal["|i1"], None] | Literal["int8"]``
370
+ The JSON-serializable representation of the data type.
371
+
372
+ Raises
373
+ ------
374
+ ValueError
375
+ If the zarr_format is not 2 or 3.
376
+ """
377
+ if zarr_format == 2:
378
+ return {"name": self._zarr_v2_names[0], "object_codec_id": None}
379
+ elif zarr_format == 3:
380
+ return self._zarr_v3_name
381
+ raise ValueError(f"zarr_format must be 2 or 3, got {zarr_format}") # pragma: no cover
382
+
383
+ @property
384
+ def item_size(self) -> int:
385
+ """
386
+ The size of a single scalar in bytes.
387
+
388
+ Returns
389
+ -------
390
+ int
391
+ The size of a single scalar in bytes.
392
+ """
393
+ return 1
394
+
395
+
396
+ @dataclass(frozen=True, kw_only=True)
397
+ class UInt8(BaseInt[np.dtypes.UInt8DType, np.uint8]):
398
+ """
399
+ A Zarr data type for arrays containing 8-bit unsigned integers.
400
+
401
+ Wraps the [`np.dtypes.UInt8DType`][numpy.dtypes.UInt8DType] data type. Scalars for this data type are instances of [`np.uint8`][numpy.uint8].
402
+
403
+ Attributes
404
+ ----------
405
+ dtype_cls : np.dtypes.UInt8DType
406
+ The class of the underlying NumPy dtype.
407
+
408
+ References
409
+ ----------
410
+ This class implements the 8-bit unsigned integer data type defined in Zarr V2 and V3.
411
+
412
+ See the [Zarr V2](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v2/v2.0.rst#data-type-encoding) and [Zarr V3](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v3/data-types/index.rst) specification documents for details.
413
+ """
414
+
415
+ dtype_cls = np.dtypes.UInt8DType
416
+ _zarr_v3_name: ClassVar[Literal["uint8"]] = "uint8"
417
+ _zarr_v2_names: ClassVar[tuple[Literal["|u1"]]] = ("|u1",)
418
+
419
+ @classmethod
420
+ def from_native_dtype(cls, dtype: TBaseDType) -> Self:
421
+ """
422
+ Create a UInt8 from an np.dtype('uint8') instance.
423
+ """
424
+ if cls._check_native_dtype(dtype):
425
+ return cls()
426
+ raise DataTypeValidationError(
427
+ f"Invalid data type: {dtype}. Expected an instance of {cls.dtype_cls}"
428
+ )
429
+
430
+ def to_native_dtype(self: Self) -> np.dtypes.UInt8DType:
431
+ """
432
+ Create a NumPy unsigned 8-bit integer dtype instance from this UInt8 ZDType.
433
+
434
+ Returns
435
+ -------
436
+ np.dtypes.UInt8DType
437
+ The NumPy unsigned 8-bit integer dtype.
438
+ """
439
+
440
+ return self.dtype_cls()
441
+
442
+ @classmethod
443
+ def _from_json_v2(cls, data: DTypeJSON) -> Self:
444
+ """
445
+ Create an instance of this data type from Zarr V2-flavored JSON.
446
+
447
+ Parameters
448
+ ----------
449
+ data : DTypeJSON
450
+ The JSON data.
451
+
452
+ Returns
453
+ -------
454
+ Self
455
+ An instance of this data type.
456
+
457
+ Raises
458
+ ------
459
+ DataTypeValidationError
460
+ If the input JSON is not a valid representation of this class.
461
+ """
462
+
463
+ if cls._check_json_v2(data):
464
+ return cls()
465
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected the string {cls._zarr_v2_names[0]!r}"
466
+ raise DataTypeValidationError(msg)
467
+
468
+ @classmethod
469
+ def _from_json_v3(cls, data: DTypeJSON) -> Self:
470
+ """
471
+ Create an instance of this data type from Zarr V3-flavored JSON.
472
+
473
+ Parameters
474
+ ----------
475
+ data : DTypeJSON
476
+ The JSON data.
477
+
478
+ Returns
479
+ -------
480
+ Self
481
+ An instance of this data type.
482
+
483
+ Raises
484
+ ------
485
+ DataTypeValidationError
486
+ If the input JSON is not a valid representation of this class.
487
+ """
488
+ if cls._check_json_v3(data):
489
+ return cls()
490
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected the string {cls._zarr_v3_name!r}"
491
+ raise DataTypeValidationError(msg)
492
+
493
+ @overload
494
+ def to_json(self, zarr_format: Literal[2]) -> DTypeConfig_V2[Literal["|u1"], None]: ...
495
+
496
+ @overload
497
+ def to_json(self, zarr_format: Literal[3]) -> Literal["uint8"]: ...
498
+
499
+ def to_json(
500
+ self, zarr_format: ZarrFormat
501
+ ) -> DTypeConfig_V2[Literal["|u1"], None] | Literal["uint8"]:
502
+ """
503
+ Convert the data type to a JSON-serializable form.
504
+
505
+ Parameters
506
+ ----------
507
+ zarr_format : ZarrFormat
508
+ The Zarr format version. Supported values are 2 and 3.
509
+
510
+ Returns
511
+ -------
512
+ ``DTypeConfig_V2[Literal["|u1"], None] | Literal["uint8"]``
513
+ The JSON-serializable representation of the data type.
514
+
515
+ Raises
516
+ ------
517
+ ValueError
518
+ If `zarr_format` is not 2 or 3.
519
+ """
520
+ if zarr_format == 2:
521
+ # For Zarr format version 2, return a dictionary with the name and object codec ID.
522
+ return {"name": self._zarr_v2_names[0], "object_codec_id": None}
523
+ elif zarr_format == 3:
524
+ # For Zarr format version 3, return the v3 name as a string.
525
+ return self._zarr_v3_name
526
+ # Raise an error if the zarr_format is neither 2 nor 3.
527
+ raise ValueError(f"zarr_format must be 2 or 3, got {zarr_format}") # pragma: no cover
528
+
529
+ @property
530
+ def item_size(self) -> int:
531
+ """
532
+ The size of a single scalar in bytes.
533
+
534
+ Returns
535
+ -------
536
+ int
537
+ The size of a single scalar in bytes.
538
+ """
539
+ return 1
540
+
541
+
542
+ @dataclass(frozen=True, kw_only=True)
543
+ class Int16(BaseInt[np.dtypes.Int16DType, np.int16], HasEndianness):
544
+ """
545
+ A Zarr data type for arrays containing 16-bit signed integers.
546
+
547
+ Wraps the [`np.dtypes.Int16DType`][numpy.dtypes.Int16DType] data type. Scalars for this data type are instances of
548
+ [`np.int16`][numpy.int16].
549
+
550
+ Attributes
551
+ ----------
552
+ dtype_cls : np.dtypes.Int16DType
553
+ The class of the underlying NumPy dtype.
554
+
555
+ References
556
+ ----------
557
+ This class implements the 16-bit signed integer data type defined in Zarr V2 and V3.
558
+
559
+ See the [Zarr V2](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v2/v2.0.rst#data-type-encoding) and [Zarr V3](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v3/data-types/index.rst) specification documents for details.
560
+ """
561
+
562
+ dtype_cls = np.dtypes.Int16DType
563
+ _zarr_v3_name: ClassVar[Literal["int16"]] = "int16"
564
+ _zarr_v2_names: ClassVar[tuple[Literal[">i2"], Literal["<i2"]]] = (">i2", "<i2")
565
+
566
+ @classmethod
567
+ def from_native_dtype(cls, dtype: TBaseDType) -> Self:
568
+ """
569
+ Create an instance of this data type from an np.dtype('int16') instance.
570
+
571
+ Parameters
572
+ ----------
573
+ dtype : np.dtype
574
+ The instance of np.dtype('int16') to create from.
575
+
576
+ Returns
577
+ -------
578
+ Self
579
+ An instance of this data type.
580
+
581
+ Raises
582
+ ------
583
+ DataTypeValidationError
584
+ If the input data type is not an instance of np.dtype('int16').
585
+ """
586
+ if cls._check_native_dtype(dtype):
587
+ return cls(endianness=get_endianness_from_numpy_dtype(dtype))
588
+ raise DataTypeValidationError(
589
+ f"Invalid data type: {dtype}. Expected an instance of {cls.dtype_cls}"
590
+ )
591
+
592
+ def to_native_dtype(self) -> np.dtypes.Int16DType:
593
+ """
594
+ Convert the data type to an np.dtype('int16') instance.
595
+
596
+ Returns
597
+ -------
598
+ np.dtype
599
+ The np.dtype('int16') instance.
600
+ """
601
+ byte_order = endianness_to_numpy_str(self.endianness)
602
+ return self.dtype_cls().newbyteorder(byte_order)
603
+
604
+ @classmethod
605
+ def _from_json_v2(cls, data: DTypeJSON) -> Self:
606
+ """
607
+ Create an instance of this data type from Zarr V2-flavored JSON.
608
+
609
+ Parameters
610
+ ----------
611
+ data : DTypeJSON
612
+ The JSON data.
613
+
614
+ Returns
615
+ -------
616
+ Self
617
+ An instance of this data type.
618
+
619
+ Raises
620
+ ------
621
+ DataTypeValidationError
622
+ If the input JSON is not a valid representation of this class.
623
+ """
624
+ if cls._check_json_v2(data):
625
+ # Going via NumPy ensures that we get the endianness correct without
626
+ # annoying string parsing.
627
+ name = data["name"]
628
+ return cls.from_native_dtype(np.dtype(name))
629
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected one of the strings {cls._zarr_v2_names!r}."
630
+ raise DataTypeValidationError(msg)
631
+
632
+ @classmethod
633
+ def _from_json_v3(cls, data: DTypeJSON) -> Self:
634
+ """
635
+ Create an instance of this data type from Zarr V3-flavored JSON.
636
+
637
+ Parameters
638
+ ----------
639
+ data : DTypeJSON
640
+ The JSON data.
641
+
642
+ Returns
643
+ -------
644
+ Self
645
+ An instance of this data type.
646
+
647
+ Raises
648
+ ------
649
+ DataTypeValidationError
650
+ If the input JSON is not a valid representation of this class.
651
+ """
652
+ if cls._check_json_v3(data):
653
+ return cls()
654
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected the string {cls._zarr_v3_name!r}"
655
+ raise DataTypeValidationError(msg)
656
+
657
+ @overload
658
+ def to_json(self, zarr_format: Literal[2]) -> DTypeConfig_V2[Literal[">i2", "<i2"], None]: ...
659
+
660
+ @overload
661
+ def to_json(self, zarr_format: Literal[3]) -> Literal["int16"]: ...
662
+
663
+ def to_json(
664
+ self, zarr_format: ZarrFormat
665
+ ) -> DTypeConfig_V2[Literal[">i2", "<i2"], None] | Literal["int16"]:
666
+ """
667
+ Serialize this ZDType to v2- or v3-flavored JSON
668
+
669
+ Parameters
670
+ ----------
671
+ zarr_format : ZarrFormat
672
+ The Zarr format version (2 or 3).
673
+
674
+ Returns
675
+ -------
676
+ DTypeConfig_V2[Literal[">i2", "<i2"], None] or Literal["int16"]
677
+ The JSON representation of the Int16 instance.
678
+
679
+ Raises
680
+ ------
681
+ ValueError
682
+ If the zarr_format is not 2 or 3.
683
+ """
684
+ if zarr_format == 2:
685
+ name = self.to_native_dtype().str
686
+ return {"name": name, "object_codec_id": None}
687
+ elif zarr_format == 3:
688
+ return self._zarr_v3_name
689
+ raise ValueError(f"zarr_format must be 2 or 3, got {zarr_format}") # pragma: no cover
690
+
691
+ @property
692
+ def item_size(self) -> int:
693
+ """
694
+ The size of a single scalar in bytes.
695
+
696
+ Returns
697
+ -------
698
+ int
699
+ The size of a single scalar in bytes.
700
+ """
701
+ return 2
702
+
703
+
704
+ @dataclass(frozen=True, kw_only=True)
705
+ class UInt16(BaseInt[np.dtypes.UInt16DType, np.uint16], HasEndianness):
706
+ """
707
+ A Zarr data type for arrays containing 16-bit unsigned integers.
708
+
709
+ Wraps the [`np.dtypes.UInt16DType`][numpy.dtypes.UInt16DType] data type. Scalars for this data type are instances of
710
+ [`np.uint16`][numpy.uint16].
711
+
712
+ Attributes
713
+ ----------
714
+ dtype_cls : np.dtypes.UInt16DType
715
+ The class of the underlying NumPy dtype.
716
+
717
+ References
718
+ ----------
719
+ This class implements the unsigned 16-bit unsigned integer data type defined in Zarr V2 and V3.
720
+
721
+ See the [Zarr V2](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v2/v2.0.rst#data-type-encoding) and [Zarr V3](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v3/data-types/index.rst) specification documents for details.
722
+ """
723
+
724
+ dtype_cls = np.dtypes.UInt16DType
725
+ _zarr_v3_name: ClassVar[Literal["uint16"]] = "uint16"
726
+ _zarr_v2_names: ClassVar[tuple[Literal[">u2"], Literal["<u2"]]] = (">u2", "<u2")
727
+
728
+ @classmethod
729
+ def from_native_dtype(cls, dtype: TBaseDType) -> Self:
730
+ """
731
+ Create an instance of this data type from an np.dtype('uint16') instance.
732
+
733
+ Parameters
734
+ ----------
735
+ dtype : np.dtype
736
+ The NumPy data type.
737
+
738
+ Returns
739
+ -------
740
+ Self
741
+ An instance of this data type.
742
+
743
+ Raises
744
+ ------
745
+ DataTypeValidationError
746
+ If the input data type is not an instance of np.dtype('uint16').
747
+ """
748
+ if cls._check_native_dtype(dtype):
749
+ return cls(endianness=get_endianness_from_numpy_dtype(dtype))
750
+ raise DataTypeValidationError(
751
+ f"Invalid data type: {dtype}. Expected an instance of {cls.dtype_cls}"
752
+ )
753
+
754
+ def to_native_dtype(self) -> np.dtypes.UInt16DType:
755
+ """
756
+ Convert the data type to an np.dtype('uint16') instance.
757
+
758
+ Returns
759
+ -------
760
+ np.dtype
761
+ The np.dtype('uint16') instance.
762
+ """
763
+ byte_order = endianness_to_numpy_str(self.endianness)
764
+ return self.dtype_cls().newbyteorder(byte_order)
765
+
766
+ @classmethod
767
+ def _from_json_v2(cls, data: DTypeJSON) -> Self:
768
+ """
769
+ Create an instance of this data type from Zarr V2-flavored JSON.
770
+
771
+ Parameters
772
+ ----------
773
+ data : DTypeJSON
774
+ The JSON data.
775
+
776
+ Returns
777
+ -------
778
+ Self
779
+ An instance of this data type.
780
+
781
+ Raises
782
+ ------
783
+ DataTypeValidationError
784
+ If the input JSON is not a valid representation of this class.
785
+ """
786
+ if cls._check_json_v2(data):
787
+ # Going via NumPy ensures that we get the endianness correct without
788
+ # annoying string parsing.
789
+ name = data["name"]
790
+ return cls.from_native_dtype(np.dtype(name))
791
+ msg = f"Invalid JSON representation of UInt16. Got {data!r}, expected one of the strings {cls._zarr_v2_names}."
792
+ raise DataTypeValidationError(msg)
793
+
794
+ @classmethod
795
+ def _from_json_v3(cls, data: DTypeJSON) -> Self:
796
+ """
797
+ Create an instance of this data type from Zarr V3-flavored JSON.
798
+
799
+ Parameters
800
+ ----------
801
+ data : DTypeJSON
802
+ The JSON data.
803
+
804
+ Returns
805
+ -------
806
+ Self
807
+ An instance of this data type.
808
+
809
+ Raises
810
+ ------
811
+ DataTypeValidationError
812
+ If the input JSON is not a valid representation of this class.
813
+ """
814
+ if cls._check_json_v3(data):
815
+ return cls()
816
+ msg = f"Invalid JSON representation of UInt16. Got {data!r}, expected the string {cls._zarr_v3_name!r}"
817
+ raise DataTypeValidationError(msg)
818
+
819
+ @overload
820
+ def to_json(self, zarr_format: Literal[2]) -> DTypeConfig_V2[Literal[">u2", "<u2"], None]: ...
821
+
822
+ @overload
823
+ def to_json(self, zarr_format: Literal[3]) -> Literal["uint16"]: ...
824
+
825
+ def to_json(
826
+ self, zarr_format: ZarrFormat
827
+ ) -> DTypeConfig_V2[Literal[">u2", "<u2"], None] | Literal["uint16"]:
828
+ """
829
+ Serialize this ZDType to v2- or v3-flavored JSON
830
+
831
+ Parameters
832
+ ----------
833
+ zarr_format : ZarrFormat
834
+ The Zarr format version (2 or 3).
835
+
836
+ Returns
837
+ -------
838
+ DTypeConfig_V2[Literal[">u2", "<u2"], None] or Literal["uint16"]
839
+ The JSON representation of the UInt16 instance.
840
+
841
+ Raises
842
+ ------
843
+ ValueError
844
+ If the zarr_format is not 2 or 3.
845
+ """
846
+ if zarr_format == 2:
847
+ name = self.to_native_dtype().str
848
+ return {"name": name, "object_codec_id": None}
849
+ elif zarr_format == 3:
850
+ return self._zarr_v3_name
851
+ raise ValueError(f"zarr_format must be 2 or 3, got {zarr_format}") # pragma: no cover
852
+
853
+ @property
854
+ def item_size(self) -> int:
855
+ """
856
+ The size of a single scalar in bytes.
857
+
858
+ Returns
859
+ -------
860
+ int
861
+ The size of a single scalar in bytes.
862
+ """
863
+ return 2
864
+
865
+
866
+ @dataclass(frozen=True, kw_only=True)
867
+ class Int32(BaseInt[np.dtypes.Int32DType, np.int32], HasEndianness):
868
+ """
869
+ A Zarr data type for arrays containing 32-bit signed integers.
870
+
871
+ Wraps the [`np.dtypes.Int32DType`][numpy.dtypes.Int32DType] data type. Scalars for this data type are instances of
872
+ [`np.int32`][numpy.int32].
873
+
874
+ Attributes
875
+ ----------
876
+ dtype_cls : np.dtypes.Int32DType
877
+ The class of the underlying NumPy dtype.
878
+
879
+ References
880
+ ----------
881
+ This class implements the 32-bit signed integer data type defined in Zarr V2 and V3.
882
+
883
+ See the [Zarr V2](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v2/v2.0.rst#data-type-encoding) and [Zarr V3](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v3/data-types/index.rst) specification documents for details.
884
+ """
885
+
886
+ dtype_cls = np.dtypes.Int32DType
887
+ _zarr_v3_name: ClassVar[Literal["int32"]] = "int32"
888
+ _zarr_v2_names: ClassVar[tuple[Literal[">i4"], Literal["<i4"]]] = (">i4", "<i4")
889
+
890
+ @classmethod
891
+ def _check_native_dtype(cls: type[Self], dtype: TBaseDType) -> TypeGuard[np.dtypes.Int32DType]:
892
+ """
893
+ A type guard that checks if the input is assignable to the type of ``cls.dtype_class``
894
+
895
+ This method is overridden for this particular data type because of a Windows-specific issue
896
+ where np.dtype('i') creates an instance of ``np.dtypes.IntDType``, rather than an
897
+ instance of ``np.dtypes.Int32DType``, even though both represent 32-bit signed integers.
898
+
899
+ Parameters
900
+ ----------
901
+ dtype : TDType
902
+ The dtype to check.
903
+
904
+ Returns
905
+ -------
906
+ Bool
907
+ True if the dtype matches, False otherwise.
908
+ """
909
+ return super()._check_native_dtype(dtype) or dtype == np.dtypes.Int32DType()
910
+
911
+ @classmethod
912
+ def from_native_dtype(cls: type[Self], dtype: TBaseDType) -> Self:
913
+ """
914
+ Create an Int32 from an np.dtype('int32') instance.
915
+
916
+ Parameters
917
+ ----------
918
+ dtype : TBaseDType
919
+ The np.dtype('int32') instance.
920
+
921
+ Returns
922
+ -------
923
+ Self
924
+ An instance of this data type.
925
+
926
+ Raises
927
+ ------
928
+ DataTypeValidationError
929
+ If the input JSON is not a valid representation of this class Int32.
930
+ """
931
+ if cls._check_native_dtype(dtype):
932
+ return cls(endianness=get_endianness_from_numpy_dtype(dtype))
933
+ raise DataTypeValidationError(
934
+ f"Invalid data type: {dtype}. Expected an instance of {cls.dtype_cls}"
935
+ )
936
+
937
+ def to_native_dtype(self: Self) -> np.dtypes.Int32DType:
938
+ """
939
+ Convert the Int32 instance to an np.dtype('int32') instance.
940
+
941
+ Returns
942
+ -------
943
+ np.dtypes.Int32DType
944
+ The np.dtype('int32') instance.
945
+ """
946
+ byte_order = endianness_to_numpy_str(self.endianness)
947
+ return self.dtype_cls().newbyteorder(byte_order)
948
+
949
+ @classmethod
950
+ def _from_json_v2(cls, data: DTypeJSON) -> Self:
951
+ """
952
+ Create an Int32 from Zarr V2-flavored JSON.
953
+
954
+ Parameters
955
+ ----------
956
+ data : DTypeJSON
957
+ The JSON data.
958
+
959
+ Returns
960
+ -------
961
+ Self
962
+ An instance of this data type.
963
+
964
+ Raises
965
+ ------
966
+ DataTypeValidationError
967
+ If the input JSON is not a valid representation of this class Int32.
968
+ """
969
+ if cls._check_json_v2(data):
970
+ # Going via NumPy ensures that we get the endianness correct without
971
+ # annoying string parsing.
972
+ name = data["name"]
973
+ return cls.from_native_dtype(np.dtype(name))
974
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected one of the strings {cls._zarr_v2_names!r}."
975
+ raise DataTypeValidationError(msg)
976
+
977
+ @classmethod
978
+ def _from_json_v3(cls, data: DTypeJSON) -> Self:
979
+ """
980
+ Create an Int32 from Zarr V3-flavored JSON.
981
+
982
+ Parameters
983
+ ----------
984
+ data : DTypeJSON
985
+ The JSON data.
986
+
987
+ Returns
988
+ -------
989
+ Self
990
+ An instance of this data type.
991
+
992
+ Raises
993
+ ------
994
+ DataTypeValidationError
995
+ If the input JSON is not a valid representation of this class Int32.
996
+ """
997
+ if cls._check_json_v3(data):
998
+ return cls()
999
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected the string {cls._zarr_v3_name!r}"
1000
+ raise DataTypeValidationError(msg)
1001
+
1002
+ @overload
1003
+ def to_json(self, zarr_format: Literal[2]) -> DTypeConfig_V2[Literal[">i4", "<i4"], None]: ...
1004
+
1005
+ @overload
1006
+ def to_json(self, zarr_format: Literal[3]) -> Literal["int32"]: ...
1007
+
1008
+ def to_json(
1009
+ self, zarr_format: ZarrFormat
1010
+ ) -> DTypeConfig_V2[Literal[">i4", "<i4"], None] | Literal["int32"]:
1011
+ """
1012
+ Serialize this ZDType to v2- or v3-flavored JSON
1013
+
1014
+ Parameters
1015
+ ----------
1016
+ zarr_format : ZarrFormat
1017
+ The Zarr format version (2 or 3).
1018
+
1019
+ Returns
1020
+ -------
1021
+ DTypeConfig_V2[Literal[">i4", "<i4"], None] or Literal["int32"]
1022
+ The JSON representation of the Int32 instance.
1023
+
1024
+ Raises
1025
+ ------
1026
+ ValueError
1027
+ If the zarr_format is not 2 or 3.
1028
+ """
1029
+ if zarr_format == 2:
1030
+ name = self.to_native_dtype().str
1031
+ return {"name": name, "object_codec_id": None}
1032
+ elif zarr_format == 3:
1033
+ return self._zarr_v3_name
1034
+ raise ValueError(f"zarr_format must be 2 or 3, got {zarr_format}") # pragma: no cover
1035
+
1036
+ @property
1037
+ def item_size(self) -> int:
1038
+ """
1039
+ The size of a single scalar in bytes.
1040
+
1041
+ Returns
1042
+ -------
1043
+ int
1044
+ The size of a single scalar in bytes.
1045
+ """
1046
+ return 4
1047
+
1048
+
1049
+ @dataclass(frozen=True, kw_only=True)
1050
+ class UInt32(BaseInt[np.dtypes.UInt32DType, np.uint32], HasEndianness):
1051
+ """
1052
+ A Zarr data type for arrays containing 32-bit unsigned integers.
1053
+
1054
+ Wraps the [`np.dtypes.UInt32DType`][numpy.dtypes.UInt32DType] data type. Scalars for this data type are instances of
1055
+ [`np.uint32`][numpy.uint32].
1056
+
1057
+ Attributes
1058
+ ----------
1059
+ dtype_cls : np.dtypes.UInt32DType
1060
+ The class of the underlying NumPy dtype.
1061
+
1062
+ References
1063
+ ----------
1064
+ This class implements the 32-bit unsigned integer data type defined in Zarr V2 and V3.
1065
+
1066
+ See the [Zarr V2](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v2/v2.0.rst#data-type-encoding) and [Zarr V3](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v3/data-types/index.rst) specification documents for details.
1067
+ """
1068
+
1069
+ dtype_cls = np.dtypes.UInt32DType
1070
+ _zarr_v3_name: ClassVar[Literal["uint32"]] = "uint32"
1071
+ _zarr_v2_names: ClassVar[tuple[Literal[">u4"], Literal["<u4"]]] = (">u4", "<u4")
1072
+
1073
+ @classmethod
1074
+ def from_native_dtype(cls, dtype: TBaseDType) -> Self:
1075
+ """
1076
+ Create a UInt32 from an np.dtype('uint32') instance.
1077
+
1078
+ Parameters
1079
+ ----------
1080
+ dtype : TBaseDType
1081
+ The NumPy data type.
1082
+
1083
+ Returns
1084
+ -------
1085
+ Self
1086
+ An instance of this data type.
1087
+
1088
+ Raises
1089
+ ------
1090
+ DataTypeValidationError
1091
+ If the input data type is not a valid representation of this class 32-bit unsigned
1092
+ integer.
1093
+ """
1094
+ if cls._check_native_dtype(dtype):
1095
+ return cls(endianness=get_endianness_from_numpy_dtype(dtype))
1096
+ raise DataTypeValidationError(
1097
+ f"Invalid data type: {dtype}. Expected an instance of {cls.dtype_cls}"
1098
+ )
1099
+
1100
+ def to_native_dtype(self) -> np.dtypes.UInt32DType:
1101
+ """
1102
+ Create a NumPy unsigned 32-bit integer dtype instance from this UInt32 ZDType.
1103
+
1104
+ Returns
1105
+ -------
1106
+ np.dtypes.UInt32DType
1107
+ The NumPy unsigned 32-bit integer dtype.
1108
+ """
1109
+ byte_order = endianness_to_numpy_str(self.endianness)
1110
+ return self.dtype_cls().newbyteorder(byte_order)
1111
+
1112
+ @classmethod
1113
+ def _from_json_v2(cls, data: DTypeJSON) -> Self:
1114
+ """
1115
+ Create an instance of this data type from Zarr V2-flavored JSON.
1116
+
1117
+ Parameters
1118
+ ----------
1119
+ data : DTypeJSON
1120
+ The JSON data.
1121
+
1122
+ Returns
1123
+ -------
1124
+ Self
1125
+ An instance of this data type.
1126
+
1127
+ Raises
1128
+ ------
1129
+ DataTypeValidationError
1130
+ If the input JSON is not a valid representation of this class 32-bit unsigned
1131
+ integer.
1132
+ """
1133
+ if cls._check_json_v2(data):
1134
+ # Going via NumPy ensures that we get the endianness correct without
1135
+ # annoying string parsing.
1136
+ name = data["name"]
1137
+ return cls.from_native_dtype(np.dtype(name))
1138
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected one of the strings {cls._zarr_v2_names}."
1139
+ raise DataTypeValidationError(msg)
1140
+
1141
+ @classmethod
1142
+ def _from_json_v3(cls, data: DTypeJSON) -> Self:
1143
+ """
1144
+ Create an instance of this data type from Zarr V3-flavored JSON.
1145
+
1146
+ Parameters
1147
+ ----------
1148
+ data : DTypeJSON
1149
+ The JSON data.
1150
+
1151
+ Returns
1152
+ -------
1153
+ Self
1154
+ An instance of this data type.
1155
+
1156
+ Raises
1157
+ ------
1158
+ DataTypeValidationError
1159
+ If the input JSON is not a valid representation of this class 32-bit unsigned
1160
+ integer.
1161
+ """
1162
+ if cls._check_json_v3(data):
1163
+ return cls()
1164
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected the string {cls._zarr_v3_name!r}"
1165
+ raise DataTypeValidationError(msg)
1166
+
1167
+ @overload
1168
+ def to_json(self, zarr_format: Literal[2]) -> DTypeConfig_V2[Literal[">u4", "<u4"], None]: ...
1169
+ @overload
1170
+ def to_json(self, zarr_format: Literal[3]) -> Literal["uint32"]: ...
1171
+ def to_json(
1172
+ self, zarr_format: ZarrFormat
1173
+ ) -> DTypeConfig_V2[Literal[">u4", "<u4"], None] | Literal["uint32"]:
1174
+ """
1175
+ Convert the data type to a JSON-serializable form.
1176
+
1177
+ Parameters
1178
+ ----------
1179
+ zarr_format : ZarrFormat
1180
+ The Zarr format version.
1181
+
1182
+ Returns
1183
+ -------
1184
+ DTypeConfig_V2[Literal[">u4", "<u4"], None] | Literal["uint32"]
1185
+ The JSON-serializable representation of the data type
1186
+ """
1187
+ if zarr_format == 2:
1188
+ name = self.to_native_dtype().str
1189
+ return {"name": name, "object_codec_id": None}
1190
+ elif zarr_format == 3:
1191
+ return self._zarr_v3_name
1192
+ raise ValueError(f"zarr_format must be 2 or 3, got {zarr_format}")
1193
+
1194
+ @property
1195
+ def item_size(self) -> int:
1196
+ """
1197
+ The size of a single scalar in bytes.
1198
+
1199
+ Returns
1200
+ -------
1201
+ int
1202
+ The size of a single scalar in bytes.
1203
+ """
1204
+ return 4
1205
+
1206
+
1207
+ @dataclass(frozen=True, kw_only=True)
1208
+ class Int64(BaseInt[np.dtypes.Int64DType, np.int64], HasEndianness):
1209
+ """
1210
+ A Zarr data type for arrays containing 64-bit signed integers.
1211
+
1212
+ Wraps the [`np.dtypes.Int64DType`][numpy.dtypes.Int64DType] data type. Scalars for this data type are instances of
1213
+ [`np.int64`][numpy.int64].
1214
+
1215
+ Attributes
1216
+ ----------
1217
+ dtype_cls : np.dtypes.Int64DType
1218
+ The class of the underlying NumPy dtype.
1219
+
1220
+ References
1221
+ ----------
1222
+ This class implements the 64-bit signed integer data type defined in Zarr V2 and V3.
1223
+
1224
+ See the [Zarr V2](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v2/v2.0.rst#data-type-encoding) and [Zarr V3](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v3/data-types/index.rst) specification documents for details.
1225
+ """
1226
+
1227
+ dtype_cls = np.dtypes.Int64DType
1228
+ _zarr_v3_name: ClassVar[Literal["int64"]] = "int64"
1229
+ _zarr_v2_names: ClassVar[tuple[Literal[">i8"], Literal["<i8"]]] = (">i8", "<i8")
1230
+
1231
+ @classmethod
1232
+ def from_native_dtype(cls, dtype: TBaseDType) -> Self:
1233
+ """
1234
+ Create an Int64 from an np.dtype('int64') instance.
1235
+
1236
+ Parameters
1237
+ ----------
1238
+ dtype : TBaseDType
1239
+ The NumPy data type.
1240
+
1241
+ Returns
1242
+ -------
1243
+ Self
1244
+ An instance of this data type.
1245
+
1246
+ Raises
1247
+ ------
1248
+ DataTypeValidationError
1249
+ If the input data type is not a valid representation of this class 64-bit signed
1250
+ integer.
1251
+ """
1252
+ if cls._check_native_dtype(dtype):
1253
+ return cls(endianness=get_endianness_from_numpy_dtype(dtype))
1254
+ raise DataTypeValidationError(
1255
+ f"Invalid data type: {dtype}. Expected an instance of {cls.dtype_cls}"
1256
+ )
1257
+
1258
+ def to_native_dtype(self) -> np.dtypes.Int64DType:
1259
+ """
1260
+ Create a NumPy signed 64-bit integer dtype instance from this Int64 ZDType.
1261
+
1262
+ Returns
1263
+ -------
1264
+ np.dtypes.Int64DType
1265
+ The NumPy signed 64-bit integer dtype.
1266
+ """
1267
+ byte_order = endianness_to_numpy_str(self.endianness)
1268
+ return self.dtype_cls().newbyteorder(byte_order)
1269
+
1270
+ @classmethod
1271
+ def _from_json_v2(cls, data: DTypeJSON) -> Self:
1272
+ """
1273
+ Create an instance of this data type from Zarr V2-flavored JSON.
1274
+
1275
+ Parameters
1276
+ ----------
1277
+ data : DTypeJSON
1278
+ The JSON data.
1279
+
1280
+ Returns
1281
+ -------
1282
+ Self
1283
+ An instance of this data type.
1284
+
1285
+ Raises
1286
+ ------
1287
+ DataTypeValidationError
1288
+ If the input JSON is not a valid representation of this class 64-bit signed
1289
+ integer.
1290
+ """
1291
+ if cls._check_json_v2(data):
1292
+ # Going via NumPy ensures that we get the endianness correct without
1293
+ # annoying string parsing.
1294
+ name = data["name"]
1295
+ return cls.from_native_dtype(np.dtype(name))
1296
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected one of the strings {cls._zarr_v2_names}."
1297
+ raise DataTypeValidationError(msg)
1298
+
1299
+ @classmethod
1300
+ def _from_json_v3(cls, data: DTypeJSON) -> Self:
1301
+ """
1302
+ Create an instance of this data type from Zarr V3-flavored JSON.
1303
+
1304
+ Parameters
1305
+ ----------
1306
+ data : DTypeJSON
1307
+ The JSON data.
1308
+
1309
+ Returns
1310
+ -------
1311
+ Self
1312
+ An instance of this data type.
1313
+
1314
+ Raises
1315
+ ------
1316
+ DataTypeValidationError
1317
+ If the input JSON is not a valid representation of this class 64-bit signed
1318
+ integer.
1319
+ """
1320
+ if cls._check_json_v3(data):
1321
+ return cls()
1322
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected the string {cls._zarr_v3_name!r}"
1323
+ raise DataTypeValidationError(msg)
1324
+
1325
+ @overload
1326
+ def to_json(self, zarr_format: Literal[2]) -> DTypeConfig_V2[Literal[">i8", "<i8"], None]: ...
1327
+ @overload
1328
+ def to_json(self, zarr_format: Literal[3]) -> Literal["int64"]: ...
1329
+ def to_json(
1330
+ self, zarr_format: ZarrFormat
1331
+ ) -> DTypeConfig_V2[Literal[">i8", "<i8"], None] | Literal["int64"]:
1332
+ """
1333
+ Convert the data type to a JSON-serializable form.
1334
+
1335
+ Parameters
1336
+ ----------
1337
+ zarr_format : ZarrFormat
1338
+ The Zarr format version.
1339
+
1340
+ Returns
1341
+ -------
1342
+ DTypeConfig_V2[Literal[">i8", "<i8"], None] | Literal["int64"]
1343
+ The JSON-serializable representation of the data type.
1344
+ """
1345
+ if zarr_format == 2:
1346
+ name = self.to_native_dtype().str
1347
+ return {"name": name, "object_codec_id": None}
1348
+ elif zarr_format == 3:
1349
+ return self._zarr_v3_name
1350
+ raise ValueError(f"zarr_format must be 2 or 3, got {zarr_format}")
1351
+
1352
+ @property
1353
+ def item_size(self) -> int:
1354
+ """
1355
+ The size of a single scalar in bytes.
1356
+
1357
+ Returns
1358
+ -------
1359
+ int
1360
+ The size of a single scalar in bytes.
1361
+ """
1362
+ return 8
1363
+
1364
+
1365
+ @dataclass(frozen=True, kw_only=True)
1366
+ class UInt64(BaseInt[np.dtypes.UInt64DType, np.uint64], HasEndianness):
1367
+ """
1368
+ A Zarr data type for arrays containing 64-bit unsigned integers.
1369
+
1370
+ Wraps the [`np.dtypes.UInt64DType`][numpy.dtypes.UInt64DType] data type. Scalars for this data type
1371
+ are instances of [`np.uint64`][numpy.uint64].
1372
+
1373
+ Attributes
1374
+ ----------
1375
+ dtype_cls: np.dtypes.UInt64DType
1376
+ The class of the underlying NumPy dtype.
1377
+
1378
+ References
1379
+ ----------
1380
+ This class implements the unsigned 64-bit integer data type defined in Zarr V2 and V3.
1381
+
1382
+ See the [Zarr V2](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v2/v2.0.rst#data-type-encoding) and [Zarr V3](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v3/data-types/index.rst) specification documents for details.
1383
+ """
1384
+
1385
+ dtype_cls = np.dtypes.UInt64DType
1386
+ _zarr_v3_name: ClassVar[Literal["uint64"]] = "uint64"
1387
+ _zarr_v2_names: ClassVar[tuple[Literal[">u8"], Literal["<u8"]]] = (">u8", "<u8")
1388
+
1389
+ def to_native_dtype(self) -> np.dtypes.UInt64DType:
1390
+ """
1391
+ Convert the data type to a native NumPy dtype.
1392
+
1393
+ Returns
1394
+ -------
1395
+ np.dtypes.UInt64DType
1396
+ The native NumPy dtype.eeeeeeeeeeeeeeeee
1397
+ """
1398
+ byte_order = endianness_to_numpy_str(self.endianness)
1399
+ return self.dtype_cls().newbyteorder(byte_order)
1400
+
1401
+ @classmethod
1402
+ def _from_json_v2(cls, data: DTypeJSON) -> Self:
1403
+ """
1404
+ Create an instance of this data type from Zarr V2-flavored JSON.
1405
+
1406
+ Parameters
1407
+ ----------
1408
+ data : DTypeJSON
1409
+ The JSON data.
1410
+
1411
+ Returns
1412
+ -------
1413
+ Self
1414
+ An instance of this data type.
1415
+
1416
+ Raises
1417
+ ------
1418
+ DataTypeValidationError
1419
+ If the input JSON is not a valid representation of this class unsigned 64-bit
1420
+ integer.
1421
+ """
1422
+ if cls._check_json_v2(data):
1423
+ # Going via NumPy ensures that we get the endianness correct without
1424
+ # annoying string parsing.
1425
+ name = data["name"]
1426
+ return cls.from_native_dtype(np.dtype(name))
1427
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected one of the strings {cls._zarr_v2_names}."
1428
+ raise DataTypeValidationError(msg)
1429
+
1430
+ @classmethod
1431
+ def _from_json_v3(cls, data: DTypeJSON) -> Self:
1432
+ """
1433
+ Create an instance of this data type from Zarr V3-flavored JSON.
1434
+
1435
+ Parameters
1436
+ ----------
1437
+ data : DTypeJSON
1438
+ The JSON data.
1439
+
1440
+ Returns
1441
+ -------
1442
+ Self
1443
+ An instance of this data type.
1444
+
1445
+ Raises
1446
+ ------
1447
+ DataTypeValidationError
1448
+ If the input JSON is not a valid representation of this class unsigned 64-bit
1449
+ integer.
1450
+ """
1451
+ if cls._check_json_v3(data):
1452
+ return cls()
1453
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected the string {cls._zarr_v3_name!r}"
1454
+ raise DataTypeValidationError(msg)
1455
+
1456
+ @overload
1457
+ def to_json(self, zarr_format: Literal[2]) -> DTypeConfig_V2[Literal[">u8", "<u8"], None]: ...
1458
+ @overload
1459
+ def to_json(self, zarr_format: Literal[3]) -> Literal["uint64"]: ...
1460
+
1461
+ def to_json(
1462
+ self, zarr_format: ZarrFormat
1463
+ ) -> DTypeConfig_V2[Literal[">u8", "<u8"], None] | Literal["uint64"]:
1464
+ """
1465
+ Convert the data type to a JSON-serializable form.
1466
+
1467
+ Parameters
1468
+ ----------
1469
+ zarr_format : ZarrFormat
1470
+ The Zarr format version.
1471
+
1472
+ Returns
1473
+ -------
1474
+ DTypeConfig_V2[Literal[">u8", "<u8"], None] | Literal["uint64"]
1475
+ The JSON-serializable representation of the data type.
1476
+ """
1477
+ if zarr_format == 2:
1478
+ name = self.to_native_dtype().str
1479
+ return {"name": name, "object_codec_id": None}
1480
+ elif zarr_format == 3:
1481
+ return self._zarr_v3_name
1482
+ raise ValueError(f"zarr_format must be 2 or 3, got {zarr_format}") # pragma: no cover
1483
+
1484
+ @classmethod
1485
+ def from_native_dtype(cls, dtype: TBaseDType) -> Self:
1486
+ """
1487
+ Create an instance of this data type from a native NumPy dtype.
1488
+
1489
+ Parameters
1490
+ ----------
1491
+ dtype : TBaseDType
1492
+ The native NumPy dtype.
1493
+
1494
+ Returns
1495
+ -------
1496
+ Self
1497
+ An instance of this data type.
1498
+
1499
+ Raises
1500
+ ------
1501
+ DataTypeValidationError
1502
+ If the input dtype is not a valid representation of this class unsigned 64-bit
1503
+ integer.
1504
+ """
1505
+ if cls._check_native_dtype(dtype):
1506
+ return cls(endianness=get_endianness_from_numpy_dtype(dtype))
1507
+ raise DataTypeValidationError(
1508
+ f"Invalid data type: {dtype}. Expected an instance of {cls.dtype_cls}"
1509
+ )
1510
+
1511
+ @property
1512
+ def item_size(self) -> int:
1513
+ """
1514
+ The size of a single scalar in bytes.
1515
+
1516
+ Returns
1517
+ -------
1518
+ int
1519
+ The size of a single scalar in bytes.
1520
+ """
1521
+ return 8
.venv/lib/python3.11/site-packages/zarr/core/dtype/npy/string.py ADDED
@@ -0,0 +1,825 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from dataclasses import dataclass
5
+ from typing import (
6
+ TYPE_CHECKING,
7
+ ClassVar,
8
+ Literal,
9
+ Protocol,
10
+ Self,
11
+ TypedDict,
12
+ TypeGuard,
13
+ overload,
14
+ runtime_checkable,
15
+ )
16
+
17
+ import numpy as np
18
+
19
+ from zarr.core.common import NamedConfig
20
+ from zarr.core.dtype.common import (
21
+ DataTypeValidationError,
22
+ DTypeConfig_V2,
23
+ DTypeJSON,
24
+ HasEndianness,
25
+ HasItemSize,
26
+ HasLength,
27
+ HasObjectCodec,
28
+ check_dtype_spec_v2,
29
+ v3_unstable_dtype_warning,
30
+ )
31
+ from zarr.core.dtype.npy.common import (
32
+ check_json_str,
33
+ endianness_to_numpy_str,
34
+ get_endianness_from_numpy_dtype,
35
+ )
36
+ from zarr.core.dtype.wrapper import TDType_co, ZDType
37
+
38
+ if TYPE_CHECKING:
39
+ from zarr.core.common import JSON, ZarrFormat
40
+ from zarr.core.dtype.wrapper import TBaseDType
41
+
42
+ _NUMPY_SUPPORTS_VLEN_STRING = hasattr(np.dtypes, "StringDType")
43
+
44
+
45
+ @runtime_checkable
46
+ class SupportsStr(Protocol):
47
+ def __str__(self) -> str: ...
48
+
49
+
50
+ class LengthBytesConfig(TypedDict):
51
+ """
52
+ Configuration for a fixed-length string data type in Zarr V3.
53
+
54
+ Attributes
55
+ ----------
56
+ length_bytes : int
57
+ The length in bytes of the data associated with this configuration.
58
+ """
59
+
60
+ length_bytes: int
61
+
62
+
63
+ class FixedLengthUTF32JSON_V2(DTypeConfig_V2[str, None]):
64
+ """
65
+ A wrapper around the JSON representation of the ``FixedLengthUTF32`` data type in Zarr V2.
66
+
67
+ The ``name`` field of this class contains the value that would appear under the
68
+ ``dtype`` field in Zarr V2 array metadata.
69
+
70
+ References
71
+ ----------
72
+ The structure of the ``name`` field is defined in the Zarr V2
73
+ [specification document](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v2/v2.0.rst#data-type-encoding).
74
+
75
+ Examples
76
+ --------
77
+
78
+ ```python
79
+ {
80
+ "name": "<U12",
81
+ "object_codec_id": None
82
+ }
83
+ ```
84
+ """
85
+
86
+
87
+ class FixedLengthUTF32JSON_V3(NamedConfig[Literal["fixed_length_utf32"], LengthBytesConfig]):
88
+ """
89
+ The JSON representation of the ``FixedLengthUTF32`` data type in Zarr V3.
90
+
91
+ References
92
+ ----------
93
+ This representation is not currently defined in an external specification.
94
+
95
+ Examples
96
+ --------
97
+ ```python
98
+ {
99
+ "name": "fixed_length_utf32",
100
+ "configuration": {
101
+ "length_bytes": 12
102
+ }
103
+ }
104
+ ```
105
+ """
106
+
107
+
108
+ @dataclass(frozen=True, kw_only=True)
109
+ class FixedLengthUTF32(
110
+ ZDType[np.dtypes.StrDType[int], np.str_], HasEndianness, HasLength, HasItemSize
111
+ ):
112
+ """
113
+ A Zarr data type for arrays containing fixed-length UTF-32 strings.
114
+
115
+ Wraps the ``np.dtypes.StrDType`` data type. Scalars for this data type are instances of
116
+ ``np.str_``.
117
+
118
+ Attributes
119
+ ----------
120
+ dtype_cls : Type[np.dtypes.StrDType]
121
+ The NumPy dtype class for this data type.
122
+ _zarr_v3_name : ClassVar[Literal["fixed_length_utf32"]]
123
+ The name of this data type in Zarr V3.
124
+ code_point_bytes : ClassVar[int] = 4
125
+ The number of bytes per code point in UTF-32, which is 4.
126
+ """
127
+
128
+ dtype_cls = np.dtypes.StrDType
129
+ _zarr_v3_name: ClassVar[Literal["fixed_length_utf32"]] = "fixed_length_utf32"
130
+ code_point_bytes: ClassVar[int] = 4 # utf32 is 4 bytes per code point
131
+
132
+ def __post_init__(self) -> None:
133
+ """
134
+ We don't allow instances of this class with length less than 1 because there is no way such
135
+ a data type can contain actual data.
136
+ """
137
+ if self.length < 1:
138
+ raise ValueError(f"length must be >= 1, got {self.length}.")
139
+
140
+ @classmethod
141
+ def from_native_dtype(cls, dtype: TBaseDType) -> Self:
142
+ """
143
+ Create a FixedLengthUTF32 from a NumPy data type.
144
+
145
+ Parameters
146
+ ----------
147
+ dtype : TBaseDType
148
+ The NumPy data type.
149
+
150
+ Returns
151
+ -------
152
+ Self
153
+ An instance of this data type.
154
+ """
155
+ if cls._check_native_dtype(dtype):
156
+ endianness = get_endianness_from_numpy_dtype(dtype)
157
+ return cls(
158
+ length=dtype.itemsize // (cls.code_point_bytes),
159
+ endianness=endianness,
160
+ )
161
+ raise DataTypeValidationError(
162
+ f"Invalid data type: {dtype}. Expected an instance of {cls.dtype_cls}"
163
+ )
164
+
165
+ def to_native_dtype(self) -> np.dtypes.StrDType[int]:
166
+ """
167
+ Convert the FixedLengthUTF32 instance to a NumPy data type.
168
+
169
+ Returns
170
+ -------
171
+ np.dtypes.StrDType[int]
172
+ The NumPy data type.
173
+ """
174
+ byte_order = endianness_to_numpy_str(self.endianness)
175
+ return self.dtype_cls(self.length).newbyteorder(byte_order)
176
+
177
+ @classmethod
178
+ def _check_json_v2(cls, data: DTypeJSON) -> TypeGuard[FixedLengthUTF32JSON_V2]:
179
+ """
180
+ Check that the input is a valid JSON representation of a NumPy U dtype.
181
+
182
+ Parameters
183
+ ----------
184
+ data : DTypeJSON
185
+ The JSON data.
186
+
187
+ Returns
188
+ -------
189
+ TypeGuard[FixedLengthUTF32JSON_V2]
190
+ Whether the input is a valid JSON representation of a NumPy U dtype.
191
+ """
192
+ return (
193
+ check_dtype_spec_v2(data)
194
+ and isinstance(data["name"], str)
195
+ and re.match(r"^[><]U\d+$", data["name"]) is not None
196
+ and data["object_codec_id"] is None
197
+ )
198
+
199
+ @classmethod
200
+ def _check_json_v3(cls, data: DTypeJSON) -> TypeGuard[FixedLengthUTF32JSON_V3]:
201
+ """
202
+ Check that the input is a valid JSON representation of this class in Zarr V3.
203
+
204
+ Parameters
205
+ ----------
206
+ data : DTypeJSON
207
+ The JSON data.
208
+
209
+ Returns
210
+ -------
211
+ TypeGuard[FixedLengthUTF32JSONV3]
212
+ Whether the input is a valid JSON representation of a NumPy U dtype.
213
+ """
214
+ return (
215
+ isinstance(data, dict)
216
+ and set(data.keys()) == {"name", "configuration"}
217
+ and data["name"] == cls._zarr_v3_name
218
+ and "configuration" in data
219
+ and isinstance(data["configuration"], dict)
220
+ and set(data["configuration"].keys()) == {"length_bytes"}
221
+ and isinstance(data["configuration"]["length_bytes"], int)
222
+ )
223
+
224
+ @overload
225
+ def to_json(self, zarr_format: Literal[2]) -> DTypeConfig_V2[str, None]: ...
226
+
227
+ @overload
228
+ def to_json(self, zarr_format: Literal[3]) -> FixedLengthUTF32JSON_V3: ...
229
+
230
+ def to_json(
231
+ self, zarr_format: ZarrFormat
232
+ ) -> DTypeConfig_V2[str, None] | FixedLengthUTF32JSON_V3:
233
+ """
234
+ Convert the FixedLengthUTF32 instance to a JSON representation.
235
+
236
+ Parameters
237
+ ----------
238
+ zarr_format : ZarrFormat
239
+ The Zarr format to use.
240
+
241
+ Returns
242
+ -------
243
+ DTypeConfig_V2[str, None] | FixedLengthUTF32JSON_V3
244
+ The JSON representation of the data type.
245
+ """
246
+ if zarr_format == 2:
247
+ return {"name": self.to_native_dtype().str, "object_codec_id": None}
248
+ elif zarr_format == 3:
249
+ v3_unstable_dtype_warning(self)
250
+ return {
251
+ "name": self._zarr_v3_name,
252
+ "configuration": {"length_bytes": self.length * self.code_point_bytes},
253
+ }
254
+ raise ValueError(f"zarr_format must be 2 or 3, got {zarr_format}") # pragma: no cover
255
+
256
+ @classmethod
257
+ def _from_json_v2(cls, data: DTypeJSON) -> Self:
258
+ """
259
+ Create a FixedLengthUTF32 from a JSON representation of a NumPy U dtype.
260
+
261
+ Parameters
262
+ ----------
263
+ data : DTypeJSON
264
+ The JSON data.
265
+
266
+ Returns
267
+ -------
268
+ Self
269
+ An instance of this data type.
270
+ """
271
+ if cls._check_json_v2(data):
272
+ # Construct the NumPy dtype instead of string parsing.
273
+ name = data["name"]
274
+ return cls.from_native_dtype(np.dtype(name))
275
+ raise DataTypeValidationError(
276
+ f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected a string representation of a NumPy U dtype."
277
+ )
278
+
279
+ @classmethod
280
+ def _from_json_v3(cls, data: DTypeJSON) -> Self:
281
+ """
282
+ Create a FixedLengthUTF32 from a JSON representation of a NumPy U dtype.
283
+
284
+ Parameters
285
+ ----------
286
+ data : DTypeJSON
287
+ The JSON data.
288
+
289
+ Returns
290
+ -------
291
+ Self
292
+ An instance of this data type.
293
+ """
294
+ if cls._check_json_v3(data):
295
+ return cls(length=data["configuration"]["length_bytes"] // cls.code_point_bytes)
296
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected {cls._zarr_v3_name}."
297
+ raise DataTypeValidationError(msg)
298
+
299
+ def default_scalar(self) -> np.str_:
300
+ """
301
+ Return the default scalar value for this data type.
302
+
303
+ Returns
304
+ -------
305
+ ``np.str_``
306
+ The default scalar value.
307
+ """
308
+ return np.str_("")
309
+
310
+ def to_json_scalar(self, data: object, *, zarr_format: ZarrFormat) -> str:
311
+ """
312
+ Convert the scalar value to a JSON representation.
313
+
314
+ Parameters
315
+ ----------
316
+ data : object
317
+ The scalar value.
318
+ zarr_format : ZarrFormat
319
+ The Zarr format to use.
320
+
321
+ Returns
322
+ -------
323
+ str
324
+ The JSON representation of the scalar value.
325
+ """
326
+ return str(data)
327
+
328
+ def from_json_scalar(self, data: JSON, *, zarr_format: ZarrFormat) -> np.str_:
329
+ """
330
+ Convert the JSON representation of a scalar value to the native scalar value.
331
+
332
+ Parameters
333
+ ----------
334
+ data : JSON
335
+ The JSON data.
336
+ zarr_format : ZarrFormat
337
+ The Zarr format to use.
338
+
339
+ Returns
340
+ -------
341
+ ``np.str_``
342
+ The native scalar value.
343
+ """
344
+ if check_json_str(data):
345
+ return self.to_native_dtype().type(data)
346
+ raise TypeError(f"Invalid type: {data}. Expected a string.") # pragma: no cover
347
+
348
+ def _check_scalar(self, data: object) -> TypeGuard[SupportsStr]:
349
+ """
350
+ Check that the input is a valid scalar value for this data type.
351
+
352
+ Parameters
353
+ ----------
354
+ data : object
355
+ The scalar value.
356
+
357
+ Returns
358
+ -------
359
+ TypeGuard[SupportsStr]
360
+ Whether the input is a valid scalar value for this data type.
361
+ """
362
+ # this is generous for backwards compatibility
363
+ return isinstance(data, SupportsStr)
364
+
365
+ def cast_scalar(self, data: object) -> np.str_:
366
+ """
367
+ Cast the scalar value to the native scalar value.
368
+
369
+ Parameters
370
+ ----------
371
+ data : object
372
+ The scalar value.
373
+
374
+ Returns
375
+ -------
376
+ ``np.str_``
377
+ The native scalar value.
378
+ """
379
+ if self._check_scalar(data):
380
+ # We explicitly truncate before casting because of the following NumPy behavior:
381
+ # >>> x = np.dtype('U3').type('hello world')
382
+ # >>> x
383
+ # np.str_('hello world')
384
+ # >>> x.dtype
385
+ # dtype('U11')
386
+
387
+ return self.to_native_dtype().type(str(data)[: self.length])
388
+
389
+ msg = ( # pragma: no cover
390
+ f"Cannot convert object {data!r} with type {type(data)} to a scalar compatible with the "
391
+ f"data type {self}."
392
+ )
393
+ raise TypeError(msg) # pragma: no-cover
394
+
395
+ @property
396
+ def item_size(self) -> int:
397
+ """
398
+ The size of a single scalar in bytes.
399
+
400
+ Returns
401
+ -------
402
+ int
403
+ The size of a single scalar in bytes.
404
+ """
405
+ return self.length * self.code_point_bytes
406
+
407
+
408
+ def check_vlen_string_json_scalar(data: object) -> TypeGuard[int | str | float]:
409
+ """
410
+ Check if the input is a valid JSON scalar for a variable-length string.
411
+
412
+ This function is generous for backwards compatibility, as Zarr Python v2 would use ints for
413
+ variable-length string fill values.
414
+
415
+ Parameters
416
+ ----------
417
+ data : object
418
+ The JSON value to check.
419
+
420
+ Returns
421
+ -------
422
+ TypeGuard[int | str | float]
423
+ True if the input is a valid scalar for a variable-length string.
424
+ """
425
+ return isinstance(data, int | str | float)
426
+
427
+
428
+ class VariableLengthUTF8JSON_V2(DTypeConfig_V2[Literal["|O"], Literal["vlen-utf8"]]):
429
+ """
430
+ A wrapper around the JSON representation of the ``VariableLengthUTF8`` data type in Zarr V2.
431
+
432
+ The ``name`` field of this class contains the value that would appear under the
433
+ ``dtype`` field in Zarr V2 array metadata. The ``object_codec_id`` field is always ``"vlen-utf8"``.
434
+
435
+ References
436
+ ----------
437
+ The structure of the ``name`` field is defined in the Zarr V2
438
+ [specification document](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v2/v2.0.rst#data-type-encoding).
439
+
440
+
441
+ Examples
442
+ --------
443
+ ```python
444
+ {
445
+ "name": "|O",
446
+ "object_codec_id": "vlen-utf8"
447
+ }
448
+ ```
449
+ """
450
+
451
+
452
+ # VariableLengthUTF8 is defined in two places, conditioned on the version of NumPy.
453
+ # If NumPy 2 is installed, then VariableLengthUTF8 is defined with the NumPy variable length
454
+ # string dtype as the native dtype. Otherwise, VariableLengthUTF8 is defined with the NumPy object
455
+ # dtype as the native dtype.
456
+ class UTF8Base(ZDType[TDType_co, str], HasObjectCodec):
457
+ """
458
+ A base class for variable-length UTF-8 string data types.
459
+
460
+ Not intended for direct use, but as a base for concrete implementations.
461
+
462
+ Attributes
463
+ ----------
464
+ object_codec_id : ClassVar[Literal["vlen-utf8"]]
465
+ The object codec ID for this data type.
466
+
467
+ References
468
+ ----------
469
+ This data type does not have a Zarr V3 specification.
470
+
471
+ The Zarr V2 data type specification can be found [here](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v2/v2.0.rst#data-type-encoding).
472
+ """
473
+
474
+ _zarr_v3_name: ClassVar[Literal["string"]] = "string"
475
+ object_codec_id: ClassVar[Literal["vlen-utf8"]] = "vlen-utf8"
476
+
477
+ @classmethod
478
+ def from_native_dtype(cls, dtype: TBaseDType) -> Self:
479
+ """
480
+ Create an instance of this data type from a compatible NumPy data type.
481
+
482
+
483
+ Parameters
484
+ ----------
485
+ dtype : TBaseDType
486
+ The native data type.
487
+
488
+ Returns
489
+ -------
490
+ Self
491
+ An instance of this data type.
492
+
493
+ Raises
494
+ ------
495
+ DataTypeValidationError
496
+ If the input is not compatible with this data type.
497
+ """
498
+ if cls._check_native_dtype(dtype):
499
+ return cls()
500
+ raise DataTypeValidationError(
501
+ f"Invalid data type: {dtype}. Expected an instance of {cls.dtype_cls}"
502
+ )
503
+
504
+ @classmethod
505
+ def _check_json_v2(
506
+ cls,
507
+ data: DTypeJSON,
508
+ ) -> TypeGuard[VariableLengthUTF8JSON_V2]:
509
+ """
510
+ "Check if the input is a valid JSON representation of a variable-length UTF-8 string dtype
511
+ for Zarr v2."
512
+
513
+ Parameters
514
+ ----------
515
+ data : DTypeJSON
516
+ The JSON data to check.
517
+
518
+ Returns
519
+ -------
520
+ ``TypeGuard[VariableLengthUTF8JSON_V2]``
521
+ Whether the input is a valid JSON representation of a NumPy "object" data type, and that the
522
+ object codec id is appropriate for variable-length UTF-8 strings.
523
+ """
524
+ return (
525
+ check_dtype_spec_v2(data)
526
+ and data["name"] == "|O"
527
+ and data["object_codec_id"] == cls.object_codec_id
528
+ )
529
+
530
+ @classmethod
531
+ def _check_json_v3(cls, data: DTypeJSON) -> TypeGuard[Literal["variable_length_utf8"]]:
532
+ """
533
+ Check that the input is a valid JSON representation of this class in Zarr V3.
534
+
535
+ Parameters
536
+ ----------
537
+ data : DTypeJSON
538
+ The JSON data to check.
539
+
540
+ Returns
541
+ -------
542
+ TypeGuard[Literal["variable_length_utf8"]]
543
+ Whether the input is a valid JSON representation of a variable length UTF-8 string
544
+ data type.
545
+ """
546
+ return data == cls._zarr_v3_name
547
+
548
+ @classmethod
549
+ def _from_json_v2(cls, data: DTypeJSON) -> Self:
550
+ """
551
+ Create an instance of this class from a JSON representation of a NumPy "object" dtype.
552
+
553
+ Parameters
554
+ ----------
555
+ data : DTypeJSON
556
+ The JSON data to create an instance from.
557
+
558
+ Returns
559
+ -------
560
+ Self
561
+ An instance of this data type.
562
+ """
563
+ if cls._check_json_v2(data):
564
+ return cls()
565
+ msg = (
566
+ f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected the string '|O'"
567
+ )
568
+ raise DataTypeValidationError(msg)
569
+
570
+ @classmethod
571
+ def _from_json_v3(cls, data: DTypeJSON) -> Self:
572
+ """
573
+ Create an instance of this class from a JSON representation of a variable length UTF-8
574
+ string data type.
575
+
576
+ Parameters
577
+ ----------
578
+ data : DTypeJSON
579
+ The JSON data to create an instance from.
580
+
581
+ Returns
582
+ -------
583
+ Self
584
+ An instance of this data type.
585
+ """
586
+ if cls._check_json_v3(data):
587
+ return cls()
588
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected {cls._zarr_v3_name}."
589
+ raise DataTypeValidationError(msg)
590
+
591
+ @overload
592
+ def to_json(self, zarr_format: Literal[2]) -> VariableLengthUTF8JSON_V2: ...
593
+ @overload
594
+ def to_json(self, zarr_format: Literal[3]) -> Literal["string"]: ...
595
+
596
+ def to_json(self, zarr_format: ZarrFormat) -> VariableLengthUTF8JSON_V2 | Literal["string"]:
597
+ """
598
+ Convert this data type to a JSON representation.
599
+
600
+ Parameters
601
+ ----------
602
+ zarr_format : int
603
+ The zarr format to use for the JSON representation.
604
+
605
+ Returns
606
+ -------
607
+ ``VariableLengthUTF8JSON_V2 | Literal["string"]``
608
+ The JSON representation of this data type.
609
+ """
610
+ if zarr_format == 2:
611
+ return {"name": "|O", "object_codec_id": self.object_codec_id}
612
+ elif zarr_format == 3:
613
+ return self._zarr_v3_name
614
+ raise ValueError(f"zarr_format must be 2 or 3, got {zarr_format}") # pragma: no cover
615
+
616
+ def default_scalar(self) -> str:
617
+ """
618
+ Return the default scalar value for this data type.
619
+
620
+ Returns
621
+ -------
622
+ str
623
+ The default scalar value.
624
+ """
625
+ return ""
626
+
627
+ def to_json_scalar(self, data: object, *, zarr_format: ZarrFormat) -> str:
628
+ """
629
+ Convert a scalar value to a JSON representation.
630
+
631
+ Parameters
632
+ ----------
633
+ data : object
634
+ The scalar value to convert.
635
+ zarr_format : int
636
+ The zarr format to use for the JSON representation.
637
+
638
+ Returns
639
+ -------
640
+ str
641
+ The JSON representation of the scalar value.
642
+ """
643
+ if self._check_scalar(data):
644
+ return self._cast_scalar_unchecked(data)
645
+ raise TypeError(f"Invalid type: {data}. Expected a string.")
646
+
647
+ def from_json_scalar(self, data: JSON, *, zarr_format: ZarrFormat) -> str:
648
+ """
649
+ Convert a JSON representation of a scalar value to the native scalar type.
650
+
651
+ Parameters
652
+ ----------
653
+ data : JSON
654
+ The JSON representation of the scalar value.
655
+ zarr_format : int
656
+ The zarr format to use for the JSON representation.
657
+
658
+ Returns
659
+ -------
660
+ str
661
+ The native scalar type of the scalar value.
662
+ """
663
+ if not check_vlen_string_json_scalar(data):
664
+ raise TypeError(f"Invalid type: {data}. Expected a string or number.")
665
+ return str(data)
666
+
667
+ def _check_scalar(self, data: object) -> TypeGuard[SupportsStr]:
668
+ """
669
+ Check that the input is a valid scalar value for this data type.
670
+
671
+ Parameters
672
+ ----------
673
+ data : object
674
+ The scalar value to check.
675
+
676
+ Returns
677
+ -------
678
+ TypeGuard[SupportsStr]
679
+ Whether the input is a valid scalar value for this data type.
680
+ """
681
+ return isinstance(data, SupportsStr)
682
+
683
+ def _cast_scalar_unchecked(self, data: SupportsStr) -> str:
684
+ """
685
+ Cast a scalar value to a string.
686
+
687
+ Parameters
688
+ ----------
689
+ data : object
690
+ The scalar value to cast.
691
+
692
+ Returns
693
+ -------
694
+ str
695
+ The string representation of the scalar value.
696
+ """
697
+ return str(data)
698
+
699
+ def cast_scalar(self, data: object) -> str:
700
+ """
701
+ Cast an object to a string.
702
+
703
+ Parameters
704
+ ----------
705
+ data : object
706
+ The value to cast.
707
+
708
+ Returns
709
+ -------
710
+ str
711
+ The input cast to str.
712
+ """
713
+ if self._check_scalar(data):
714
+ return self._cast_scalar_unchecked(data)
715
+ msg = ( # pragma: no cover
716
+ f"Cannot convert object {data!r} with type {type(data)} to a scalar compatible with the "
717
+ f"data type {self}."
718
+ )
719
+ raise TypeError(msg) # pragma: no cover
720
+
721
+
722
+ if _NUMPY_SUPPORTS_VLEN_STRING:
723
+
724
+ @dataclass(frozen=True, kw_only=True)
725
+ class VariableLengthUTF8(UTF8Base[np.dtypes.StringDType]): # type: ignore[type-var]
726
+ """
727
+ A Zarr data type for arrays containing variable-length UTF-8 strings.
728
+
729
+ Wraps the ``np.dtypes.StringDType`` data type. Scalars for this data type are instances
730
+ of ``str``.
731
+
732
+
733
+ Attributes
734
+ ----------
735
+ dtype_cls : Type[np.dtypes.StringDType]
736
+ The NumPy dtype class for this data type.
737
+ _zarr_v3_name : ClassVar[Literal["variable_length_utf8"]] = "variable_length_utf8"
738
+ The name of this data type in Zarr V3.
739
+ object_codec_id : ClassVar[Literal["vlen-utf8"]] = "vlen-utf8"
740
+ The object codec ID for this data type.
741
+ """
742
+
743
+ dtype_cls = np.dtypes.StringDType
744
+
745
+ @classmethod
746
+ def from_native_dtype(cls, dtype: TBaseDType) -> Self:
747
+ """
748
+ Create an instance of this data type from a compatible NumPy data type.
749
+ We reject NumPy StringDType instances that have the `na_object` field set,
750
+ because this is not representable by the Zarr `string` data type.
751
+
752
+ Parameters
753
+ ----------
754
+ dtype : TBaseDType
755
+ The native data type.
756
+
757
+ Returns
758
+ -------
759
+ Self
760
+ An instance of this data type.
761
+
762
+ Raises
763
+ ------
764
+ DataTypeValidationError
765
+ If the input is not compatible with this data type.
766
+ ValueError
767
+ If the input is `numpy.dtypes.StringDType` and has `na_object` set.
768
+ """
769
+ if cls._check_native_dtype(dtype):
770
+ if hasattr(dtype, "na_object"):
771
+ msg = (
772
+ f"Zarr data type resolution from {dtype} failed. "
773
+ "Attempted to resolve a zarr data type from a `numpy.dtypes.StringDType` "
774
+ "with `na_object` set, which is not supported."
775
+ )
776
+ raise ValueError(msg)
777
+ return cls()
778
+ raise DataTypeValidationError(
779
+ f"Invalid data type: {dtype}. Expected an instance of {cls.dtype_cls}"
780
+ )
781
+
782
+ def to_native_dtype(self) -> np.dtypes.StringDType:
783
+ """
784
+ Create a NumPy string dtype from this VariableLengthUTF8 ZDType.
785
+
786
+ Returns
787
+ -------
788
+ np.dtypes.StringDType
789
+ The NumPy string dtype.
790
+ """
791
+ return self.dtype_cls()
792
+
793
+ else:
794
+ # Numpy pre-2 does not have a variable length string dtype, so we use the Object dtype instead.
795
+ @dataclass(frozen=True, kw_only=True)
796
+ class VariableLengthUTF8(UTF8Base[np.dtypes.ObjectDType]): # type: ignore[no-redef]
797
+ """
798
+ A Zarr data type for arrays containing variable-length UTF-8 strings.
799
+
800
+ Wraps the ``np.dtypes.ObjectDType`` data type. Scalars for this data type are instances
801
+ of ``str``.
802
+
803
+
804
+ Attributes
805
+ ----------
806
+ dtype_cls : Type[np.dtypes.ObjectDType]
807
+ The NumPy dtype class for this data type.
808
+ _zarr_v3_name : ClassVar[Literal["variable_length_utf8"]] = "variable_length_utf8"
809
+ The name of this data type in Zarr V3.
810
+ object_codec_id : ClassVar[Literal["vlen-utf8"]] = "vlen-utf8"
811
+ The object codec ID for this data type.
812
+ """
813
+
814
+ dtype_cls = np.dtypes.ObjectDType
815
+
816
+ def to_native_dtype(self) -> np.dtypes.ObjectDType:
817
+ """
818
+ Create a NumPy object dtype from this VariableLengthUTF8 ZDType.
819
+
820
+ Returns
821
+ -------
822
+ np.dtypes.ObjectDType
823
+ The NumPy object dtype.
824
+ """
825
+ return self.dtype_cls()
.venv/lib/python3.11/site-packages/zarr/core/dtype/npy/structured.py ADDED
@@ -0,0 +1,477 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Sequence
4
+ from dataclasses import dataclass
5
+ from typing import TYPE_CHECKING, ClassVar, Literal, Self, TypeGuard, cast, overload
6
+
7
+ import numpy as np
8
+
9
+ from zarr.core.common import NamedConfig
10
+ from zarr.core.dtype.common import (
11
+ DataTypeValidationError,
12
+ DTypeConfig_V2,
13
+ DTypeJSON,
14
+ HasItemSize,
15
+ StructuredName_V2,
16
+ check_dtype_spec_v2,
17
+ check_structured_dtype_name_v2,
18
+ v3_unstable_dtype_warning,
19
+ )
20
+ from zarr.core.dtype.npy.common import (
21
+ bytes_from_json,
22
+ bytes_to_json,
23
+ check_json_str,
24
+ )
25
+ from zarr.core.dtype.wrapper import TBaseDType, TBaseScalar, ZDType
26
+
27
+ if TYPE_CHECKING:
28
+ from zarr.core.common import JSON, ZarrFormat
29
+
30
+ StructuredScalarLike = list[object] | tuple[object, ...] | bytes | int
31
+
32
+
33
+ class StructuredJSON_V2(DTypeConfig_V2[StructuredName_V2, None]):
34
+ """
35
+ A wrapper around the JSON representation of the ``Structured`` data type in Zarr V2.
36
+
37
+ The ``name`` field is a sequence of sequences, where each inner sequence has two values:
38
+ the field name and the data type name for that field (which could be another sequence).
39
+ The data type names are strings, and the object codec ID is always None.
40
+
41
+ References
42
+ ----------
43
+ The structure of the ``name`` field is defined in the Zarr V2
44
+ [specification document](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v2/v2.0.rst#data-type-encoding).
45
+
46
+ Examples
47
+ --------
48
+ ```python
49
+ {
50
+ "name": [
51
+ ["f0", "<m8[10s]"],
52
+ ["f1", "<m8[10s]"],
53
+ ],
54
+ "object_codec_id": None
55
+ }
56
+ ```
57
+ """
58
+
59
+
60
+ class StructuredJSON_V3(
61
+ NamedConfig[Literal["structured"], dict[str, Sequence[Sequence[str | DTypeJSON]]]]
62
+ ):
63
+ """
64
+ A JSON representation of a structured data type in Zarr V3.
65
+
66
+ References
67
+ ----------
68
+ This representation is not currently defined in an external specification.
69
+
70
+ Examples
71
+ --------
72
+ ```python
73
+ {
74
+ "name": "structured",
75
+ "configuration": {
76
+ "fields": [
77
+ ["f0", "int32"],
78
+ ["f1", "float64"],
79
+ ]
80
+ }
81
+ }
82
+ ```
83
+ """
84
+
85
+
86
+ @dataclass(frozen=True, kw_only=True)
87
+ class Structured(ZDType[np.dtypes.VoidDType[int], np.void], HasItemSize):
88
+ """
89
+ A Zarr data type for arrays containing structured scalars, AKA "record arrays".
90
+
91
+ Wraps the NumPy `np.dtypes.VoidDType` if the data type has fields. Scalars for this data
92
+ type are instances of `np.void`, with a ``fields`` attribute.
93
+
94
+ Attributes
95
+ ----------
96
+ fields : Sequence[tuple[str, ZDType]]
97
+ The fields of the structured dtype.
98
+
99
+ References
100
+ ----------
101
+ This data type does not have a Zarr V3 specification.
102
+
103
+ The Zarr V2 data type specification can be found [here](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v2/v2.0.rst#data-type-encoding).
104
+ """
105
+
106
+ _zarr_v3_name: ClassVar[Literal["structured"]] = "structured"
107
+ dtype_cls = np.dtypes.VoidDType # type: ignore[assignment]
108
+ fields: tuple[tuple[str, ZDType[TBaseDType, TBaseScalar]], ...]
109
+
110
+ def __post_init__(self) -> None:
111
+ if len(self.fields) < 1:
112
+ raise ValueError(f"must have at least one field. Got {self.fields!r}")
113
+
114
+ @classmethod
115
+ def _check_native_dtype(cls, dtype: TBaseDType) -> TypeGuard[np.dtypes.VoidDType[int]]:
116
+ """
117
+ Check that this dtype is a numpy structured dtype
118
+
119
+ Parameters
120
+ ----------
121
+ dtype : np.dtypes.DTypeLike
122
+ The dtype to check.
123
+
124
+ Returns
125
+ -------
126
+ TypeGuard[np.dtypes.VoidDType]
127
+ True if the dtype matches, False otherwise.
128
+ """
129
+ return isinstance(dtype, cls.dtype_cls) and dtype.fields is not None
130
+
131
+ @classmethod
132
+ def from_native_dtype(cls, dtype: TBaseDType) -> Self:
133
+ """
134
+ Create a Structured ZDType from a native NumPy data type.
135
+
136
+ Parameters
137
+ ----------
138
+ dtype : TBaseDType
139
+ The native data type.
140
+
141
+ Returns
142
+ -------
143
+ Self
144
+ An instance of this data type.
145
+
146
+ Raises
147
+ ------
148
+ DataTypeValidationError
149
+ If the input data type is not an instance of np.dtypes.VoidDType with a non-null
150
+ ``fields`` attribute.
151
+
152
+ Notes
153
+ -----
154
+ This method attempts to resolve the fields of the structured dtype using the data type
155
+ registry.
156
+ """
157
+ from zarr.core.dtype import get_data_type_from_native_dtype
158
+
159
+ fields: list[tuple[str, ZDType[TBaseDType, TBaseScalar]]] = []
160
+ if cls._check_native_dtype(dtype):
161
+ # fields of a structured numpy dtype are either 2-tuples or 3-tuples. we only
162
+ # care about the first element in either case.
163
+ for key, (dtype_instance, *_) in dtype.fields.items(): # type: ignore[union-attr]
164
+ dtype_wrapped = get_data_type_from_native_dtype(dtype_instance)
165
+ fields.append((key, dtype_wrapped))
166
+
167
+ return cls(fields=tuple(fields))
168
+ raise DataTypeValidationError(
169
+ f"Invalid data type: {dtype}. Expected an instance of {cls.dtype_cls}"
170
+ )
171
+
172
+ def to_native_dtype(self) -> np.dtypes.VoidDType[int]:
173
+ """
174
+ Convert the structured Zarr data type to a native NumPy void dtype.
175
+
176
+ This method constructs a NumPy dtype with fields corresponding to the
177
+ fields of the structured Zarr data type, by converting each field's
178
+ data type to its native dtype representation.
179
+
180
+ Returns
181
+ -------
182
+ np.dtypes.VoidDType[int]
183
+ The native NumPy void dtype representing the structured data type.
184
+ """
185
+
186
+ return cast(
187
+ "np.dtypes.VoidDType[int]",
188
+ np.dtype([(key, dtype.to_native_dtype()) for (key, dtype) in self.fields]),
189
+ )
190
+
191
+ @classmethod
192
+ def _check_json_v2(
193
+ cls,
194
+ data: DTypeJSON,
195
+ ) -> TypeGuard[StructuredJSON_V2]:
196
+ """
197
+ Check if the input is a valid JSON representation of a Structured data type
198
+ for Zarr V2.
199
+
200
+ The input data must be a mapping that contains a "name" key that is not a str,
201
+ and an "object_codec_id" key that is None.
202
+
203
+ Parameters
204
+ ----------
205
+ data : DTypeJSON
206
+ The JSON data to check.
207
+
208
+ Returns
209
+ -------
210
+ TypeGuard[StructuredJSON_V2]
211
+ True if the input is a valid JSON representation of a Structured data type
212
+ for Zarr V2, False otherwise.
213
+ """
214
+ return (
215
+ check_dtype_spec_v2(data)
216
+ and not isinstance(data["name"], str)
217
+ and check_structured_dtype_name_v2(data["name"])
218
+ and data["object_codec_id"] is None
219
+ )
220
+
221
+ @classmethod
222
+ def _check_json_v3(cls, data: DTypeJSON) -> TypeGuard[StructuredJSON_V3]:
223
+ """
224
+ Check that the input is a valid JSON representation of this class in Zarr V3.
225
+
226
+ Parameters
227
+ ----------
228
+ data : DTypeJSON
229
+ The JSON data to check.
230
+
231
+ Returns
232
+ -------
233
+ TypeGuard[StructuredJSON_V3]
234
+ True if the input is a valid JSON representation of a structured data type for Zarr V3,
235
+ False otherwise.
236
+ """
237
+
238
+ return (
239
+ isinstance(data, dict)
240
+ and set(data.keys()) == {"name", "configuration"}
241
+ and data["name"] == cls._zarr_v3_name
242
+ and isinstance(data["configuration"], dict)
243
+ and set(data["configuration"].keys()) == {"fields"}
244
+ )
245
+
246
+ @classmethod
247
+ def _from_json_v2(cls, data: DTypeJSON) -> Self:
248
+ # avoid circular import
249
+ from zarr.core.dtype import get_data_type_from_json
250
+
251
+ if cls._check_json_v2(data):
252
+ # structured dtypes are constructed directly from a list of lists
253
+ # note that we do not handle the object codec here! this will prevent structured
254
+ # dtypes from containing object dtypes.
255
+ return cls(
256
+ fields=tuple( # type: ignore[misc]
257
+ ( # type: ignore[misc]
258
+ f_name,
259
+ get_data_type_from_json(
260
+ {"name": f_dtype, "object_codec_id": None}, zarr_format=2
261
+ ),
262
+ )
263
+ for f_name, f_dtype in data["name"]
264
+ )
265
+ )
266
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected a JSON array of arrays"
267
+ raise DataTypeValidationError(msg)
268
+
269
+ @classmethod
270
+ def _from_json_v3(cls, data: DTypeJSON) -> Self:
271
+ # avoid circular import
272
+ from zarr.core.dtype import get_data_type_from_json
273
+
274
+ if cls._check_json_v3(data):
275
+ config = data["configuration"]
276
+ meta_fields = config["fields"]
277
+ return cls(
278
+ fields=tuple(
279
+ (f_name, get_data_type_from_json(f_dtype, zarr_format=3)) # type: ignore[misc]
280
+ for f_name, f_dtype in meta_fields
281
+ )
282
+ )
283
+ msg = f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected a JSON object with the key {cls._zarr_v3_name!r}"
284
+ raise DataTypeValidationError(msg)
285
+
286
+ @overload
287
+ def to_json(self, zarr_format: Literal[2]) -> StructuredJSON_V2: ...
288
+
289
+ @overload
290
+ def to_json(self, zarr_format: Literal[3]) -> StructuredJSON_V3: ...
291
+
292
+ def to_json(self, zarr_format: ZarrFormat) -> StructuredJSON_V2 | StructuredJSON_V3:
293
+ """
294
+ Convert the structured data type to a JSON-serializable form.
295
+
296
+ Parameters
297
+ ----------
298
+ zarr_format : ZarrFormat
299
+ The Zarr format version. Accepted values are 2 and 3.
300
+
301
+ Returns
302
+ -------
303
+ StructuredJSON_V2 | StructuredJSON_V3
304
+ The JSON representation of the structured data type.
305
+
306
+ Raises
307
+ ------
308
+ ValueError
309
+ If the zarr_format is not 2 or 3.
310
+ """
311
+ if zarr_format == 2:
312
+ fields = [
313
+ [f_name, f_dtype.to_json(zarr_format=zarr_format)["name"]]
314
+ for f_name, f_dtype in self.fields
315
+ ]
316
+ return {"name": fields, "object_codec_id": None}
317
+ elif zarr_format == 3:
318
+ v3_unstable_dtype_warning(self)
319
+ fields = [
320
+ [f_name, f_dtype.to_json(zarr_format=zarr_format)] # type: ignore[list-item]
321
+ for f_name, f_dtype in self.fields
322
+ ]
323
+ base_dict = {
324
+ "name": self._zarr_v3_name,
325
+ "configuration": {"fields": fields},
326
+ }
327
+ return cast("StructuredJSON_V3", base_dict)
328
+ raise ValueError(f"zarr_format must be 2 or 3, got {zarr_format}") # pragma: no cover
329
+
330
+ def _check_scalar(self, data: object) -> TypeGuard[StructuredScalarLike]:
331
+ # TODO: implement something more precise here!
332
+ """
333
+ Check that the input is a valid scalar value for this structured data type.
334
+
335
+ Parameters
336
+ ----------
337
+ data : object
338
+ The scalar value to check.
339
+
340
+ Returns
341
+ -------
342
+ TypeGuard[StructuredScalarLike]
343
+ Whether the input is a valid scalar value for this structured data type.
344
+ """
345
+ return isinstance(data, (bytes, list, tuple, int, np.void))
346
+
347
+ def _cast_scalar_unchecked(self, data: StructuredScalarLike) -> np.void:
348
+ """
349
+ Cast a python object to a numpy structured scalar without type checking.
350
+
351
+ Parameters
352
+ ----------
353
+ data : StructuredScalarLike
354
+ The data to cast.
355
+
356
+ Returns
357
+ -------
358
+ np.void
359
+ The casted data as a numpy structured scalar.
360
+
361
+ Notes
362
+ -----
363
+ This method does not perform any type checking.
364
+ The input data must be castable to a numpy structured scalar.
365
+
366
+ """
367
+ na_dtype = self.to_native_dtype()
368
+ if isinstance(data, bytes):
369
+ res = np.frombuffer(data, dtype=na_dtype)[0]
370
+ elif isinstance(data, list | tuple):
371
+ res = np.array([tuple(data)], dtype=na_dtype)[0]
372
+ else:
373
+ res = np.array([data], dtype=na_dtype)[0]
374
+ return cast("np.void", res)
375
+
376
+ def cast_scalar(self, data: object) -> np.void:
377
+ """
378
+ Cast a Python object to a NumPy structured scalar.
379
+
380
+ This function attempts to cast the provided data to a NumPy structured scalar.
381
+ If the data is compatible with the structured scalar type, it is cast without
382
+ type checking. Otherwise, a TypeError is raised.
383
+
384
+ Parameters
385
+ ----------
386
+ data : object
387
+ The data to be cast to a NumPy structured scalar.
388
+
389
+ Returns
390
+ -------
391
+ np.void
392
+ The data cast as a NumPy structured scalar.
393
+
394
+ Raises
395
+ ------
396
+ TypeError
397
+ If the data cannot be converted to a NumPy structured scalar.
398
+ """
399
+
400
+ if self._check_scalar(data):
401
+ return self._cast_scalar_unchecked(data)
402
+ msg = (
403
+ f"Cannot convert object {data!r} with type {type(data)} to a scalar compatible with the "
404
+ f"data type {self}."
405
+ )
406
+ raise TypeError(msg)
407
+
408
+ def default_scalar(self) -> np.void:
409
+ """
410
+ Get the default scalar value for this structured data type.
411
+
412
+ Returns
413
+ -------
414
+ np.void
415
+ The default scalar value, which is the scalar representation of 0
416
+ cast to this structured data type.
417
+ """
418
+
419
+ return self._cast_scalar_unchecked(0)
420
+
421
+ def from_json_scalar(self, data: JSON, *, zarr_format: ZarrFormat) -> np.void:
422
+ """
423
+ Read a JSON-serializable value as a NumPy structured scalar.
424
+
425
+ Parameters
426
+ ----------
427
+ data : JSON
428
+ The JSON-serializable value.
429
+ zarr_format : ZarrFormat
430
+ The zarr format version.
431
+
432
+ Returns
433
+ -------
434
+ np.void
435
+ The NumPy structured scalar.
436
+
437
+ Raises
438
+ ------
439
+ TypeError
440
+ If the input is not a base64-encoded string.
441
+ """
442
+ if check_json_str(data):
443
+ as_bytes = bytes_from_json(data, zarr_format=zarr_format)
444
+ dtype = self.to_native_dtype()
445
+ return cast("np.void", np.array([as_bytes]).view(dtype)[0])
446
+ raise TypeError(f"Invalid type: {data}. Expected a string.")
447
+
448
+ def to_json_scalar(self, data: object, *, zarr_format: ZarrFormat) -> str:
449
+ """
450
+ Convert a scalar to a JSON-serializable string representation.
451
+
452
+ Parameters
453
+ ----------
454
+ data : object
455
+ The scalar to convert.
456
+ zarr_format : ZarrFormat
457
+ The zarr format version.
458
+
459
+ Returns
460
+ -------
461
+ str
462
+ A string representation of the scalar, which is a base64-encoded
463
+ string of the bytes that make up the scalar.
464
+ """
465
+ return bytes_to_json(self.cast_scalar(data).tobytes(), zarr_format)
466
+
467
+ @property
468
+ def item_size(self) -> int:
469
+ """
470
+ The size of a single scalar in bytes.
471
+
472
+ Returns
473
+ -------
474
+ int
475
+ The size of a single scalar in bytes.
476
+ """
477
+ return self.to_native_dtype().itemsize
.venv/lib/python3.11/site-packages/zarr/core/dtype/npy/time.py ADDED
@@ -0,0 +1,885 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from datetime import datetime, timedelta
5
+ from typing import (
6
+ TYPE_CHECKING,
7
+ ClassVar,
8
+ Literal,
9
+ Self,
10
+ TypedDict,
11
+ TypeGuard,
12
+ TypeVar,
13
+ cast,
14
+ get_args,
15
+ overload,
16
+ )
17
+
18
+ import numpy as np
19
+ from typing_extensions import ReadOnly
20
+
21
+ from zarr.core.common import NamedConfig
22
+ from zarr.core.dtype.common import (
23
+ DataTypeValidationError,
24
+ DTypeConfig_V2,
25
+ DTypeJSON,
26
+ HasEndianness,
27
+ HasItemSize,
28
+ check_dtype_spec_v2,
29
+ )
30
+ from zarr.core.dtype.npy.common import (
31
+ DATETIME_UNIT,
32
+ DateTimeUnit,
33
+ check_json_int,
34
+ endianness_to_numpy_str,
35
+ get_endianness_from_numpy_dtype,
36
+ )
37
+ from zarr.core.dtype.wrapper import TBaseDType, ZDType
38
+
39
+ if TYPE_CHECKING:
40
+ from zarr.core.common import JSON, ZarrFormat
41
+
42
+ TimeDeltaLike = str | int | bytes | np.timedelta64 | timedelta | None
43
+ DateTimeLike = str | int | bytes | np.datetime64 | datetime | None
44
+
45
+
46
+ def datetime_from_int(data: int, *, unit: DateTimeUnit, scale_factor: int) -> np.datetime64:
47
+ """
48
+ Convert an integer to a datetime64.
49
+
50
+ Parameters
51
+ ----------
52
+ data : int
53
+ The integer to convert.
54
+ unit : DateTimeUnit
55
+ The unit of the datetime64.
56
+ scale_factor : int
57
+ The scale factor of the datetime64.
58
+
59
+ Returns
60
+ -------
61
+ numpy.datetime64
62
+ The datetime64 value.
63
+ """
64
+ dtype_name = f"datetime64[{scale_factor}{unit}]"
65
+ return cast("np.datetime64", np.int64(data).view(dtype_name))
66
+
67
+
68
+ def datetimelike_to_int(data: np.datetime64 | np.timedelta64) -> int:
69
+ """
70
+ Convert a datetime64 or a timedelta64 to an integer.
71
+
72
+ Parameters
73
+ ----------
74
+ data : np.datetime64 | numpy.timedelta64
75
+ The value to convert.
76
+
77
+ Returns
78
+ -------
79
+ int
80
+ An integer representation of the scalar.
81
+ """
82
+ return data.view(np.int64).item()
83
+
84
+
85
+ def check_json_time(data: JSON) -> TypeGuard[Literal["NaT"] | int]:
86
+ """
87
+ Type guard to check if the input JSON data is the literal string "NaT"
88
+ or an integer.
89
+ """
90
+ return check_json_int(data) or data == "NaT"
91
+
92
+
93
+ BaseTimeDType_co = TypeVar(
94
+ "BaseTimeDType_co",
95
+ bound=np.dtypes.TimeDelta64DType | np.dtypes.DateTime64DType,
96
+ covariant=True,
97
+ )
98
+ BaseTimeScalar_co = TypeVar(
99
+ "BaseTimeScalar_co", bound=np.timedelta64 | np.datetime64, covariant=True
100
+ )
101
+
102
+
103
+ class TimeConfig(TypedDict):
104
+ """
105
+ The configuration for the numpy.timedelta64 or numpy.datetime64 data type in Zarr V3.
106
+
107
+ Attributes
108
+ ----------
109
+ unit : ReadOnly[DateTimeUnit]
110
+ A string encoding a unit of time.
111
+ scale_factor : ReadOnly[int]
112
+ A scale factor.
113
+
114
+ Examples
115
+ --------
116
+ ```python
117
+ {"unit": "ms", "scale_factor": 1}
118
+ ```
119
+ """
120
+
121
+ unit: ReadOnly[DateTimeUnit]
122
+ scale_factor: ReadOnly[int]
123
+
124
+
125
+ class DateTime64JSON_V3(NamedConfig[Literal["numpy.datetime64"], TimeConfig]):
126
+ """
127
+ The JSON representation of the ``numpy.datetime64`` data type in Zarr V3.
128
+
129
+ References
130
+ ----------
131
+ This representation is defined in the ``numpy.datetime64``
132
+ [specification document](https://zarr-specs.readthedocs.io/en/latest/spec/v3/datatypes.html#numpy-datetime64).
133
+
134
+ Examples
135
+ --------
136
+ ```python
137
+ {
138
+ "name": "numpy.datetime64",
139
+ "configuration": {
140
+ "unit": "ms",
141
+ "scale_factor": 1
142
+ }
143
+ }
144
+ ```
145
+ """
146
+
147
+
148
+ class TimeDelta64JSON_V3(NamedConfig[Literal["numpy.timedelta64"], TimeConfig]):
149
+ """
150
+ The JSON representation of the ``TimeDelta64`` data type in Zarr V3.
151
+
152
+ References
153
+ ----------
154
+ This representation is defined in the numpy.timedelta64
155
+ [specification document](https://zarr-specs.readthedocs.io/en/latest/spec/v3/datatypes.html#numpy-timedelta64).
156
+
157
+ Examples
158
+ --------
159
+ ```python
160
+ {
161
+ "name": "numpy.timedelta64",
162
+ "configuration": {
163
+ "unit": "ms",
164
+ "scale_factor": 1
165
+ }
166
+ }
167
+ ```
168
+ """
169
+
170
+
171
+ class TimeDelta64JSON_V2(DTypeConfig_V2[str, None]):
172
+ """
173
+ A wrapper around the JSON representation of the ``TimeDelta64`` data type in Zarr V2.
174
+
175
+ The ``name`` field of this class contains the value that would appear under the
176
+ ``dtype`` field in Zarr V2 array metadata.
177
+
178
+ References
179
+ ----------
180
+ The structure of the ``name`` field is defined in the Zarr V2
181
+ [specification document](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v2/v2.0.rst#data-type-encoding).
182
+
183
+
184
+ Examples
185
+ --------
186
+ ```python
187
+ {
188
+ "name": "<m8[1s]",
189
+ "object_codec_id": None
190
+ }
191
+ ```
192
+ """
193
+
194
+
195
+ class DateTime64JSON_V2(DTypeConfig_V2[str, None]):
196
+ """
197
+ A wrapper around the JSON representation of the ``DateTime64`` data type in Zarr V2.
198
+
199
+ The ``name`` field of this class contains the value that would appear under the
200
+ ``dtype`` field in Zarr V2 array metadata.
201
+
202
+ References
203
+ ----------
204
+ The structure of the ``name`` field is defined in the Zarr V2
205
+ [specification document](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v2/v2.0.rst#data-type-encoding).
206
+
207
+
208
+ Examples
209
+ --------
210
+ ```python
211
+ {
212
+ "name": "<M8[10s]",
213
+ "object_codec_id": None
214
+ }
215
+ ```
216
+ """
217
+
218
+
219
+ @dataclass(frozen=True, kw_only=True, slots=True)
220
+ class TimeDTypeBase(ZDType[BaseTimeDType_co, BaseTimeScalar_co], HasEndianness, HasItemSize):
221
+ """
222
+ A base class for data types that represent time via the NumPy TimeDelta64 and DateTime64 data
223
+ types.
224
+
225
+ Attributes
226
+ ----------
227
+ scale_factor : int
228
+ The scale factor for the time unit.
229
+ unit : str
230
+ The unit of time.
231
+ """
232
+
233
+ _numpy_name: ClassVar[Literal["datetime64", "timedelta64"]]
234
+ scale_factor: int
235
+ unit: DateTimeUnit
236
+
237
+ def __post_init__(self) -> None:
238
+ if self.scale_factor < 1:
239
+ raise ValueError(f"scale_factor must be > 0, got {self.scale_factor}.")
240
+ if self.scale_factor >= 2**31:
241
+ raise ValueError(f"scale_factor must be < 2147483648, got {self.scale_factor}.")
242
+ if self.unit not in get_args(DateTimeUnit):
243
+ raise ValueError(f"unit must be one of {get_args(DateTimeUnit)}, got {self.unit!r}.")
244
+
245
+ @classmethod
246
+ def from_native_dtype(cls, dtype: TBaseDType) -> Self:
247
+ """
248
+ Create an instance of this class from a native NumPy data type.
249
+
250
+ Parameters
251
+ ----------
252
+ dtype : TBaseDType
253
+ The native NumPy dtype to convert.
254
+
255
+ Returns
256
+ -------
257
+ Self
258
+ An instance of this data type.
259
+
260
+ Raises
261
+ ------
262
+ DataTypeValidationError
263
+ If the dtype is not a valid representation of this class.
264
+ """
265
+
266
+ if cls._check_native_dtype(dtype):
267
+ unit, scale_factor = np.datetime_data(dtype.name)
268
+ unit = cast("DateTimeUnit", unit)
269
+ return cls(
270
+ unit=unit,
271
+ scale_factor=scale_factor,
272
+ endianness=get_endianness_from_numpy_dtype(dtype),
273
+ )
274
+ raise DataTypeValidationError(
275
+ f"Invalid data type: {dtype}. Expected an instance of {cls.dtype_cls}"
276
+ )
277
+
278
+ def to_native_dtype(self) -> BaseTimeDType_co:
279
+ # Numpy does not allow creating datetime64 or timedelta64 via
280
+ # np.dtypes.{dtype_name}()
281
+ # so we use np.dtype with a formatted string.
282
+ """
283
+ Convert this data type to a NumPy temporal data type with the appropriate
284
+ unit and scale factor.
285
+
286
+ Returns
287
+ -------
288
+ BaseTimeDType_co
289
+ A NumPy data type object representing the time data type with
290
+ the specified unit, scale factor, and byte order.
291
+ """
292
+
293
+ dtype_string = f"{self._numpy_name}[{self.scale_factor}{self.unit}]"
294
+ return np.dtype(dtype_string).newbyteorder(endianness_to_numpy_str(self.endianness)) # type: ignore[return-value]
295
+
296
+ def to_json_scalar(self, data: object, *, zarr_format: ZarrFormat) -> int:
297
+ """
298
+ Convert a python object to a JSON representation of a datetime64 or timedelta64 scalar.
299
+
300
+ Parameters
301
+ ----------
302
+ data : object
303
+ The python object to convert.
304
+ zarr_format : ZarrFormat
305
+ The Zarr format version (2 or 3).
306
+
307
+ Returns
308
+ -------
309
+ int
310
+ The JSON representation of the scalar.
311
+ """
312
+ return datetimelike_to_int(data) # type: ignore[arg-type]
313
+
314
+ @property
315
+ def item_size(self) -> int:
316
+ """
317
+ The size of a single scalar in bytes.
318
+
319
+ Returns
320
+ -------
321
+ int
322
+ The size of a single scalar in bytes.
323
+ """
324
+ return 8
325
+
326
+
327
+ @dataclass(frozen=True, kw_only=True, slots=True)
328
+ class TimeDelta64(TimeDTypeBase[np.dtypes.TimeDelta64DType, np.timedelta64], HasEndianness):
329
+ """
330
+ A Zarr data type for arrays containing NumPy TimeDelta64 data.
331
+
332
+ Wraps the ``np.dtypesTimeDelta64DType`` data type. Scalars for this data type
333
+ are instances of `np.timedelta64`.
334
+
335
+ Attributes
336
+ ----------
337
+ dtype_cls : Type[np.dtypesTimeDelta64DType]
338
+ The NumPy dtype class for this data type.
339
+ scale_factor : int
340
+ The scale factor for this data type.
341
+ unit : DateTimeUnit
342
+ The unit for this data type.
343
+
344
+ References
345
+ ----------
346
+ The Zarr V2 representation of this data type is defined in the Zarr V2
347
+ [specification document](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v2/v2.0.rst#data-type-encoding).
348
+
349
+ The Zarr V3 representation of this data type is defined in the ``numpy.timedelta64``
350
+ [specification document](https://github.com/zarr-developers/zarr-extensions/tree/main/data-types/numpy.timedelta64)
351
+ """
352
+
353
+ # mypy infers the type of np.dtypes.TimeDelta64DType to be
354
+ # "Callable[[Literal['Y', 'M', 'W', 'D'] | Literal['h', 'm', 's', 'ms', 'us', 'ns', 'ps', 'fs', 'as']], Never]"
355
+ dtype_cls = np.dtypes.TimeDelta64DType # type: ignore[assignment]
356
+ unit: DateTimeUnit = "generic"
357
+ scale_factor: int = 1
358
+ _zarr_v3_name: ClassVar[Literal["numpy.timedelta64"]] = "numpy.timedelta64"
359
+ _zarr_v2_names: ClassVar[tuple[Literal[">m8"], Literal["<m8"]]] = (">m8", "<m8")
360
+ _numpy_name: ClassVar[Literal["timedelta64"]] = "timedelta64"
361
+
362
+ @classmethod
363
+ def _check_json_v2(cls, data: DTypeJSON) -> TypeGuard[TimeDelta64JSON_V2]:
364
+ """
365
+ Validate that the provided JSON input accurately represents a NumPy timedelta64 data type,
366
+ which could be in the form of strings like "<m8" or ">m8[10s]". This method serves as a type
367
+ guard, helping to refine the type of unknown JSON input by confirming its adherence to the
368
+ expected format for NumPy timedelta64 data types.
369
+
370
+ The JSON input should contain a "name" key with a value that matches the expected string
371
+ pattern for NumPy timedelta64 data types. The pattern includes an optional unit enclosed
372
+ within square brackets, following the base type identifier.
373
+
374
+ Returns
375
+ -------
376
+ bool
377
+ True if the JSON input is a valid representation of this class,
378
+ otherwise False.
379
+ """
380
+ if not check_dtype_spec_v2(data):
381
+ return False
382
+ name = data["name"]
383
+ # match <m[ns], >m[M], etc
384
+ # consider making this a standalone function
385
+ if not isinstance(name, str):
386
+ return False
387
+ if not name.startswith(cls._zarr_v2_names):
388
+ return False
389
+ if len(name) == 3:
390
+ # no unit, and
391
+ # we already checked that this string is either <m8 or >m8
392
+ return True
393
+ else:
394
+ return name[4:-1].endswith(DATETIME_UNIT) and name[-1] == "]"
395
+
396
+ @classmethod
397
+ def _check_json_v3(cls, data: DTypeJSON) -> TypeGuard[DateTime64JSON_V3]:
398
+ """
399
+ Check that the input is a valid JSON representation of this class in Zarr V3.
400
+
401
+ Returns
402
+ -------
403
+ TypeGuard[DateTime64JSON_V3]
404
+ True if the JSON input is a valid representation of this class,
405
+ otherwise False.
406
+ """
407
+ return (
408
+ isinstance(data, dict)
409
+ and set(data.keys()) == {"name", "configuration"}
410
+ and data["name"] == cls._zarr_v3_name
411
+ and isinstance(data["configuration"], dict)
412
+ and set(data["configuration"].keys()) == {"unit", "scale_factor"}
413
+ )
414
+
415
+ @classmethod
416
+ def _from_json_v2(cls, data: DTypeJSON) -> Self:
417
+ """
418
+ Create a TimeDelta64 from a Zarr V2-flavored JSON.
419
+
420
+ Parameters
421
+ ----------
422
+ data : DTypeJSON
423
+ The JSON data.
424
+
425
+ Returns
426
+ -------
427
+ TimeDelta64
428
+ An instance of TimeDelta64.
429
+
430
+ Raises
431
+ ------
432
+ DataTypeValidationError
433
+ If the input JSON is not a valid representation of this class.
434
+ """
435
+ if cls._check_json_v2(data):
436
+ name = data["name"]
437
+ return cls.from_native_dtype(np.dtype(name))
438
+ msg = (
439
+ f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected a string "
440
+ f"representation of an instance of {cls.dtype_cls}"
441
+ )
442
+ raise DataTypeValidationError(msg)
443
+
444
+ @classmethod
445
+ def _from_json_v3(cls, data: DTypeJSON) -> Self:
446
+ """
447
+ Create a TimeDelta64 from a Zarr V3-flavored JSON.
448
+
449
+ The JSON representation of a TimeDelta64 in Zarr V3 is a dict with a 'name' key
450
+ with the value 'numpy.timedelta64', and a 'configuration' key with a value of a dict
451
+ with a 'unit' key and a 'scale_factor' key.
452
+
453
+ For example:
454
+
455
+ ```json
456
+ {
457
+ "name": "numpy.timedelta64",
458
+ "configuration": {
459
+ "unit": "generic",
460
+ "scale_factor": 1
461
+ }
462
+ }
463
+ ```
464
+
465
+ """
466
+ if cls._check_json_v3(data):
467
+ unit = data["configuration"]["unit"]
468
+ scale_factor = data["configuration"]["scale_factor"]
469
+ return cls(unit=unit, scale_factor=scale_factor)
470
+ msg = (
471
+ f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected a dict "
472
+ f"with a 'name' key with the value 'numpy.timedelta64', "
473
+ "and a 'configuration' key with a value of a dict with a 'unit' key and a "
474
+ "'scale_factor' key"
475
+ )
476
+ raise DataTypeValidationError(msg)
477
+
478
+ @overload
479
+ def to_json(self, zarr_format: Literal[2]) -> TimeDelta64JSON_V2: ...
480
+ @overload
481
+ def to_json(self, zarr_format: Literal[3]) -> TimeDelta64JSON_V3: ...
482
+
483
+ def to_json(self, zarr_format: ZarrFormat) -> TimeDelta64JSON_V2 | TimeDelta64JSON_V3:
484
+ """
485
+ Serialize this data type to JSON.
486
+
487
+ Parameters
488
+ ----------
489
+ zarr_format : ZarrFormat
490
+ The Zarr format version (2 or 3).
491
+
492
+ Returns
493
+ -------
494
+ TimeDelta64JSON_V2 | TimeDelta64JSON_V3
495
+ The JSON representation of the data type.
496
+
497
+ Raises
498
+ ------
499
+ ValueError
500
+ If the zarr_format is not 2 or 3.
501
+ """
502
+ if zarr_format == 2:
503
+ name = self.to_native_dtype().str
504
+ return {"name": name, "object_codec_id": None}
505
+ elif zarr_format == 3:
506
+ return {
507
+ "name": self._zarr_v3_name,
508
+ "configuration": {"unit": self.unit, "scale_factor": self.scale_factor},
509
+ }
510
+ raise ValueError(f"zarr_format must be 2 or 3, got {zarr_format}") # pragma: no cover
511
+
512
+ def _check_scalar(self, data: object) -> TypeGuard[TimeDeltaLike]:
513
+ """
514
+ Check if the input is a scalar of this data type.
515
+
516
+ Parameters
517
+ ----------
518
+ data : object
519
+ The object to check.
520
+
521
+ Returns
522
+ -------
523
+ TypeGuard[TimeDeltaLike]
524
+ True if the input is a scalar of this data type, False otherwise.
525
+ """
526
+ if data is None:
527
+ return True
528
+ return isinstance(data, str | int | bytes | np.timedelta64 | timedelta)
529
+
530
+ def _cast_scalar_unchecked(self, data: TimeDeltaLike) -> np.timedelta64:
531
+ """
532
+ Cast the provided scalar input to a numpy timedelta64 without any type checking.
533
+
534
+ This method assumes that the input data is already a valid scalar of this data type,
535
+ and does not perform any validation or type checks. It directly casts the input
536
+ to a numpy timedelta64 scalar using the unit and scale factor defined in the class.
537
+
538
+ Parameters
539
+ ----------
540
+ data : TimeDeltaLike
541
+ The scalar input data to cast.
542
+
543
+ Returns
544
+ -------
545
+ numpy.timedelta64
546
+ The input data cast as a numpy timedelta64 scalar.
547
+ """
548
+ return self.to_native_dtype().type(data, f"{self.scale_factor}{self.unit}")
549
+
550
+ def cast_scalar(self, data: object) -> np.timedelta64:
551
+ """
552
+ Cast the input to a numpy timedelta64 scalar. If the input is not a scalar of this data type,
553
+ raise a TypeError.
554
+ """
555
+ if self._check_scalar(data):
556
+ return self._cast_scalar_unchecked(data)
557
+ msg = (
558
+ f"Cannot convert object {data!r} with type {type(data)} to a scalar compatible with the "
559
+ f"data type {self}."
560
+ )
561
+ raise TypeError(msg)
562
+
563
+ def default_scalar(self) -> np.timedelta64:
564
+ """
565
+ Return a default scalar of this data type.
566
+
567
+ This method provides a default value for the timedelta64 scalar, which is
568
+ a 'Not-a-Time' (NaT) value.
569
+ """
570
+ return np.timedelta64("NaT")
571
+
572
+ def from_json_scalar(self, data: JSON, *, zarr_format: ZarrFormat) -> np.timedelta64:
573
+ """
574
+ Create a scalar of this data type from JSON input.
575
+
576
+ Parameters
577
+ ----------
578
+ data : JSON
579
+ The JSON representation of the scalar value.
580
+ zarr_format : int
581
+ The zarr format to use for the JSON representation.
582
+
583
+ Returns
584
+ -------
585
+ numpy.timedelta64
586
+ The scalar value of this data type.
587
+
588
+ Raises
589
+ ------
590
+ TypeError
591
+ If the input JSON is not a valid representation of a scalar for this data type.
592
+ """
593
+ if check_json_time(data):
594
+ return self.to_native_dtype().type(data, f"{self.scale_factor}{self.unit}")
595
+ raise TypeError(f"Invalid type: {data}. Expected an integer.") # pragma: no cover
596
+
597
+
598
+ @dataclass(frozen=True, kw_only=True, slots=True)
599
+ class DateTime64(TimeDTypeBase[np.dtypes.DateTime64DType, np.datetime64], HasEndianness):
600
+ """
601
+ A Zarr data type for arrays containing NumPy Datetime64 data.
602
+
603
+ Wraps the ``np.dtypes.TimeDelta64DType`` data type. Scalars for this data type
604
+ are instances of ``np.datetime64``.
605
+
606
+ Attributes
607
+ ----------
608
+ dtype_cls : Type[np.dtypesTimeDelta64DType]
609
+ The numpy dtype class for this data type.
610
+ unit : DateTimeUnit
611
+ The unit of time for this data type.
612
+ scale_factor : int
613
+ The scale factor for the time unit.
614
+
615
+ References
616
+ ----------
617
+ The Zarr V2 representation of this data type is defined in the Zarr V2
618
+ [specification document](https://github.com/zarr-developers/zarr-specs/blob/main/docs/v2/v2.0.rst#data-type-encoding).
619
+
620
+ The Zarr V3 representation of this data type is defined in the ``numpy.datetime64``
621
+ [specification document](https://github.com/zarr-developers/zarr-extensions/tree/main/data-types/numpy.datetime64)
622
+ """
623
+
624
+ dtype_cls = np.dtypes.DateTime64DType # type: ignore[assignment]
625
+ _zarr_v3_name: ClassVar[Literal["numpy.datetime64"]] = "numpy.datetime64"
626
+ _zarr_v2_names: ClassVar[tuple[Literal[">M8"], Literal["<M8"]]] = (">M8", "<M8")
627
+ _numpy_name: ClassVar[Literal["datetime64"]] = "datetime64"
628
+ unit: DateTimeUnit = "generic"
629
+ scale_factor: int = 1
630
+
631
+ @classmethod
632
+ def _check_json_v2(cls, data: DTypeJSON) -> TypeGuard[DateTime64JSON_V2]:
633
+ """
634
+ Check that the input is a valid JSON representation of this data type.
635
+
636
+ Parameters
637
+ ----------
638
+ data : DTypeJSON
639
+ The JSON data to check.
640
+
641
+ Returns
642
+ -------
643
+ TypeGuard[DateTime64JSON_V2]
644
+ True if the input is a valid JSON representation of a NumPy datetime64 data type,
645
+ otherwise False.
646
+ """
647
+ if not check_dtype_spec_v2(data):
648
+ return False
649
+ name = data["name"]
650
+ if not isinstance(name, str):
651
+ return False
652
+ if not name.startswith(cls._zarr_v2_names):
653
+ return False
654
+ if len(name) == 3:
655
+ # no unit, and
656
+ # we already checked that this string is either <M8 or >M8
657
+ return True
658
+ else:
659
+ return name[4:-1].endswith(DATETIME_UNIT) and name[-1] == "]"
660
+
661
+ @classmethod
662
+ def _check_json_v3(cls, data: DTypeJSON) -> TypeGuard[DateTime64JSON_V3]:
663
+ """
664
+ Check that the input is a valid JSON representation of this class in Zarr V3.
665
+
666
+ Parameters
667
+ ----------
668
+ data : DTypeJSON
669
+ The JSON data to check.
670
+
671
+ Returns
672
+ -------
673
+ TypeGuard[DateTime64JSON_V3]
674
+ True if the input is a valid JSON representation of a numpy datetime64 data type in Zarr V3, False otherwise.
675
+ """
676
+
677
+ return (
678
+ isinstance(data, dict)
679
+ and set(data.keys()) == {"name", "configuration"}
680
+ and data["name"] == cls._zarr_v3_name
681
+ and isinstance(data["configuration"], dict)
682
+ and set(data["configuration"].keys()) == {"unit", "scale_factor"}
683
+ )
684
+
685
+ @classmethod
686
+ def _from_json_v2(cls, data: DTypeJSON) -> Self:
687
+ """
688
+ Create an instance of this data type from a Zarr V2-flavored JSON representation.
689
+
690
+ This method checks if the provided JSON data is a valid representation of this class.
691
+ If valid, it creates an instance using the native NumPy dtype. Otherwise, it raises a
692
+ DataTypeValidationError.
693
+
694
+ Parameters
695
+ ----------
696
+ data : DTypeJSON
697
+ The JSON data to parse.
698
+
699
+ Returns
700
+ -------
701
+ Self
702
+ An instance of this data type.
703
+
704
+ Raises
705
+ ------
706
+ DataTypeValidationError
707
+ If the input JSON is not a valid representation of this class.
708
+ """
709
+
710
+ if cls._check_json_v2(data):
711
+ name = data["name"]
712
+ return cls.from_native_dtype(np.dtype(name))
713
+ msg = (
714
+ f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected a string "
715
+ f"representation of an instance of {cls.dtype_cls}"
716
+ )
717
+ raise DataTypeValidationError(msg)
718
+
719
+ @classmethod
720
+ def _from_json_v3(cls, data: DTypeJSON) -> Self:
721
+ """
722
+ Create an instance of this data type from a Zarr V3-flavored JSON representation.
723
+
724
+ This method checks if the provided JSON data is a valid representation of this class.
725
+ If valid, it creates an instance using the native NumPy dtype. Otherwise, it raises a
726
+ DataTypeValidationError.
727
+
728
+ Parameters
729
+ ----------
730
+ data : DTypeJSON
731
+ The JSON data to parse.
732
+
733
+ Returns
734
+ -------
735
+ Self
736
+ An instance of this data type.
737
+
738
+ Raises
739
+ ------
740
+ DataTypeValidationError
741
+ If the input JSON is not a valid representation of this class.
742
+ """
743
+ if cls._check_json_v3(data):
744
+ unit = data["configuration"]["unit"]
745
+ scale_factor = data["configuration"]["scale_factor"]
746
+ return cls(unit=unit, scale_factor=scale_factor)
747
+ msg = (
748
+ f"Invalid JSON representation of {cls.__name__}. Got {data!r}, expected a dict "
749
+ f"with a 'name' key with the value 'numpy.datetime64', "
750
+ "and a 'configuration' key with a value of a dict with a 'unit' key and a "
751
+ "'scale_factor' key"
752
+ )
753
+ raise DataTypeValidationError(msg)
754
+
755
+ @overload
756
+ def to_json(self, zarr_format: Literal[2]) -> DateTime64JSON_V2: ...
757
+ @overload
758
+ def to_json(self, zarr_format: Literal[3]) -> DateTime64JSON_V3: ...
759
+
760
+ def to_json(self, zarr_format: ZarrFormat) -> DateTime64JSON_V2 | DateTime64JSON_V3:
761
+ """
762
+ Serialize this data type to JSON.
763
+
764
+ Parameters
765
+ ----------
766
+ zarr_format : ZarrFormat
767
+ The Zarr format version (2 or 3).
768
+
769
+ Returns
770
+ -------
771
+ DateTime64JSON_V2 | DateTime64JSON_V3
772
+ The JSON representation of the data type.
773
+
774
+ Raises
775
+ ------
776
+ ValueError
777
+ If the zarr_format is not 2 or 3.
778
+ """
779
+ if zarr_format == 2:
780
+ name = self.to_native_dtype().str
781
+ return {"name": name, "object_codec_id": None}
782
+ elif zarr_format == 3:
783
+ return {
784
+ "name": self._zarr_v3_name,
785
+ "configuration": {"unit": self.unit, "scale_factor": self.scale_factor},
786
+ }
787
+ raise ValueError(f"zarr_format must be 2 or 3, got {zarr_format}") # pragma: no cover
788
+
789
+ def _check_scalar(self, data: object) -> TypeGuard[DateTimeLike]:
790
+ """
791
+ Check if the input is convertible to a scalar of this data type.
792
+
793
+ Parameters
794
+ ----------
795
+ data : object
796
+ The object to check.
797
+
798
+ Returns
799
+ -------
800
+ TypeGuard[DateTimeLike]
801
+ True if the input is a scalar of this data type, False otherwise.
802
+ """
803
+ if data is None:
804
+ return True
805
+ return isinstance(data, str | int | bytes | np.datetime64 | datetime)
806
+
807
+ def _cast_scalar_unchecked(self, data: DateTimeLike) -> np.datetime64:
808
+ """
809
+ Cast the input to a scalar of this data type without any type checking.
810
+
811
+ Parameters
812
+ ----------
813
+ data : DateTimeLike
814
+ The scalar data to cast.
815
+
816
+ Returns
817
+ -------
818
+ numpy.datetime64
819
+ The input cast to a NumPy datetime scalar.
820
+ """
821
+ return self.to_native_dtype().type(data, f"{self.scale_factor}{self.unit}")
822
+
823
+ def cast_scalar(self, data: object) -> np.datetime64:
824
+ """
825
+ Cast the input to a scalar of this data type after a type check.
826
+
827
+ Parameters
828
+ ----------
829
+ data : object
830
+ The scalar value to cast.
831
+
832
+ Returns
833
+ -------
834
+ numpy.datetime64
835
+ The input cast to a NumPy datetime scalar.
836
+
837
+ Raises
838
+ ------
839
+ TypeError
840
+ If the data cannot be converted to a numpy datetime scalar.
841
+ """
842
+ if self._check_scalar(data):
843
+ return self._cast_scalar_unchecked(data)
844
+ msg = (
845
+ f"Cannot convert object {data!r} with type {type(data)} to a scalar compatible with the "
846
+ f"data type {self}."
847
+ )
848
+ raise TypeError(msg)
849
+
850
+ def default_scalar(self) -> np.datetime64:
851
+ """
852
+ Return the default scalar value for this data type.
853
+
854
+ Returns
855
+ -------
856
+ numpy.datetime64
857
+ The default scalar value, which is a 'Not-a-Time' (NaT) value
858
+ """
859
+
860
+ return np.datetime64("NaT")
861
+
862
+ def from_json_scalar(self, data: JSON, *, zarr_format: ZarrFormat) -> np.datetime64:
863
+ """
864
+ Read a JSON-serializable value as a scalar.
865
+
866
+ Parameters
867
+ ----------
868
+ data : JSON
869
+ The JSON-serializable value.
870
+ zarr_format : ZarrFormat
871
+ The zarr format version.
872
+
873
+ Returns
874
+ -------
875
+ numpy.datetime64
876
+ The numpy datetime scalar.
877
+
878
+ Raises
879
+ ------
880
+ TypeError
881
+ If the input is not a valid integer type.
882
+ """
883
+ if check_json_time(data):
884
+ return self._cast_scalar_unchecked(data)
885
+ raise TypeError(f"Invalid type: {data}. Expected an integer.") # pragma: no cover
.venv/lib/python3.11/site-packages/zarr/core/dtype/registry.py ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import contextlib
4
+ from dataclasses import dataclass, field
5
+ from typing import TYPE_CHECKING, Self
6
+
7
+ import numpy as np
8
+
9
+ from zarr.core.dtype.common import (
10
+ DataTypeValidationError,
11
+ DTypeJSON,
12
+ )
13
+
14
+ if TYPE_CHECKING:
15
+ from importlib.metadata import EntryPoint
16
+
17
+ from zarr.core.common import ZarrFormat
18
+ from zarr.core.dtype.wrapper import TBaseDType, TBaseScalar, ZDType
19
+
20
+
21
+ # This class is different from the other registry classes, which inherit from
22
+ # dict. IMO it's simpler to just do a dataclass. But long-term we should
23
+ # have just 1 registry class in use.
24
+ @dataclass(frozen=True, kw_only=True)
25
+ class DataTypeRegistry:
26
+ """
27
+ A registry for ZDType classes.
28
+
29
+ This registry is a mapping from Zarr data type names to their
30
+ corresponding ZDType classes.
31
+
32
+ Attributes
33
+ ----------
34
+ contents : dict[str, type[ZDType[TBaseDType, TBaseScalar]]]
35
+ The mapping from Zarr data type names to their corresponding
36
+ ZDType classes.
37
+ """
38
+
39
+ contents: dict[str, type[ZDType[TBaseDType, TBaseScalar]]] = field(
40
+ default_factory=dict, init=False
41
+ )
42
+
43
+ _lazy_load_list: list[EntryPoint] = field(default_factory=list, init=False)
44
+
45
+ def _lazy_load(self) -> None:
46
+ """
47
+ Load all data types from the lazy load list and register them with
48
+ the registry. After loading, clear the lazy load list.
49
+ """
50
+ for e in self._lazy_load_list:
51
+ self.register(e.load()._zarr_v3_name, e.load())
52
+
53
+ self._lazy_load_list.clear()
54
+
55
+ def register(self: Self, key: str, cls: type[ZDType[TBaseDType, TBaseScalar]]) -> None:
56
+ """
57
+ Register a data type with the registry.
58
+
59
+ Parameters
60
+ ----------
61
+ key : str
62
+ The Zarr V3 name of the data type.
63
+ cls : type[ZDType[TBaseDType, TBaseScalar]]
64
+ The class of the data type to register.
65
+
66
+ Notes
67
+ -----
68
+ This method is idempotent. If the data type is already registered, this
69
+ method does nothing.
70
+ """
71
+ if key not in self.contents or self.contents[key] != cls:
72
+ self.contents[key] = cls
73
+
74
+ def unregister(self, key: str) -> None:
75
+ """
76
+ Unregister a data type from the registry.
77
+
78
+ Parameters
79
+ ----------
80
+ key : str
81
+ The key associated with the ZDType class to be unregistered.
82
+
83
+ Returns
84
+ -------
85
+ None
86
+
87
+ Raises
88
+ ------
89
+ KeyError
90
+ If the data type is not found in the registry.
91
+ """
92
+ if key in self.contents:
93
+ del self.contents[key]
94
+ else:
95
+ raise KeyError(f"Data type '{key}' not found in registry.")
96
+
97
+ def get(self, key: str) -> type[ZDType[TBaseDType, TBaseScalar]]:
98
+ """
99
+ Retrieve a registered ZDType class by its key.
100
+
101
+ Parameters
102
+ ----------
103
+ key : str
104
+ The key associated with the desired ZDType class.
105
+
106
+ Returns
107
+ -------
108
+ type[ZDType[TBaseDType, TBaseScalar]]
109
+ The ZDType class registered under the given key.
110
+
111
+ Raises
112
+ ------
113
+ KeyError
114
+ If the key is not found in the registry.
115
+ """
116
+
117
+ return self.contents[key]
118
+
119
+ def match_dtype(self, dtype: TBaseDType) -> ZDType[TBaseDType, TBaseScalar]:
120
+ """
121
+ Match a native data type, e.g. a NumPy data type, to a registered ZDType.
122
+
123
+ Parameters
124
+ ----------
125
+ dtype : TBaseDType
126
+ The native data type to match.
127
+
128
+ Returns
129
+ -------
130
+ ZDType[TBaseDType, TBaseScalar]
131
+ The matched ZDType corresponding to the provided NumPy data type.
132
+
133
+ Raises
134
+ ------
135
+ ValueError
136
+ If the data type is a NumPy "Object" type, which is ambiguous, or if multiple
137
+ or no Zarr data types are found that match the provided dtype.
138
+
139
+ Notes
140
+ -----
141
+ This function attempts to resolve a Zarr data type from a given native data type.
142
+ If the dtype is a NumPy "Object" data type, it raises a ValueError, as this type
143
+ can represent multiple Zarr data types. In such cases, a specific Zarr data type
144
+ should be explicitly constructed instead of relying on dynamic resolution.
145
+
146
+ If multiple matches are found, it will also raise a ValueError. In this case
147
+ conflicting data types must be unregistered, or the Zarr data type should be explicitly
148
+ constructed.
149
+ """
150
+
151
+ if dtype == np.dtype("O"):
152
+ msg = (
153
+ f"Zarr data type resolution from {dtype} failed. "
154
+ 'Attempted to resolve a zarr data type from a numpy "Object" data type, which is '
155
+ 'ambiguous, as multiple zarr data types can be represented by the numpy "Object" '
156
+ "data type. "
157
+ "In this case you should construct your array by providing a specific Zarr data "
158
+ 'type. For a list of Zarr data types that are compatible with the numpy "Object"'
159
+ "data type, see https://github.com/zarr-developers/zarr-python/issues/3117"
160
+ )
161
+ raise ValueError(msg)
162
+ matched: list[ZDType[TBaseDType, TBaseScalar]] = []
163
+ for val in self.contents.values():
164
+ # DataTypeValidationError means "this dtype doesn't match me", which is
165
+ # expected and suppressed. Other exceptions (e.g. ValueError for a dtype
166
+ # that matches the type but has an invalid configuration) are propagated
167
+ # to the caller.
168
+ with contextlib.suppress(DataTypeValidationError):
169
+ matched.append(val.from_native_dtype(dtype))
170
+ if len(matched) == 1:
171
+ return matched[0]
172
+ elif len(matched) > 1:
173
+ msg = (
174
+ f"Zarr data type resolution from {dtype} failed. "
175
+ f"Multiple data type wrappers found that match dtype '{dtype}': {matched}. "
176
+ "You should unregister one of these data types, or avoid Zarr data type inference "
177
+ "entirely by providing a specific Zarr data type when creating your array."
178
+ "For more information, see https://github.com/zarr-developers/zarr-python/issues/3117"
179
+ )
180
+ raise ValueError(msg)
181
+ raise ValueError(f"No Zarr data type found that matches dtype '{dtype!r}'")
182
+
183
+ def match_json(
184
+ self, data: DTypeJSON, *, zarr_format: ZarrFormat
185
+ ) -> ZDType[TBaseDType, TBaseScalar]:
186
+ """
187
+ Match a JSON representation of a data type to a registered ZDType.
188
+
189
+ Parameters
190
+ ----------
191
+ data : DTypeJSON
192
+ The JSON representation of a data type to match.
193
+ zarr_format : ZarrFormat
194
+ The Zarr format version to consider when matching data types.
195
+
196
+ Returns
197
+ -------
198
+ ZDType[TBaseDType, TBaseScalar]
199
+ The matched ZDType corresponding to the JSON representation.
200
+
201
+ Raises
202
+ ------
203
+ ValueError
204
+ If no matching Zarr data type is found for the given JSON data.
205
+ """
206
+
207
+ for val in self.contents.values():
208
+ try:
209
+ return val.from_json(data, zarr_format=zarr_format)
210
+ except DataTypeValidationError:
211
+ pass
212
+ raise ValueError(f"No Zarr data type found that matches {data!r}")
.venv/lib/python3.11/site-packages/zarr/core/dtype/wrapper.py ADDED
@@ -0,0 +1,300 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Wrapper for native array data types.
3
+
4
+ The ``ZDType`` class is an abstract base class for wrapping native array data types, e.g. NumPy dtypes.
5
+ ``ZDType`` provides a common interface for working with data types in a way that is independent of the
6
+ underlying data type system.
7
+
8
+ The wrapper class encapsulates a native data type. Instances of the class can be created from a
9
+ native data type instance, and a native data type instance can be created from an instance of the
10
+ wrapper class.
11
+
12
+ The wrapper class is responsible for:
13
+ - Serializing and deserializing a native data type to Zarr V2 or Zarr V3 metadata.
14
+ This ensures that the data type can be properly stored and retrieved from array metadata.
15
+ - Serializing and deserializing scalar values to Zarr V2 or Zarr V3 metadata. This is important for
16
+ storing a fill value for an array in a manner that is valid for the data type.
17
+
18
+ You can add support for a new data type in Zarr by subclassing ``ZDType`` wrapper class and adapt its methods
19
+ to support your native data type. The wrapper class must be added to a data type registry
20
+ (defined elsewhere) before array creation routines or array reading routines can use your new data
21
+ type.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ from abc import ABC, abstractmethod
27
+ from dataclasses import dataclass
28
+ from typing import (
29
+ TYPE_CHECKING,
30
+ ClassVar,
31
+ Generic,
32
+ Literal,
33
+ Self,
34
+ TypeGuard,
35
+ TypeVar,
36
+ overload,
37
+ )
38
+
39
+ import numpy as np
40
+
41
+ if TYPE_CHECKING:
42
+ from zarr.core.common import JSON, ZarrFormat
43
+ from zarr.core.dtype.common import DTypeJSON, DTypeSpec_V2, DTypeSpec_V3
44
+
45
+ # This the upper bound for the scalar types we support. It's numpy scalars + str,
46
+ # because the new variable-length string dtype in numpy does not have a corresponding scalar type
47
+ TBaseScalar = np.generic | str | bytes
48
+ # This is the bound for the dtypes that we support. If we support non-numpy dtypes,
49
+ # then this bound will need to be widened.
50
+ TBaseDType = np.dtype[np.generic]
51
+
52
+ # These two type parameters are covariant because we want
53
+ # x : ZDType[BaseDType, BaseScalar] = ZDType[SubDType, SubScalar]
54
+ # to type check
55
+ TScalar_co = TypeVar("TScalar_co", bound=TBaseScalar, covariant=True)
56
+ TDType_co = TypeVar("TDType_co", bound=TBaseDType, covariant=True)
57
+
58
+
59
+ @dataclass(frozen=True, kw_only=True, slots=True)
60
+ class ZDType(ABC, Generic[TDType_co, TScalar_co]):
61
+ """
62
+ Abstract base class for wrapping native array data types, e.g. numpy dtypes
63
+
64
+ Attributes
65
+ ----------
66
+ dtype_cls : ClassVar[type[TDType]]
67
+ The wrapped dtype class. This is a class variable.
68
+ _zarr_v3_name : ClassVar[str]
69
+ The name given to the data type by a Zarr v3 data type specification. This is a
70
+ class variable, and it should generally be unique across different data types.
71
+ """
72
+
73
+ # this class will create a native data type
74
+ dtype_cls: ClassVar[type[TDType_co]]
75
+ _zarr_v3_name: ClassVar[str]
76
+
77
+ @classmethod
78
+ def _check_native_dtype(cls: type[Self], dtype: TBaseDType) -> TypeGuard[TDType_co]:
79
+ """
80
+ Check that a native data type matches the dtype_cls class attribute.
81
+
82
+ Used as a type guard.
83
+
84
+ Parameters
85
+ ----------
86
+ dtype : TDType
87
+ The dtype to check.
88
+
89
+ Returns
90
+ -------
91
+ Bool
92
+ True if the dtype matches, False otherwise.
93
+ """
94
+ return type(dtype) is cls.dtype_cls
95
+
96
+ @classmethod
97
+ @abstractmethod
98
+ def from_native_dtype(cls: type[Self], dtype: TBaseDType) -> Self:
99
+ """
100
+ Create a ZDType instance from a native data type.
101
+
102
+ This method is used when taking a user-provided native data type, like a NumPy data type,
103
+ and creating the corresponding ZDType instance from them.
104
+
105
+ Parameters
106
+ ----------
107
+ dtype : TDType
108
+ The native data type object to wrap.
109
+
110
+ Returns
111
+ -------
112
+ Self
113
+ The ZDType that wraps the native data type.
114
+
115
+ Raises
116
+ ------
117
+ TypeError
118
+ If the native data type is not consistent with the wrapped data type.
119
+ """
120
+ raise NotImplementedError # pragma: no cover
121
+
122
+ @abstractmethod
123
+ def to_native_dtype(self: Self) -> TDType_co:
124
+ """
125
+ Return an instance of the wrapped data type. This operation inverts ``from_native_dtype``.
126
+
127
+ Returns
128
+ -------
129
+ TDType
130
+ The native data type wrapped by this ZDType.
131
+ """
132
+ raise NotImplementedError # pragma: no cover
133
+
134
+ @classmethod
135
+ @abstractmethod
136
+ def _from_json_v2(cls: type[Self], data: DTypeJSON) -> Self:
137
+ raise NotImplementedError # pragma: no cover
138
+
139
+ @classmethod
140
+ @abstractmethod
141
+ def _from_json_v3(cls: type[Self], data: DTypeJSON) -> Self:
142
+ raise NotImplementedError # pragma: no cover
143
+
144
+ @classmethod
145
+ def from_json(cls: type[Self], data: DTypeJSON, *, zarr_format: ZarrFormat) -> Self:
146
+ """
147
+ Create an instance of this ZDType from JSON data.
148
+
149
+ Parameters
150
+ ----------
151
+ data : DTypeJSON
152
+ The JSON representation of the data type.
153
+
154
+ zarr_format : ZarrFormat
155
+ The zarr format version.
156
+
157
+ Returns
158
+ -------
159
+ Self
160
+ An instance of this data type.
161
+ """
162
+ if zarr_format == 2:
163
+ return cls._from_json_v2(data)
164
+ if zarr_format == 3:
165
+ return cls._from_json_v3(data)
166
+ raise ValueError(f"zarr_format must be 2 or 3, got {zarr_format}") # pragma: no cover
167
+
168
+ @overload
169
+ def to_json(self, zarr_format: Literal[2]) -> DTypeSpec_V2: ...
170
+
171
+ @overload
172
+ def to_json(self, zarr_format: Literal[3]) -> DTypeSpec_V3: ...
173
+
174
+ @abstractmethod
175
+ def to_json(self, zarr_format: ZarrFormat) -> DTypeSpec_V2 | DTypeSpec_V3:
176
+ """
177
+ Serialize this ZDType to JSON.
178
+
179
+ Parameters
180
+ ----------
181
+ zarr_format : ZarrFormat
182
+ The zarr format version.
183
+
184
+ Returns
185
+ -------
186
+ DTypeJSON_V2 | DTypeJSON_V3
187
+ The JSON-serializable representation of the wrapped data type
188
+ """
189
+ raise NotImplementedError # pragma: no cover
190
+
191
+ @abstractmethod
192
+ def _check_scalar(self, data: object) -> bool:
193
+ """
194
+ Check that a python object is a valid scalar value for the wrapped data type.
195
+
196
+ Parameters
197
+ ----------
198
+ data : object
199
+ A value to check.
200
+
201
+ Returns
202
+ -------
203
+ Bool
204
+ True if the object is valid, False otherwise.
205
+ """
206
+ raise NotImplementedError # pragma: no cover
207
+
208
+ @abstractmethod
209
+ def cast_scalar(self, data: object) -> TScalar_co:
210
+ """
211
+ Cast a python object to the wrapped scalar type.
212
+
213
+ The type of the provided scalar is first checked for compatibility.
214
+ If it's incompatible with the associated scalar type, a ``TypeError`` will be raised.
215
+
216
+ Parameters
217
+ ----------
218
+ data : object
219
+ The python object to cast.
220
+
221
+ Returns
222
+ -------
223
+ TScalar
224
+ The cast value.
225
+ """
226
+ raise NotImplementedError # pragma: no cover
227
+
228
+ @abstractmethod
229
+ def default_scalar(self) -> TScalar_co:
230
+ """
231
+ Get the default scalar value for the wrapped data type.
232
+
233
+ This is a method, rather than an attribute, because the default value for some data types depends on parameters that are
234
+ not known until a concrete data type is wrapped. For example, data types parametrized by a
235
+ length like fixed-length strings or bytes will generate scalars consistent with that length.
236
+
237
+ Returns
238
+ -------
239
+ TScalar
240
+ The default value for this data type.
241
+ """
242
+ raise NotImplementedError # pragma: no cover
243
+
244
+ @abstractmethod
245
+ def from_json_scalar(self: Self, data: JSON, *, zarr_format: ZarrFormat) -> TScalar_co:
246
+ """
247
+ Read a JSON-serializable value as a scalar.
248
+
249
+ Parameters
250
+ ----------
251
+ data : JSON
252
+ A JSON representation of a scalar value.
253
+ zarr_format : ZarrFormat
254
+ The zarr format version. This is specified because the JSON serialization of scalars
255
+ differs between Zarr V2 and Zarr V3.
256
+
257
+ Returns
258
+ -------
259
+ TScalar
260
+ The deserialized scalar value.
261
+ """
262
+ raise NotImplementedError # pragma: no cover
263
+
264
+ @abstractmethod
265
+ def to_json_scalar(self, data: object, *, zarr_format: ZarrFormat) -> JSON:
266
+ """
267
+ Serialize a python object to the JSON representation of a scalar.
268
+
269
+ The value will first be cast to the scalar type associated with this ZDType, then serialized
270
+ to JSON.
271
+
272
+ Parameters
273
+ ----------
274
+ data : object
275
+ The value to convert.
276
+ zarr_format : ZarrFormat
277
+ The zarr format version. This is specified because the JSON serialization of scalars
278
+ differs between Zarr V2 and Zarr V3.
279
+
280
+ Returns
281
+ -------
282
+ JSON
283
+ The JSON-serialized scalar.
284
+ """
285
+ raise NotImplementedError # pragma: no cover
286
+
287
+
288
+ def scalar_failed_type_check_msg(
289
+ cls_instance: ZDType[TBaseDType, TBaseScalar], bad_scalar: object
290
+ ) -> str:
291
+ """
292
+ Generate an error message reporting that a particular value failed a type check when attempting
293
+ to cast that value to a scalar.
294
+ """
295
+ return (
296
+ f"The value {bad_scalar!r} failed a type check. "
297
+ f"It cannot be safely cast to a scalar compatible with {cls_instance}. "
298
+ f"Consult the documentation for {cls_instance} to determine the possible values that can "
299
+ "be cast to scalars of the wrapped data type."
300
+ )
.venv/lib/python3.11/site-packages/zarr/core/group.py ADDED
The diff for this file is too large to render. See raw diff