├── tests ├── __init__.py └── test_core.py ├── MANIFEST.in ├── zarrdump ├── __init__.py └── core.py ├── .pre-commit-config.yaml ├── .github └── workflows │ ├── lint.yaml │ └── pytest.yaml ├── Makefile ├── tox.ini ├── pyproject.toml ├── LICENSE ├── HISTORY.rst ├── README.rst └── .gitignore /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include HISTORY.rst 2 | -------------------------------------------------------------------------------- /zarrdump/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.6.1" 2 | 3 | from .core import dump 4 | 5 | __all__ = ["dump"] 6 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/astral-sh/ruff-pre-commit 3 | rev: v0.8.1 4 | hooks: 5 | - id: ruff 6 | args: ["--fix"] 7 | - id: ruff-format 8 | - repo: https://github.com/pre-commit/pre-commit-hooks 9 | rev: v5.0.0 10 | hooks: 11 | - id: check-added-large-files 12 | args: [--maxkb=250] 13 | - id: trailing-whitespace 14 | - id: end-of-file-fixer 15 | -------------------------------------------------------------------------------- /.github/workflows/lint.yaml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | pre-commit: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Check out code 16 | uses: actions/checkout@v4 17 | 18 | - name: Set up Python 19 | uses: actions/setup-python@v4 20 | with: 21 | python-version: "3.12" 22 | 23 | - name: Install dependencies 24 | run: | 25 | python -m pip install --upgrade pip 26 | pip install pre-commit 27 | 28 | - name: Run pre-commit 29 | run: pre-commit run --all-files 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | clean: clean-build clean-pyc clean-test 3 | 4 | clean-build: 5 | rm -fr build/ 6 | rm -fr dist/ 7 | rm -fr .eggs/ 8 | find . -name '*.egg-info' -exec rm -fr {} + 9 | find . -name '*.egg' -exec rm -f {} + 10 | 11 | clean-pyc: 12 | find . -name '*.pyc' -exec rm -f {} + 13 | find . -name '*.pyo' -exec rm -f {} + 14 | find . -name '*~' -exec rm -f {} + 15 | find . -name '__pycache__' -exec rm -fr {} + 16 | 17 | clean-test: 18 | rm -fr .tox/ 19 | rm -f .coverage 20 | rm -fr htmlcov/ 21 | rm -fr .pytest_cache 22 | 23 | test: 24 | pytest 25 | 26 | dist: 27 | rm -rf dist 28 | python -m build 29 | 30 | release: dist 31 | python -m twine upload dist/* 32 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # tox (https://tox.readthedocs.io/) is a tool for running tests 2 | # in multiple virtualenvs. This configuration file will run the 3 | # test suite on all supported python versions. To use it, "pip install tox" 4 | # and then run "tox" from this directory. 5 | 6 | [tox] 7 | envlist = py-xarray{21,202206,202306,202312,202501,202509} 8 | 9 | [testenv] 10 | deps = 11 | click 12 | pytest 13 | fsspec 14 | gcsfs 15 | xarray21: xarray>=0.21.0,<0.22.0 16 | xarray202206: xarray>=2022.06.0,<2022.07.0 17 | xarray202306: xarray>=2023.06.0,<2023.07.0 18 | xarray202312: xarray>=2023.12.0,<2024.01.0 19 | xarray202501: xarray>=2025.01.0,<2025.02.0 20 | xarray202509: xarray>=2025.09.0,<2025.10.0 21 | xarray21: numpy<2 22 | xarray202206: numpy<2 23 | xarray202306: numpy<2 24 | xarray21: zarr<3 25 | xarray202206: zarr<3 26 | xarray202306: zarr<3 27 | xarray202312: zarr<3 28 | xarray202501: zarr>=3 29 | xarray202509: zarr>=3.1 30 | commands = 31 | pytest 32 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "zarrdump" 7 | version = "0.6.1" 8 | description = "Describe zarr stores from the command line." 9 | readme = "README.rst" 10 | license = { text = "BSD 3-Clause license" } 11 | authors = [ 12 | { name = "Oliver Watt-Meyer", email = "oliverwatt@gmail.com" } 13 | ] 14 | requires-python = ">=3.10" 15 | dependencies = [ 16 | "click>=7.0.0", 17 | "fsspec>=0.7.0", 18 | "xarray>=0.21.0", 19 | "zarr>=2.3.0" 20 | ] 21 | classifiers = [ 22 | "Development Status :: 2 - Pre-Alpha", 23 | "Intended Audience :: Developers", 24 | "License :: OSI Approved :: BSD License", 25 | "Natural Language :: English", 26 | "Programming Language :: Python :: 3", 27 | "Programming Language :: Python :: 3.10", 28 | "Programming Language :: Python :: 3.11", 29 | "Programming Language :: Python :: 3.12", 30 | "Programming Language :: Python :: 3.13", 31 | "Programming Language :: Python :: 3.14" 32 | ] 33 | [project.urls] 34 | Homepage = "https://github.com/oliverwm1/zarrdump" 35 | Changelog = "https://github.com/oliverwm1/zarrdump/blob/main/HISTORY.rst" 36 | 37 | [project.scripts] 38 | zarrdump = "zarrdump.core:dump" 39 | 40 | [tool.setuptools] 41 | packages = ["zarrdump"] 42 | 43 | [project.optional-dependencies] 44 | dev = ["pre-commit", "pytest", "ruff"] 45 | 46 | [tool.ruff.lint] 47 | select = ["E", "F", "I", "W", "UP"] 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, Oliver Watt-Meyer 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /.github/workflows/pytest.yaml: -------------------------------------------------------------------------------- 1 | name: pytest 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] 12 | fail-fast: false 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Set up Python ${{ matrix.python-version }} 17 | uses: actions/setup-python@v4 18 | with: 19 | python-version: ${{ matrix.python-version }} 20 | - name: Install dependencies 21 | run: | 22 | python -m pip install --upgrade pip 23 | pip install tox 24 | pip install -e . 25 | - name: Test with tox 26 | run: | 27 | PYTHON_VERSION=$(python --version | cut -d' ' -f2) 28 | 29 | if [[ "$PYTHON_VERSION" == "3.10"* ]]; then 30 | echo "Detected Python version: $PYTHON_VERSION" 31 | echo "Skipping tests with xarray versions that require newer python" 32 | tox -p --skip-env py-xarray2025 33 | elif [[ "$PYTHON_VERSION" == "3.12"* ]]; then 34 | echo "Detected Python version: $PYTHON_VERSION" 35 | echo "Skipping py-xarray21 environment which fails with newer python versions" 36 | tox -p --skip-env py-xarray21 37 | elif [[ "$PYTHON_VERSION" == "3.13"* ]]; then 38 | echo "Detected Python version: $PYTHON_VERSION" 39 | echo "Running only in environments which build quickly" 40 | tox -p -e py-xarray202312,py-xarray202501,py-xarray202509 41 | elif [[ "$PYTHON_VERSION" == "3.14"* ]]; then 42 | echo "Detected Python version: $PYTHON_VERSION" 43 | echo "Running only in environments which build quickly" 44 | tox -p -e py-xarray202501,py-xarray202509 45 | else 46 | tox -p 47 | fi 48 | -------------------------------------------------------------------------------- /HISTORY.rst: -------------------------------------------------------------------------------- 1 | v0.6.1 (2025-09-21) 2 | ------------------- 3 | 4 | - drop support for python 3.8 5 | - add tox environment for xarray 2025.09.0 and zarr-python>=3.1 6 | - resolve issue where non-None storage options were being used when opening a local zarr (resolves https://github.com/oliverwm1/zarrdump/issues/21) 7 | 8 | v0.6.0 (2025-04-02) 9 | ------------------- 10 | 11 | - add gcsfs to tox testenv 12 | - add `--storage-option` flag (contributed by @garciampred in https://github.com/oliverwm1/zarrdump/pull/18) 13 | 14 | v0.5.0 (2025-03-07) 15 | ------------------- 16 | 17 | - add support for zarr-python>=3.0.0 18 | - drop support for xarray<0.19.0 19 | - defer to xarray/zarr for handling consolidated metadata 20 | 21 | v0.4.2 (2025-01-20) 22 | ------------------- 23 | 24 | - pin zarr-python dependency to below version 3 25 | - specify python>=3.8 requirement in setup.py 26 | 27 | v0.4.1 (2022-08-22) 28 | ------------------- 29 | 30 | - ensure can open and dump zarr group when xarray v2022.06.0 installed 31 | 32 | v0.4.0 (2022-08-13) 33 | ------------------- 34 | 35 | - add -m/--max-rows option to allow setting the xarray display_max_rows option. Default is set to 999. 36 | 37 | v0.3.0 (2021-12-05) 38 | ------------------- 39 | 40 | - add -i/--info flag to allow printing with ncdump-style using xr.Dataset.info() 41 | 42 | v0.2.2 (2021-03-28) 43 | ------------------- 44 | 45 | - change implementation for checking whether object is an xarray dataset 46 | - apply black formatting 47 | 48 | v0.2.1 (2020-11-26) 49 | ------------------- 50 | 51 | - remove gcsfs from requirements 52 | - improve README and pypi page description 53 | 54 | v0.2.0 (2020-11-18) 55 | ------------------- 56 | 57 | - allowing dumping info for zarr arrays or zarr groups that don't represent xarray datasets 58 | - add -v option to print info for specific variable in dataset or group 59 | 60 | v0.1.1 (2020-10-04) 61 | ------------------- 62 | 63 | - bug fix 64 | 65 | v0.1.0 (2020-09-22) 66 | ------------------- 67 | 68 | - initial implementation 69 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://img.shields.io/pypi/v/zarrdump.svg 2 | :target: https://pypi.org/project/zarrdump/ 3 | 4 | Describe `zarr `_ stores from the command line. A path to any filesystem implemented by `fsspec `_ is valid. 5 | 6 | Installation 7 | ------------ 8 | 9 | :: 10 | 11 | $ pip install zarrdump 12 | 13 | Usage 14 | ----- 15 | 16 | If zarr store can be opened by `xarray `_, the xarray representation will be displayed: 17 | :: 18 | 19 | $ zarrdump gs://bucket/dataset.zarr 20 | 21 | Dimensions: (lat: 73, lon: 144, time: 32) 22 | Coordinates: 23 | * lat (lat) float64 -90.0 -87.5 -85.0 -82.5 -80.0 ... 82.5 85.0 87.5 90.0 24 | * lon (lon) float64 0.0 2.5 5.0 7.5 10.0 ... 350.0 352.5 355.0 357.5 25 | * time (time) object 2016-12-01 00:00:00 ... 2017-01-01 00:00:00 26 | Data variables: 27 | ps (time, lat, lon) float32 dask.array 28 | ts (time, lat, lon) float32 dask.array 29 | 30 | 31 | Can show information for a particular variable/array: 32 | :: 33 | 34 | $ zarrdump -v ts gs://bucket/dataset.zarr 35 | 36 | dask.array 37 | Coordinates: 38 | * lat (lat) float64 -90.0 -87.5 -85.0 -82.5 -80.0 ... 82.5 85.0 87.5 90.0 39 | * lon (lon) float64 0.0 2.5 5.0 7.5 10.0 ... 350.0 352.5 355.0 357.5 40 | * time (time) object 2016-12-01 00:00:00 ... 2017-01-01 00:00:00 41 | Attributes: 42 | longname: surface temperature 43 | units: K 44 | 45 | Diagnostic information will also be printed for zarr arrays or zarr groups which do not represent xarray datasets: 46 | :: 47 | 48 | $ zarrdump group.zarr 49 | Name : / 50 | Type : zarr.hierarchy.Group 51 | Read-only : False 52 | Store type : fsspec.mapping.FSMap 53 | No. members : 2 54 | No. arrays : 2 55 | No. groups : 0 56 | Arrays : bar, foo 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | .vscode 132 | -------------------------------------------------------------------------------- /zarrdump/core.py: -------------------------------------------------------------------------------- 1 | import click 2 | import fsspec 3 | import xarray as xr 4 | import zarr 5 | 6 | ZARR_MAJOR_VERSION = zarr.__version__.split(".")[0] 7 | 8 | 9 | # From https://stackoverflow.com/questions/51164033/python-click-multiple-key-value-pair-arguments 10 | def _attributes_to_dict( 11 | ctx: click.Context, attribute: click.Option, attributes: tuple[str, ...] | None 12 | ) -> dict[str, str] | None: 13 | """Click callback that converts attributes specified in the form `key=value` to a 14 | dictionary""" 15 | if attributes is None or len(attributes) == 0: 16 | return None 17 | else: 18 | result = {} 19 | for arg in attributes: 20 | k, v = arg.split("=") 21 | if k in result: 22 | raise click.BadParameter(f"Attribute {k!r} is specified twice") 23 | result[k] = v 24 | 25 | return result 26 | 27 | 28 | @click.command() 29 | @click.argument("url") 30 | @click.option("-v", "--variable", type=str, help="Dump variable's info") 31 | @click.option("-m", "--max-rows", default=999, help="Maximum number of rows to display") 32 | @click.option("-i", "--info", is_flag=True, help="Use ncdump style") 33 | @click.option( 34 | "--storage-option", 35 | help="Key/value pair separated by '=', to be passed to the storage_options " 36 | "argument of fsspec. It can be used multiple times to pass multiple " 37 | "arguments. For example: --storage-option profile=contabo " 38 | "--storage-option endpoint=https://eu2.contabostorage.com", 39 | multiple=True, 40 | callback=_attributes_to_dict, 41 | default=None, 42 | ) 43 | def dump( 44 | url: str, 45 | variable: str, 46 | max_rows: int, 47 | info: bool, 48 | storage_option: dict[str, str] | None, 49 | ): 50 | fs, _, _ = fsspec.get_fs_token_paths(url, storage_options=storage_option) 51 | if not fs.exists(url): 52 | raise click.ClickException(f"No file or directory at {url}") 53 | 54 | object_, object_is_xarray = _open_with_xarray_or_zarr(url, storage_option) 55 | 56 | if variable is not None: 57 | if info: 58 | raise click.ClickException("Cannot use both '-v' and '-i' options") 59 | object_ = object_[variable] 60 | 61 | if not object_is_xarray: 62 | object_ = object_.info 63 | 64 | if object_is_xarray and info: 65 | object_.info() 66 | else: 67 | with xr.set_options(display_max_rows=max_rows): 68 | print(object_) 69 | 70 | 71 | def _open_with_xarray_or_zarr( 72 | url: str, storage_option: dict[str, str] | None = None 73 | ) -> tuple[xr.Dataset | zarr.Group | zarr.Array, bool]: 74 | if ZARR_MAJOR_VERSION >= "3": 75 | # TODO: remove ValueError here once a version of xarray is released 76 | # with https://github.com/pydata/xarray/pull/10025 merged 77 | exceptions = (KeyError, ValueError) 78 | else: 79 | exceptions = (KeyError, TypeError) 80 | 81 | try: 82 | result = xr.open_zarr(url, storage_options=storage_option) 83 | is_xarray_dataset = True 84 | except exceptions: 85 | # xarray cannot open dataset, fall back to using zarr directly 86 | result = zarr.open(url, storage_options=storage_option) 87 | is_xarray_dataset = False 88 | 89 | return result, is_xarray_dataset 90 | -------------------------------------------------------------------------------- /tests/test_core.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pytest 3 | import xarray as xr 4 | import zarr 5 | from click.testing import CliRunner 6 | 7 | import zarrdump 8 | from zarrdump.core import _open_with_xarray_or_zarr, dump 9 | 10 | ZARR_MAJOR_VERSION = zarr.__version__.split(".")[0] 11 | 12 | 13 | def test_version(): 14 | assert zarrdump.__version__ == "0.6.1" 15 | 16 | 17 | @pytest.fixture() 18 | def tmp_xarray_ds(tmpdir): 19 | def write_ds_to_zarr(consolidated=False, n_vars=1): 20 | ds = xr.Dataset( 21 | {f"var{i}": xr.DataArray(range(3)) for i in range(1, n_vars + 1)} 22 | ) 23 | path = str(tmpdir.join("test.zarr")) 24 | ds.to_zarr(path, consolidated=consolidated) 25 | return ds, path 26 | 27 | return write_ds_to_zarr 28 | 29 | 30 | @pytest.fixture() 31 | def tmp_zarr_group(tmpdir): 32 | def write_group_to_zarr(consolidated=False): 33 | path = str(tmpdir.join("test.zarr")) 34 | z = zarr.open_group(path) 35 | if ZARR_MAJOR_VERSION >= "3": 36 | arr = z.create_array("var1", shape=(3, 5), dtype=np.float32) 37 | else: 38 | arr = z.create_dataset("var1", shape=(3, 5), dtype=np.float32) 39 | arr[:] = 1.0 40 | if consolidated: 41 | zarr.consolidate_metadata(path) 42 | return z, path 43 | 44 | return write_group_to_zarr 45 | 46 | 47 | @pytest.mark.parametrize("consolidated", [True, False]) 48 | def test__open_with_xarray_or_zarr_on_zarr_group(tmp_zarr_group, consolidated): 49 | group, path = tmp_zarr_group(consolidated=consolidated) 50 | opened_group, is_xarray_dataset = _open_with_xarray_or_zarr(path) 51 | np.testing.assert_allclose(group["var1"], opened_group["var1"]) 52 | assert not is_xarray_dataset 53 | 54 | 55 | @pytest.mark.parametrize("consolidated", [True, False]) 56 | def test__open_with_xarray_or_zarr_on_xarray_ds(tmp_xarray_ds, consolidated): 57 | ds, path = tmp_xarray_ds(consolidated=consolidated) 58 | opened_ds, is_xarray_dataset = _open_with_xarray_or_zarr(path) 59 | np.testing.assert_allclose(ds["var1"], opened_ds["var1"]) 60 | assert is_xarray_dataset 61 | 62 | 63 | def test_dump_non_existent_url(): 64 | runner = CliRunner() 65 | result = runner.invoke(dump, ["non/existent/path"]) 66 | assert result.exit_code == 1 67 | assert result.output == "Error: No file or directory at non/existent/path\n" 68 | 69 | 70 | @pytest.mark.parametrize("options", [[], ["-v", "var1"]]) 71 | def test_dump_executes_on_zarr_group(tmp_zarr_group, options): 72 | runner = CliRunner() 73 | _, path = tmp_zarr_group(consolidated=True) 74 | result = runner.invoke(dump, [path] + options) 75 | assert result.exit_code == 0 76 | if "-v" in options: 77 | assert "Array" in result.output 78 | else: 79 | assert "Group" in result.output 80 | 81 | 82 | @pytest.mark.parametrize("options", [[], ["-v", "var1"], ["--info"]]) 83 | def test_dump_executes_on_xarray_dataset(tmp_xarray_ds, options): 84 | runner = CliRunner() 85 | _, path = tmp_xarray_ds(consolidated=True) 86 | result = runner.invoke(dump, [path] + options) 87 | assert result.exit_code == 0 88 | if "-v" in options: 89 | expected_content = " 30 110 | 111 | 112 | def test_dump_max_rows_limited(tmp_xarray_ds): 113 | runner = CliRunner() 114 | _, path = tmp_xarray_ds(consolidated=True, n_vars=30) 115 | result = runner.invoke(dump, [path, "-m", 10]) 116 | assert len(result.output.split("\n")) < 20 # give some buffer over 10 117 | 118 | 119 | @pytest.mark.parametrize("token", ["anon", "invalid_test_token"]) 120 | def test_storage_options(token): 121 | runner = CliRunner() 122 | result = runner.invoke( 123 | dump, 124 | [ 125 | "--storage-option", 126 | f"token={token}", 127 | "gs://gcp-public-data-arco-era5/co/single-level-reanalysis.zarr", 128 | ], 129 | ) 130 | if token == "anon": 131 | assert result.exit_code == 0 132 | assert len(result.output.split("\n")) > 30 133 | else: 134 | assert result.exit_code == 1 135 | assert str(result.exception) == "Invalid Credentials, 401" 136 | --------------------------------------------------------------------------------