Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
ca049a1
Function for finding NWB's & Zarrs
jwodder Dec 16, 2021
6d1bd2f
Add local asset subclasses for different file extensions
jwodder Dec 17, 2021
fd7bfb6
Add methods for metadata & validation
jwodder Jan 3, 2022
96b6c5d
Use a dedicated type for digest value+algorithm pairs
jwodder Jan 7, 2022
aaa217f
Let the new find_dandi_files() take multiple paths
jwodder Jan 7, 2022
8c71cf6
Make upload.py use the new find_dandi_files()
jwodder Jan 7, 2022
bf1fa33
Rename get_metadata()'s `allow_any_path` to `ignore_errors`
jwodder Jan 7, 2022
f5f84d8
Abstract out the calculation of local assets' sizes & mtimes
jwodder Jan 7, 2022
46f7aff
Abstract out calculation of local assets' etags
jwodder Jan 7, 2022
19b1fd6
Calculating Zarr metadata
jwodder Jan 7, 2022
7c19cfc
Some code cleanup
jwodder Jan 7, 2022
891feb1
Move core asset upload code to files.py
jwodder Jan 7, 2022
0bbe8ad
Add RemoteBlobAsset and RemoteZarrAsset classes
jwodder Jan 10, 2022
362a432
Implementation of Zarr checksumming
jwodder Jan 10, 2022
9b4d78c
Give `BaseRemoteAsset` `is_blob()` and `is_zarr()` methods
jwodder Jan 10, 2022
c3c5102
download --sync: Use new `find_dandi_files()`
jwodder Jan 10, 2022
580b9cd
dandi_zarr_checksum is now a digest type
jwodder Jan 10, 2022
5299561
`BaseRemoteAsset.get_etag()`
jwodder Jan 10, 2022
f1f4e0f
`ZarrAsset.iter_upload()`
jwodder Jan 10, 2022
67a2bb5
Basic test of uploading a Zarr
jwodder Jan 11, 2022
f660050
Fix
jwodder Jan 12, 2022
8c8e87e
Test & fix
jwodder Jan 12, 2022
f503eba
Make LGTM happy
jwodder Jan 12, 2022
ab9ffb0
Add a zarr_dandiset fixture
jwodder Jan 12, 2022
4e5d10c
Test replacing an uploaded Zarr with a non-Zarr at the same path
jwodder Jan 12, 2022
25176da
Add xfailing tests involving Zarr downloads
jwodder Jan 12, 2022
692eebf
Error on attempting to upload a Zarr to an embargoed Dandiset
jwodder Jan 12, 2022
b0796a4
Add some docstrings
jwodder Jan 13, 2022
6b8f07a
Rename `get_size()` and `get_mtime()` to `size` and `modified`
jwodder Jan 13, 2022
193d27c
Raise NotImplementedError when trying to download a Zarr
jwodder Jan 13, 2022
448fb47
Don't recognize empty directories as Zarrs
jwodder Jan 14, 2022
b2711b2
Validation no longer needs to be skipped when uploading non-NWB files
jwodder Jan 17, 2022
335f6d7
Rewrite get_zarr_checksum() using dandischema
jwodder Jan 14, 2022
5e561e9
Classes for files & directories inside Zarrs
jwodder Jan 13, 2022
e3642af
Fixes
jwodder Jan 17, 2022
607455f
Fetching Zarr entry checksum & stats
jwodder Jan 17, 2022
e2ca267
Test uploading a Zarr containing an empty directory
jwodder Jan 19, 2022
e179a49
Test `upload --sync` with Zarrs
jwodder Jan 19, 2022
0a24a8c
Docstrings
jwodder Jan 19, 2022
8aa0c4c
First complete draft of Zarr download
jwodder Jan 19, 2022
a22dd95
More tests of ProgressCombiner
jwodder Jan 19, 2022
5aa819d
Fixes
jwodder Jan 19, 2022
a3d3479
Catch an error case
jwodder Jan 20, 2022
4fa2395
Delete extra files in a local Zarr after downloading
jwodder Jan 20, 2022
8072d5d
Checksum Zarrs after downloading
jwodder Jan 20, 2022
f85b392
Make the BaseRemoteAsset download methods error if called on a Zarr
jwodder Jan 20, 2022
99ab41a
Add versionadded:: directives
jwodder Jan 20, 2022
4832383
Make `dandi validate` support Zarrs
jwodder Jan 20, 2022
3c60086
Update `dandi download` and `dandi validate` docs
jwodder Jan 20, 2022
c12f72b
`dandi download --sync` with Zarrs
jwodder Jan 20, 2022
0da8d93
`get_digest()` → `get_raw_digest()`; `get_etag()` → `get_digest()`
jwodder Jan 21, 2022
c0a32ef
Replace `is_blob()` and `is_zarr()` with an `asset_type` property & enum
jwodder Jan 21, 2022
a60d5d4
Type-annotate a utility function
jwodder Jan 24, 2022
0caa08f
Update version in which Zarr support will be released
jwodder Jan 24, 2022
b3a01de
`BaseRemoteAsset.digest_type`
jwodder Jan 25, 2022
2c6279e
Add more information to an error message
jwodder Jan 25, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions dandi/cli/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,25 @@
# Aux common functionality


class IntColonInt(click.ParamType):
name = "int:int"

def convert(self, value, param, ctx):
if isinstance(value, str):
v1, colon, v2 = value.partition(":")
try:
v1 = int(v1)
v2 = int(v2) if colon else None
except ValueError:
self.fail("Value must be of the form `N[:M]`", param, ctx)
return (v1, v2)
else:
return value
Comment thread
yarikoptic marked this conversation as resolved.

def get_metavar(self, param):
return "N[:M]"


# ???: could make them always available but hidden
# via hidden=True.
def devel_option(*args, **kwargs):
Expand Down
10 changes: 6 additions & 4 deletions dandi/cli/cmd_download.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import click

from .base import instance_option, map_to_click_exceptions
from .base import IntColonInt, instance_option, map_to_click_exceptions
from ..consts import known_instances, known_instances_rev
from ..dandiarchive import _dandi_url_parser, parse_dandi_url

Expand Down Expand Up @@ -70,8 +70,9 @@ def get_metavar(self, param):
@click.option(
"-J",
"--jobs",
help="Number of parallel download jobs.",
default=6, # TODO: come up with smart auto-scaling etc
type=IntColonInt(),
help="Number of parallel download jobs and, optionally number of subjobs per Zarr asset",
default="6", # TODO: come up with smart auto-scaling etc
show_default=True,
)
@click.option(
Expand Down Expand Up @@ -141,7 +142,8 @@ def download(
output_dir,
existing=existing,
format=format,
jobs=jobs,
jobs=jobs[0],
jobs_per_zarr=jobs[1],
get_metadata="dandiset.yaml" in download_types,
get_assets="assets" in download_types,
sync=sync,
Expand Down
4 changes: 2 additions & 2 deletions dandi/cli/cmd_ls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from .base import devel_option, lgr, map_to_click_exceptions
from ..dandiarchive import DandisetURL, _dandi_url_parser, parse_dandi_url
from ..misctypes import Digest
from ..utils import is_url

# TODO: all the recursion options etc
Expand Down Expand Up @@ -354,8 +355,7 @@ def fn():
rec = nwb2asset(
path,
schema_version=schema,
digest=digest,
digest_type="dandi_etag",
digest=Digest.dandi_etag(digest),
).json_dict()
else:
rec = get_metadata(path)
Expand Down
26 changes: 4 additions & 22 deletions dandi/cli/cmd_upload.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,14 @@
import click

from .base import (
IntColonInt,
devel_debug_option,
devel_option,
instance_option,
map_to_click_exceptions,
)


class IntColonInt(click.ParamType):
name = "int:int"

def convert(self, value, param, ctx):
if isinstance(value, str):
v1, colon, v2 = value.partition(":")
try:
v1 = int(v1)
v2 = int(v2) if colon else None
except ValueError:
self.fail("Value must be of the form `N[:M]`", param, ctx)
return (v1, v2)
else:
return value

def get_metavar(self, param):
return "N[:M]"


@click.command()
# @dandiset_path_option(
# help="Top directory (local) of the dandiset. Files will be uploaded with "
Expand Down Expand Up @@ -102,9 +84,9 @@ def upload(
Local Dandiset should pass validation. For that, the assets should first
be organized using the `dandi organize` command.

By default all .nwb files in the Dandiset (excluding directories starting
with a period) will be considered for the upload. You can point to
specific files you would like to validate and have uploaded.
By default all .nwb, .zarr, and .ngff assets in the Dandiset (ignoring
directories starting with a period) will be considered for the upload. You
can point to specific files you would like to validate and have uploaded.
"""
from ..upload import upload

Expand Down
4 changes: 2 additions & 2 deletions dandi/cli/cmd_validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
@devel_debug_option()
@map_to_click_exceptions
def validate(paths, schema=None, devel_debug=False, allow_any_path=False):
"""Validate files for NWB (and DANDI) compliance.
"""Validate files for NWB and DANDI compliance.

Exits with non-0 exit code if any file is not compliant.
"""
Expand All @@ -43,7 +43,7 @@ def validate(paths, schema=None, devel_debug=False, allow_any_path=False):
all_files_errors = {}
nfiles = 0
for path, errors in validate_(
paths,
*paths,
Comment thread
yarikoptic marked this conversation as resolved.
schema_version=schema,
devel_debug=devel_debug,
allow_any_path=allow_any_path,
Expand Down
7 changes: 7 additions & 0 deletions dandi/cli/tests/test_download.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def test_download_defaults(mocker):
existing="error",
format="pyout",
jobs=6,
jobs_per_zarr=None,
Comment thread
yarikoptic marked this conversation as resolved.
get_metadata=True,
get_assets=True,
sync=False,
Expand All @@ -34,6 +35,7 @@ def test_download_all_types(mocker):
existing="error",
format="pyout",
jobs=6,
jobs_per_zarr=None,
get_metadata=True,
get_assets=True,
sync=False,
Expand All @@ -50,6 +52,7 @@ def test_download_metadata_only(mocker):
existing="error",
format="pyout",
jobs=6,
jobs_per_zarr=None,
get_metadata=True,
get_assets=False,
sync=False,
Expand All @@ -66,6 +69,7 @@ def test_download_assets_only(mocker):
existing="error",
format="pyout",
jobs=6,
jobs_per_zarr=None,
get_metadata=False,
get_assets=True,
sync=False,
Expand Down Expand Up @@ -94,6 +98,7 @@ def test_download_gui_instance_in_dandiset(mocker):
existing="error",
format="pyout",
jobs=6,
jobs_per_zarr=None,
get_metadata=True,
get_assets=True,
sync=False,
Expand All @@ -113,6 +118,7 @@ def test_download_api_instance_in_dandiset(mocker):
existing="error",
format="pyout",
jobs=6,
jobs_per_zarr=None,
get_metadata=True,
get_assets=True,
sync=False,
Expand All @@ -136,6 +142,7 @@ def test_download_url_instance_match(mocker):
existing="error",
format="pyout",
jobs=6,
jobs_per_zarr=None,
get_metadata=True,
get_assets=True,
sync=False,
Expand Down
9 changes: 9 additions & 0 deletions dandi/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,12 @@ class EmbargoStatus(Enum):
#: HTTP response status codes that should always be retried (until we run out
#: of retries)
RETRY_STATUSES = (500, 502, 503, 504)

#: Maximum allowed depth of a Zarr directory tree
MAX_ZARR_DEPTH = 5

#: MIME type assigned to & used to identify Zarr assets
ZARR_MIME_TYPE = "application/x-zarr"

#: Maximum number of Zarr directory entries to upload at once
ZARR_UPLOAD_BATCH_SIZE = 255
Loading