Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BF: guard download_generator to not propagate errors #1008

Merged
merged 3 commits into from
Aug 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
25 changes: 23 additions & 2 deletions dandi/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
from .support.digests import get_digest, get_zarr_checksum
from .support.iterators import IteratorWithAggregation
from .support.pyout import naturalsize
from .support.typing import Literal
from .utils import (
abbrev_prompt,
ensure_datetime,
Expand Down Expand Up @@ -130,6 +131,7 @@ def download(
get_metadata=get_metadata,
get_assets=get_assets,
jobs_per_zarr=jobs_per_zarr,
on_error="yield" if format == "pyout" else "raise",
**kw,
)

Expand Down Expand Up @@ -207,6 +209,7 @@ def download_generator(
get_metadata: bool = True,
get_assets: bool = True,
jobs_per_zarr: Optional[int] = None,
on_error: Literal["raise", "yield"] = "raise",
) -> Iterator[dict]:
"""A generator for downloads of files, folders, or entire dandiset from DANDI
(as identified by URL)
Expand Down Expand Up @@ -309,13 +312,31 @@ def download_generator(
lock=lock,
)

# If exception is raised we might just raise it, or yield
# an error record
gen = {
"raise": _download_generator,
"yield": _download_generator_guard(path, _download_generator),
}[on_error]

if yield_generator_for_fields:
yield {"path": path, yield_generator_for_fields: _download_generator}
yield {"path": path, yield_generator_for_fields: gen}
else:
for resp in _download_generator:
for resp in gen:
yield dict(resp, path=path)


def _download_generator_guard(path: str, generator: Iterator[dict]) -> Iterator[dict]:
try:
yield from generator
except Exception as exc:
lgr.exception("Caught while downloading %s:", path)
yield {
"status": "error",
"message": str(exc.__class__.__name__),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure you don't want to include the error message via str(exc)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, let's do str

Suggested change
"message": str(exc.__class__.__name__),
"message": str(exc),

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went back on this since then it becomes "too verbose" with each message just repeating the known path and summary duplicating all those

$> dandi download DANDI:000029
PATH                                     SIZE     DONE    DONE% CHECKSUM STATUS    MESSAGE              
dandiset.yaml                                                            skipped   no change            
...64/sub-anm369964_behavior+ecephys.nwb                                 error     File './000029/sub...
...63/sub-anm369963_behavior+ecephys.nwb                                 error     File './000029/sub...
...62/sub-anm369962_behavior+ecephys.nwb                                 error     File './000029/sub...
sub-RAT123/sub-RAT123.nwb                                                error     File './000029/sub...
sample.txt                                                               error     File './000029/sam...
sample.json                                                              error     File './000029/sam...
Summary:                                 0 Bytes  0 Bytes                1 skipped 1 no change          
                                         +20.7 MB 0.00%                  6 error   1 File './000029/s...
                                                                                   1 File './000029/s...
                                                                                   1 File './000029/s...
                                                                                   1 File './000029/s...
                                                                                   1 File './000029/s...
                                                                                   1 File './000029/s...
2022-08-03 11:38:10,873 [    INFO] Logs saved in /home/yoh/.cache/dandi-cli/log/20220803153807Z-1022747.log
(dev3) 1 41191.....................................:Wed 03 Aug 2022 11:38:12 AM EDT:.
lena:/tmp
$> dandi download DANDI:000029
PATH                                             SIZE     DONE    DONE% CHECKSUM STATUS    MESSAGE                                                                          
dandiset.yaml                                                                    skipped   no change                                                                        
sub-anm369964/sub-anm369964_behavior+ecephys.nwb                                 error     File './000029/sub-anm369964/sub-anm369964_behavior+ecephys.nwb' already exists  
sub-anm369963/sub-anm369963_behavior+ecephys.nwb                                 error     File './000029/sub-anm369963/sub-anm369963_behavior+ecephys.nwb' already exists  
sub-anm369962/sub-anm369962_behavior+ecephys.nwb                                 error     File './000029/sub-anm369962/sub-anm369962_behavior+ecephys.nwb' already exists  
sub-RAT123/sub-RAT123.nwb                                                        error     File './000029/sub-RAT123/sub-RAT123.nwb' already exists                         
sample.txt                                                                       error     File './000029/sample.txt' already exists                                        
sample.json                                                                      error     File './000029/sample.json' already exists                                       
Summary:                                         0 Bytes  0 Bytes                1 skipped 1 no change                                                                      
                                                 +20.7 MB 0.00%                  6 error   1 File './000029/sub-anm369964/sub-anm369964_behavior+ecephys.nwb' already exists
                                                                                           1 File './000029/sub-anm369963/sub-anm369963_behavior+ecephys.nwb' already exists
                                                                                           1 File './000029/sub-anm369962/sub-anm369962_behavior+ecephys.nwb' already exists
                                                                                           1 File './000029/sub-RAT123/sub-RAT123.nwb' already exists                       
                                                                                           1 File './000029/sample.txt' already exists                                      
                                                                                           1 File './000029/sample.json' already exists     

with only reporting the class we have better depiction with details of exception present in the log:

$> dandi download DANDI:000029
PATH                                             SIZE     DONE    DONE% CHECKSUM STATUS    MESSAGE          
dandiset.yaml                                                                    skipped   no change        
sub-anm369964/sub-anm369964_behavior+ecephys.nwb                                 error     FileExistsError  
sub-anm369963/sub-anm369963_behavior+ecephys.nwb                                 error     FileExistsError  
sub-anm369962/sub-anm369962_behavior+ecephys.nwb                                 error     FileExistsError  
sub-RAT123/sub-RAT123.nwb                                                        error     FileExistsError  
sample.txt                                                                       error     FileExistsError  
sample.json                                                                      error     FileExistsError  
Summary:                                         0 Bytes  0 Bytes                1 skipped 1 no change      
                                                 +20.7 MB 0.00%                  6 error   6 FileExistsError
2022-08-03 11:41:08,828 [    INFO] Logs saved in /home/yoh/.cache/dandi-cli/log/20220803154105Z-1024172.log

}


class ItemsSummary:
"""A helper "structure" to accumulate information about assets to be downloaded

Expand Down
6 changes: 1 addition & 5 deletions dandi/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import os.path as op
from pathlib import Path
import re
import sys
from typing import (
TYPE_CHECKING,
Any,
Expand Down Expand Up @@ -664,10 +663,7 @@ def extract_field(field: str, metadata: dict) -> Any:


if TYPE_CHECKING:
if sys.version_info >= (3, 8):
from typing import TypedDict
else:
from typing_extensions import TypedDict
from .support.typing import TypedDict

class Neurodatum(TypedDict):
module: str
Expand Down
6 changes: 6 additions & 0 deletions dandi/support/typing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import sys

if sys.version_info >= (3, 8):
from typing import Literal, TypedDict # noqa: F401
else:
from typing_extensions import Literal, TypedDict # noqa: F401
10 changes: 4 additions & 6 deletions dandi/tests/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import re
import shutil
from subprocess import DEVNULL, check_output, run
import sys
import tempfile
from time import sleep
from typing import TYPE_CHECKING, Any, Callable, Dict, Iterator, List, Optional, Union
Expand Down Expand Up @@ -179,10 +178,7 @@ def organized_nwb_dir2(


if TYPE_CHECKING:
if sys.version_info >= (3, 8):
from typing import Literal
else:
from typing_extensions import Literal
from ..support.typing import Literal

Scope = Union[
Literal["session"],
Expand Down Expand Up @@ -448,7 +444,9 @@ def bids_dandiset(new_dandiset: SampleDandiset, bids_examples: str) -> SampleDan


@pytest.fixture()
def bids_dandiset_invalid(new_dandiset: SampleDandiset, bids_examples: str) -> SampleDandiset:
def bids_dandiset_invalid(
new_dandiset: SampleDandiset, bids_examples: str
) -> SampleDandiset:
yarikoptic marked this conversation as resolved.
Show resolved Hide resolved
copytree(
os.path.join(bids_examples, "invalid_pet001"),
str(new_dandiset.dspath) + "/",
Expand Down
13 changes: 10 additions & 3 deletions dandi/tests/test_download.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
"https://dandiarchive.org/dandiset/000027/draft",
],
)
def test_download_000027(url: str, tmp_path: Path) -> None:
def test_download_000027(
url: str, tmp_path: Path, capsys: pytest.CaptureFixture
) -> None:
ret = download(url, tmp_path) # type: ignore[func-returns-value]
assert not ret # we return nothing ATM, might want to "generate"
dsdir = tmp_path / "000027"
Expand All @@ -48,9 +50,14 @@ def test_download_000027(url: str, tmp_path: Path) -> None:
Digester(["md5"])(dsdir / "sub-RAT123" / "sub-RAT123.nwb")["md5"]
== "33318fd510094e4304868b4a481d4a5a"
)
# redownload - since already exist there should be an exception
# redownload - since already exist there should be an exception if we are
# not using pyout
with pytest.raises(FileExistsError):
download(url, tmp_path)
download(url, tmp_path, format="debug")
assert "FileExistsError" not in capsys.readouterr().out
# but no exception is raised, and rather it gets output to pyout otherwise
download(url, tmp_path)
assert "FileExistsError" in capsys.readouterr().out

# TODO: somehow get that status report about what was downloaded and what not
download(url, tmp_path, existing="skip") # TODO: check that skipped
Expand Down
6 changes: 1 addition & 5 deletions dandi/upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import os.path
from pathlib import Path
import re
import sys
import time
from typing import TYPE_CHECKING, Any, Dict, Iterator, List, Optional, Set, Tuple, Union

Expand All @@ -26,10 +25,7 @@
from .utils import ensure_datetime, get_instance, pluralize

if TYPE_CHECKING:
if sys.version_info >= (3, 8):
from typing import TypedDict
else:
from typing_extensions import TypedDict
from .support.typing import TypedDict

class Uploaded(TypedDict):
size: int
Expand Down