Upload folder using huggingface_hub (part 19)
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .venv/lib/python3.11/site-packages/zarr-3.1.6.dist-info/INSTALLER +1 -0
- .venv/lib/python3.11/site-packages/zarr-3.1.6.dist-info/METADATA +171 -0
- .venv/lib/python3.11/site-packages/zarr-3.1.6.dist-info/RECORD +103 -0
- .venv/lib/python3.11/site-packages/zarr-3.1.6.dist-info/REQUESTED +0 -0
- .venv/lib/python3.11/site-packages/zarr-3.1.6.dist-info/WHEEL +4 -0
- .venv/lib/python3.11/site-packages/zarr-3.1.6.dist-info/entry_points.txt +5 -0
- .venv/lib/python3.11/site-packages/zarr-3.1.6.dist-info/licenses/LICENSE.txt +21 -0
- .venv/lib/python3.11/site-packages/zarr/core/attributes.py +57 -0
- .venv/lib/python3.11/site-packages/zarr/core/buffer/__init__.py +21 -0
- .venv/lib/python3.11/site-packages/zarr/core/buffer/__pycache__/__init__.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/zarr/core/buffer/__pycache__/core.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/zarr/core/buffer/__pycache__/cpu.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/zarr/core/buffer/__pycache__/gpu.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/zarr/core/buffer/core.py +586 -0
- .venv/lib/python3.11/site-packages/zarr/core/buffer/cpu.py +238 -0
- .venv/lib/python3.11/site-packages/zarr/core/buffer/gpu.py +237 -0
- .venv/lib/python3.11/site-packages/zarr/core/chunk_grids.py +306 -0
- .venv/lib/python3.11/site-packages/zarr/core/chunk_key_encodings.py +129 -0
- .venv/lib/python3.11/site-packages/zarr/core/codec_pipeline.py +562 -0
- .venv/lib/python3.11/site-packages/zarr/core/common.py +254 -0
- .venv/lib/python3.11/site-packages/zarr/core/config.py +153 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/__init__.py +281 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/__pycache__/__init__.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/__pycache__/common.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/__pycache__/registry.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/__pycache__/wrapper.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/common.py +233 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__init__.py +0 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/__init__.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/bool.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/bytes.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/common.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/complex.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/float.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/int.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/string.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/structured.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/__pycache__/time.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/bool.py +319 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/bytes.py +1279 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/common.py +581 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/complex.py +418 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/float.py +435 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/int.py +1521 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/string.py +825 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/structured.py +477 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/npy/time.py +885 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/registry.py +212 -0
- .venv/lib/python3.11/site-packages/zarr/core/dtype/wrapper.py +300 -0
- .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
|
|
|