diff --git a/dandi/dandiapi.py b/dandi/dandiapi.py index de5947d09..9a5e625c3 100644 --- a/dandi/dandiapi.py +++ b/dandi/dandiapi.py @@ -447,7 +447,11 @@ def __init__( dandi_instance = get_instance(instance_name) api_url = dandi_instance.api elif dandi_instance is not None: - raise ValueError("api_url and dandi_instance are mutually exclusive") + raise ValueError( + "api_url and dandi_instance are mutually exclusive. " + "Use either 'api_url' to specify a custom API URL, " + "or 'dandi_instance' to use a registered DANDI instance, but not both." + ) else: dandi_instance = get_instance(api_url) super().__init__(api_url) @@ -574,7 +578,10 @@ def get_dandiset( self, self.get(f"/dandisets/{dandiset_id}/") ) except HTTP404Error: - raise NotFoundError(f"No such Dandiset: {dandiset_id!r}") + raise NotFoundError( + f"No such Dandiset: {dandiset_id!r}. " + "Verify the Dandiset ID is correct and that you have access. " + ) if version_id is not None and version_id != d.version_id: if version_id == DRAFT: return d.for_version(d.draft_version) @@ -744,7 +751,11 @@ def get_asset(self, asset_id: str) -> BaseRemoteAsset: try: info = self.get(f"/assets/{asset_id}/info/") except HTTP404Error: - raise NotFoundError(f"No such asset: {asset_id!r}") + raise NotFoundError( + f"No such asset: {asset_id!r}. " + "Verify the asset ID is correct. " + "Use 'dandi ls' to list available assets." + ) metadata = info.pop("metadata", None) return BaseRemoteAsset.from_base_data(self, info, metadata) @@ -1318,7 +1329,11 @@ def get_asset_by_path(self, path: str) -> RemoteAsset: a for a in self.get_assets_with_path_prefix(path) if a.path == path ) except ValueError: - raise NotFoundError(f"No asset at path {path!r}") + raise NotFoundError( + f"No asset at path {path!r} in version {self.version_id}. " + "Verify the path is correct and the asset exists in this version. " + "Use 'dandi ls' to list available assets." + ) else: return asset diff --git a/dandi/tests/test_dandiarchive.py b/dandi/tests/test_dandiarchive.py index cc5d988a4..e17b3f099 100644 --- a/dandi/tests/test_dandiarchive.py +++ b/dandi/tests/test_dandiarchive.py @@ -588,11 +588,17 @@ def test_get_nonexistent_dandiset( parsed_url.get_dandiset(client) # No error with pytest.raises(NotFoundError) as excinfo: parsed_url.get_dandiset(client, lazy=False) - assert str(excinfo.value) == "No such Dandiset: '999999'" + assert str(excinfo.value) == ( + "No such Dandiset: '999999'. " + "Verify the Dandiset ID is correct and that you have access. " + ) assert list(parsed_url.get_assets(client)) == [] with pytest.raises(NotFoundError) as excinfo: next(parsed_url.get_assets(client, strict=True)) - assert str(excinfo.value) == "No such Dandiset: '999999'" + assert str(excinfo.value) == ( + "No such Dandiset: '999999'. " + "Verify the Dandiset ID is correct and that you have access. " + ) @pytest.mark.parametrize("version", ["draft", "0.999999.9999"]) @@ -608,7 +614,10 @@ def test_get_nonexistent_dandiset_asset_id( assert list(parsed_url.get_assets(client)) == [] with pytest.raises(NotFoundError) as excinfo: next(parsed_url.get_assets(client, strict=True)) - assert str(excinfo.value) == "No such Dandiset: '999999'" + assert str(excinfo.value) == ( + "No such Dandiset: '999999'. " + "Verify the Dandiset ID is correct and that you have access. " + ) def test_get_dandiset_nonexistent_asset_id(text_dandiset: SampleDandiset) -> None: @@ -635,7 +644,11 @@ def test_get_nonexistent_asset_id(local_dandi_api: DandiAPI) -> None: assert list(parsed_url.get_assets(client)) == [] with pytest.raises(NotFoundError) as excinfo: next(parsed_url.get_assets(client, strict=True)) - assert str(excinfo.value) == "No such asset: '00000000-0000-0000-0000-000000000000'" + assert str(excinfo.value) == ( + "No such asset: '00000000-0000-0000-0000-000000000000'. " + "Verify the asset ID is correct. " + "Use 'dandi ls' to list available assets." + ) @pytest.mark.parametrize("version_suffix", ["", "@draft", "@0.999999.9999"]) @@ -648,7 +661,10 @@ def test_get_nonexistent_dandiset_asset_path( assert list(parsed_url.get_assets(client)) == [] with pytest.raises(NotFoundError) as excinfo: next(parsed_url.get_assets(client, strict=True)) - assert str(excinfo.value) == "No such Dandiset: '999999'" + assert str(excinfo.value) == ( + "No such Dandiset: '999999'. " + "Verify the Dandiset ID is correct and that you have access. " + ) def test_get_nonexistent_asset_path(text_dandiset: SampleDandiset) -> None: @@ -661,7 +677,11 @@ def test_get_nonexistent_asset_path(text_dandiset: SampleDandiset) -> None: assert list(parsed_url.get_assets(client)) == [] with pytest.raises(NotFoundError) as excinfo: next(parsed_url.get_assets(client, strict=True)) - assert str(excinfo.value) == "No asset at path 'does/not/exist'" + assert str(excinfo.value) == ( + "No asset at path 'does/not/exist' in version draft. " + "Verify the path is correct and the asset exists in this version. " + "Use 'dandi ls' to list available assets." + ) @pytest.mark.parametrize("version_suffix", ["", "@draft", "@0.999999.9999"]) @@ -677,7 +697,10 @@ def test_get_nonexistent_dandiset_asset_folder( assert list(parsed_url.get_assets(client)) == [] with pytest.raises(NotFoundError) as excinfo: next(parsed_url.get_assets(client, strict=True)) - assert str(excinfo.value) == "No such Dandiset: '999999'" + assert str(excinfo.value) == ( + "No such Dandiset: '999999'. " + "Verify the Dandiset ID is correct and that you have access. " + ) def test_get_nonexistent_asset_folder(text_dandiset: SampleDandiset) -> None: @@ -706,7 +729,10 @@ def test_get_nonexistent_dandiset_asset_prefix( assert list(parsed_url.get_assets(client)) == [] with pytest.raises(NotFoundError) as excinfo: next(parsed_url.get_assets(client, strict=True)) - assert str(excinfo.value) == "No such Dandiset: '999999'" + assert str(excinfo.value) == ( + "No such Dandiset: '999999'. " + "Verify the Dandiset ID is correct and that you have access. " + ) def test_get_nonexistent_asset_prefix(text_dandiset: SampleDandiset) -> None: diff --git a/dandi/tests/test_delete.py b/dandi/tests/test_delete.py index 0bd0d2b9d..9a6ff8dd1 100644 --- a/dandi/tests/test_delete.py +++ b/dandi/tests/test_delete.py @@ -252,7 +252,10 @@ def test_delete_nonexistent_dandiset( devel_debug=True, force=True, ) - assert str(excinfo.value) == "No such Dandiset: '999999'" + assert str(excinfo.value) == ( + "No such Dandiset: '999999'. " + "Verify the Dandiset ID is correct and that you have access. " + ) delete_spy.assert_not_called()