Skip to content

Commit a2740fc

Browse files
authored
Feature/error typology (#183)
* Exceptions * Some tests, and make InvalidRequest compatible with no message * QA
1 parent 2bee174 commit a2740fc

File tree

10 files changed

+89
-22
lines changed

10 files changed

+89
-22
lines changed

cads_adaptors/adaptors/cadsobs/adaptor.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from cads_adaptors.adaptors.cadsobs.api_client import CadsobsApiClient
66
from cads_adaptors.adaptors.cds import AbstractCdsAdaptor
7+
from cads_adaptors.exceptions import CadsObsRuntimeError
78

89
logger = logging.getLogger(__name__)
910

@@ -119,7 +120,7 @@ def handle_sources_list(self, dataset_source: list | str) -> str:
119120
"Asking for more than one observation_types in the same"
120121
"request is currently unsupported."
121122
)
122-
raise RuntimeError(
123+
raise CadsObsRuntimeError(
123124
"Asking for more than one observation_types in the same"
124125
"request is currently unsupported."
125126
)

cads_adaptors/adaptors/cadsobs/retrieve.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
_get_char_sizes,
1313
_get_output_path,
1414
)
15+
from cads_adaptors.exceptions import CadsObsRuntimeError
1516

1617
logger = logging.getLogger(__name__)
1718

@@ -51,7 +52,7 @@ def retrieve_data(
5152
)
5253
# Check if the resulting file is empty
5354
if len(oncobj.variables) == 0 or len(oncobj.variables["report_timestamp"]) == 0:
54-
raise RuntimeError(
55+
raise CadsObsRuntimeError(
5556
"No data was found, try a different parameter combination."
5657
)
5758
# Add atributes

cads_adaptors/adaptors/cadsobs/utils.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
RetrieveFormat,
1616
RetrieveParams,
1717
)
18+
from cads_adaptors.exceptions import CadsObsRuntimeError
1819

1920
logger = logging.getLogger(__name__)
2021
MAX_NUMBER_OF_GROUPS = 10
@@ -197,7 +198,7 @@ def _get_param_name_in_data(retrieved_dataset: h5netcdf.File, param_name: str) -
197198
else:
198199
param_name_in_data = f"{coord}|station_configuration"
199200
case _:
200-
raise RuntimeError(f"Unknown parameter name {param_name}")
201+
raise CadsObsRuntimeError(f"Unknown parameter name {param_name}")
201202
return param_name_in_data
202203

203204

@@ -332,7 +333,7 @@ def _get_code_mapping(
332333
elif isinstance(incobj, xarray.Dataset):
333334
attrs = incobj["observed_variable"].attrs
334335
else:
335-
raise RuntimeError("Unsupported input type")
336+
raise CadsObsRuntimeError("Unsupported input type")
336337
if inverse:
337338
mapping = {c: v for v, c in zip(attrs["labels"], attrs["codes"])}
338339
else:

cads_adaptors/adaptors/mars.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from typing import Any, BinaryIO, Union
33

44
from cads_adaptors.adaptors import Context, Request, cds
5+
from cads_adaptors.exceptions import MarsNoDataError, MarsRuntimeError, MarsSystemError
56
from cads_adaptors.tools import adaptor_tools
67
from cads_adaptors.tools.date_tools import implement_embargo
78
from cads_adaptors.tools.general import ensure_list, split_requests_on_keys
@@ -40,7 +41,7 @@ def get_mars_server_list(config) -> list[str]:
4041
with open(mars_server_list) as f:
4142
mars_servers = f.read().splitlines()
4243
else:
43-
raise SystemError(
44+
raise MarsSystemError(
4445
"MARS servers cannot be found, this is an error at the system level."
4546
)
4647
return mars_servers
@@ -94,7 +95,7 @@ def execute_mars(
9495
context.add_user_visible_error(message=error_message)
9596

9697
error_message += f"Exception: {reply.error}\n"
97-
raise RuntimeError(error_message)
98+
raise MarsRuntimeError(error_message)
9899

99100
if not os.path.getsize(target):
100101
error_message = (
@@ -104,7 +105,7 @@ def execute_mars(
104105
context.add_user_visible_error(
105106
message=error_message,
106107
)
107-
raise RuntimeError(error_message)
108+
raise MarsNoDataError(error_message)
108109

109110
return target
110111

cads_adaptors/adaptors/multi.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from cads_adaptors import AbstractCdsAdaptor, mapping
55
from cads_adaptors.adaptors import Request
6+
from cads_adaptors.exceptions import MultiAdaptorNoDataError
67
from cads_adaptors.tools.general import ensure_list
78

89

@@ -109,7 +110,7 @@ def retrieve(self, request: Request):
109110
results += this_result
110111

111112
if len(results) == 0:
112-
raise RuntimeError(
113+
raise MultiAdaptorNoDataError(
113114
"MultiAdaptor returned no results, the error logs of the sub-adaptors is as follows:\n"
114115
f"{exception_logs}"
115116
)

cads_adaptors/adaptors/roocs/__init__.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from cads_adaptors import mapping
66
from cads_adaptors.adaptors.cds import AbstractCdsAdaptor, Request
7+
from cads_adaptors.exceptions import RoocsRuntimeError, RoocsValueError
78

89
ROOK_URL = "http://compute.mips.copernicus-climate.eu/wps"
910

@@ -33,7 +34,7 @@ def retrieve(self, request: Request) -> BinaryIO:
3334
try:
3435
urls = response.download_urls()
3536
except Exception:
36-
raise Exception(response.status)
37+
raise RoocsRuntimeError(response.status)
3738
urls += [response.provenance(), response.provenance_image()]
3839

3940
paths = url_tools.try_download(urls, context=self.context)
@@ -143,7 +144,7 @@ def find_facets(self, request):
143144
matched_facets.append(raw_candidate)
144145

145146
if not matched_facets:
146-
raise ValueError(f"No data found for request {request}")
147+
raise RoocsValueError(f"No data found for request {request}")
147148

148149
# raise ValueError(str(raw_candidate) + " | " + str(self.facets_order))
149150
return [

cads_adaptors/adaptors/roocs/operators.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import types
33
import typing as T
44

5+
from cads_adaptors.exceptions import RoocsValueError
6+
57
DEFAULT_DATE_FORMAT = "%Y-%m-%d"
68

79

@@ -141,7 +143,7 @@ def area(self):
141143
extents = extents.split(delimiter)
142144
break
143145
else:
144-
raise ValueError(f"invalid area argument: {extents}")
146+
raise RoocsValueError(f"invalid area argument: {extents}")
145147

146148
# reorder extents from MARS-style (NWSE) to rooki-style (WSEN)
147149
extents_order = [1, 2, 3, 0]

cads_adaptors/exceptions.py

+37-3
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,43 @@ class ParameterError(TypeError):
2222
class InvalidRequest(Exception):
2323
"""Raised when an invalid request is sent to the adaptor."""
2424

25-
def __init__(self, msgs):
25+
def __init__(self, msgs: None | str | list[str] = None):
2626
if isinstance(msgs, str):
2727
msgs = [msgs]
28-
super().__init__("\n".join(msgs))
2928

30-
self.messages = deepcopy(msgs)
29+
if msgs is not None:
30+
super().__init__("\n".join(msgs))
31+
32+
self.messages = deepcopy(msgs)
33+
34+
35+
class MarsRuntimeError(RuntimeError):
36+
"""Raised when a MARS request fails."""
37+
38+
39+
class MarsNoDataError(InvalidRequest):
40+
"""Raised when a MARS request returns no data."""
41+
42+
43+
class MarsSystemError(SystemError):
44+
"""Raised when a MARS request fails due to a system error."""
45+
46+
47+
class UrlNoDataError(InvalidRequest):
48+
"""Raised when a MARS request returns no data."""
49+
50+
51+
class MultiAdaptorNoDataError(InvalidRequest):
52+
"""Raised when a MultiAdaptor request returns no data."""
53+
54+
55+
class CadsObsRuntimeError(RuntimeError):
56+
"""Raised when a CADS-observation repository request fails."""
57+
58+
59+
class RoocsRuntimeError(RuntimeError):
60+
"""Raised when a ROOCS request fails."""
61+
62+
63+
class RoocsValueError(ValueError):
64+
"""Raised when a ROOCS request fails due to a value error."""

cads_adaptors/tools/url_tools.py

+12-8
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from tqdm import tqdm
1313

1414
from cads_adaptors.adaptors import Context
15+
from cads_adaptors.exceptions import InvalidRequest, UrlNoDataError
1516
from cads_adaptors.tools import hcube_tools
1617

1718

@@ -66,17 +67,18 @@ def try_download(urls: List[str], context: Context, **kwargs) -> List[str]:
6667
if len(paths) == 0:
6768
context.add_user_visible_error(
6869
"Your request has not found any data, please check your selection.\n"
69-
"If you believe this to be a data store error, please contact user support."
70+
"This may be due to temporary connectivity issues with the source data.\n"
71+
"If this problem persists, please contact user support."
7072
)
71-
raise RuntimeError(
73+
raise UrlNoDataError(
7274
f"Request empty. No data found from the following URLs:"
7375
f"\n{yaml.safe_dump(urls, indent=2)} "
7476
)
7577
# TODO: raise a warning if len(paths)<len(urls). Need to check who sees this warning
7678
return paths
7779

7880

79-
# TODO use targzstream
81+
# TODO: remove unused function
8082
def download_zip_from_urls(
8183
urls: List[str],
8284
base_target: str,
@@ -94,7 +96,7 @@ def download_zip_from_urls(
9496
return target
9597

9698

97-
# TODO zipstream for archive creation
99+
# TODO: remove unused function
98100
def download_tgz_from_urls(
99101
urls: List[str],
100102
base_target: str,
@@ -112,27 +114,29 @@ def download_tgz_from_urls(
112114
return target
113115

114116

117+
# TODO: remove unused function
115118
def download_from_urls(
116119
urls: List[str],
117120
context: Context,
118-
data_format: str = "zip",
121+
download_format: str = "zip",
119122
) -> str:
120123
base_target = str(hash(tuple(urls)))
121124

122-
if data_format == "tgz":
125+
if download_format == "tgz":
123126
target = download_tgz_from_urls(
124127
urls=urls, base_target=base_target, context=context
125128
)
126-
elif data_format == "zip":
129+
elif download_format == "zip":
127130
target = download_zip_from_urls(
128131
urls=urls, base_target=base_target, context=context
129132
)
130133
else:
131-
raise ValueError(f"{data_format=} is not supported")
134+
raise InvalidRequest(f"Download format '{download_format}' is not supported")
132135

133136
return target
134137

135138

139+
# TODO: remove unused function
136140
def download_zip_from_urls_in_memory(
137141
urls: List[str], target: Optional[str] = None
138142
) -> str:

tests/test_05_exceptions.py

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import pytest
2+
3+
from cads_adaptors import exceptions
4+
5+
6+
@pytest.mark.parametrize(
7+
"test_exception",
8+
[
9+
exceptions.MarsNoDataError,
10+
exceptions.MarsRuntimeError,
11+
exceptions.MarsSystemError,
12+
exceptions.MultiAdaptorNoDataError,
13+
exceptions.UrlNoDataError,
14+
exceptions.RoocsRuntimeError,
15+
exceptions.RoocsValueError,
16+
exceptions.CadsObsRuntimeError,
17+
],
18+
)
19+
def test_exceptions_raise_as_expected(test_exception) -> None:
20+
with pytest.raises(test_exception):
21+
raise test_exception

0 commit comments

Comments
 (0)