freva-client 2508.0.0__py3-none-any.whl → 2509.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of freva-client might be problematic. Click here for more details.
- freva_client/__init__.py +2 -1
- freva_client/auth.py +21 -12
- freva_client/cli/auth_cli.py +6 -0
- freva_client/cli/cli_utils.py +38 -1
- freva_client/cli/databrowser_cli.py +232 -32
- freva_client/query.py +255 -38
- freva_client/utils/auth_utils.py +38 -0
- freva_client/utils/databrowser_utils.py +118 -32
- {freva_client-2508.0.0.data → freva_client-2509.0.0.data}/data/share/freva/freva.toml +5 -0
- {freva_client-2508.0.0.dist-info → freva_client-2509.0.0.dist-info}/METADATA +1 -1
- freva_client-2509.0.0.dist-info/RECORD +20 -0
- freva_client-2508.0.0.dist-info/RECORD +0 -20
- {freva_client-2508.0.0.dist-info → freva_client-2509.0.0.dist-info}/WHEEL +0 -0
- {freva_client-2508.0.0.dist-info → freva_client-2509.0.0.dist-info}/entry_points.txt +0 -0
|
@@ -40,15 +40,50 @@ class Config:
|
|
|
40
40
|
self,
|
|
41
41
|
host: Optional[str] = None,
|
|
42
42
|
uniq_key: Literal["file", "uri"] = "file",
|
|
43
|
-
flavour: str =
|
|
43
|
+
flavour: Optional[str] = None,
|
|
44
44
|
) -> None:
|
|
45
45
|
self.databrowser_url = f"{self.get_api_url(host)}/databrowser"
|
|
46
46
|
self.auth_url = f"{self.get_api_url(host)}/auth/v2"
|
|
47
|
+
self.get_api_main_url = self.get_api_url(host)
|
|
47
48
|
self.uniq_key = uniq_key
|
|
48
|
-
self.
|
|
49
|
+
self.flavour = self.get_flavour(flavour)
|
|
49
50
|
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
@cached_property
|
|
52
|
+
def validate_server(self) -> bool:
|
|
53
|
+
"""Ping the databrowser to check if it is reachable."""
|
|
54
|
+
try:
|
|
55
|
+
res = requests.get(f"{self.get_api_main_url}/ping", timeout=15)
|
|
56
|
+
return res.status_code == 200
|
|
57
|
+
except Exception as e:
|
|
58
|
+
raise ValueError(
|
|
59
|
+
f"Could not connect to {self.databrowser_url}: {e}"
|
|
60
|
+
) from None
|
|
61
|
+
|
|
62
|
+
def get_flavour(self, flavour: Optional[str]) -> str:
|
|
63
|
+
"""Get the current flavour."""
|
|
64
|
+
if flavour:
|
|
65
|
+
return flavour
|
|
66
|
+
else:
|
|
67
|
+
try:
|
|
68
|
+
config_flavour = self._get_databrowser_params_from_config().get(
|
|
69
|
+
"flavour", ""
|
|
70
|
+
)
|
|
71
|
+
return config_flavour or "freva"
|
|
72
|
+
except ValueError:
|
|
73
|
+
return "freva"
|
|
74
|
+
|
|
75
|
+
def _read_ini(self, path: Path) -> Dict[str, str]:
|
|
76
|
+
"""Read an ini file.
|
|
77
|
+
|
|
78
|
+
Parameters
|
|
79
|
+
----------
|
|
80
|
+
path : Path
|
|
81
|
+
Path to the ini configuration file.
|
|
82
|
+
Returns
|
|
83
|
+
-------
|
|
84
|
+
Dict[str, str]
|
|
85
|
+
Dictionary with the configuration.
|
|
86
|
+
"""
|
|
52
87
|
ini_parser = ConfigParser(interpolation=ExtendedInterpolation())
|
|
53
88
|
ini_parser.read_string(path.read_text())
|
|
54
89
|
config = ini_parser["evaluation_system"]
|
|
@@ -59,41 +94,99 @@ class Config:
|
|
|
59
94
|
port = port or config.get("databrowser.port", "")
|
|
60
95
|
if port:
|
|
61
96
|
host = f"{host}:{port}"
|
|
62
|
-
|
|
97
|
+
host = f"{scheme}://{host}"
|
|
98
|
+
flavour = config.get("databrowser.default_flavour", "")
|
|
99
|
+
return {
|
|
100
|
+
"host": host,
|
|
101
|
+
"flavour": flavour,
|
|
102
|
+
}
|
|
63
103
|
|
|
64
|
-
def _read_toml(self, path: Path) -> str:
|
|
65
|
-
"""Read a new style toml config file.
|
|
104
|
+
def _read_toml(self, path: Path) -> Dict[str, str]:
|
|
105
|
+
"""Read a new style toml config file.
|
|
106
|
+
|
|
107
|
+
Parameters
|
|
108
|
+
----------
|
|
109
|
+
path : Path
|
|
110
|
+
Path to the toml configuration file.
|
|
111
|
+
Returns
|
|
112
|
+
-------
|
|
113
|
+
Dict[str, str]
|
|
114
|
+
Dictionary with the configuration.
|
|
115
|
+
"""
|
|
66
116
|
try:
|
|
67
117
|
config = tomli.loads(path.read_text()).get("freva", {})
|
|
68
|
-
scheme, host = self._split_url(cast(str, config
|
|
118
|
+
scheme, host = self._split_url(cast(str, config.get("host", "")))
|
|
119
|
+
flavour = config.get("default_flavour", "")
|
|
69
120
|
except (tomli.TOMLDecodeError, KeyError):
|
|
70
|
-
return
|
|
121
|
+
return {}
|
|
71
122
|
host, _, port = host.partition(":")
|
|
72
123
|
if port:
|
|
73
124
|
host = f"{host}:{port}"
|
|
74
|
-
|
|
125
|
+
host = f"{scheme}://{host}"
|
|
126
|
+
return {
|
|
127
|
+
"host": host,
|
|
128
|
+
"flavour": flavour,
|
|
129
|
+
}
|
|
75
130
|
|
|
76
|
-
def _read_config(
|
|
77
|
-
|
|
131
|
+
def _read_config(
|
|
132
|
+
self, path: Path, file_type: Literal["toml", "ini"]
|
|
133
|
+
) -> Dict[str, str]:
|
|
134
|
+
"""Read the configuration.
|
|
135
|
+
|
|
136
|
+
Parameters
|
|
137
|
+
----------
|
|
138
|
+
path : Path
|
|
139
|
+
Path to the configuration file.
|
|
140
|
+
file_type : Literal["toml", "ini"]
|
|
141
|
+
Type of the configuration file.
|
|
142
|
+
Returns
|
|
143
|
+
-------
|
|
144
|
+
Dict[str, str]
|
|
145
|
+
Dictionary with the configuration.
|
|
146
|
+
"""
|
|
78
147
|
data_types = {"toml": self._read_toml, "ini": self._read_ini}
|
|
79
148
|
try:
|
|
80
149
|
return data_types[file_type](path)
|
|
81
150
|
except KeyError:
|
|
82
151
|
pass
|
|
83
|
-
return
|
|
152
|
+
return {}
|
|
153
|
+
|
|
154
|
+
@cached_property
|
|
155
|
+
def _get_headers(self) -> Optional[Dict[str, str]]:
|
|
156
|
+
"""Get the headers for requests."""
|
|
157
|
+
from freva_client.auth import Auth
|
|
158
|
+
|
|
159
|
+
from .auth_utils import load_token
|
|
160
|
+
auth = Auth()
|
|
161
|
+
token = auth._auth_token or load_token(auth.token_file)
|
|
162
|
+
if token and "access_token" in token:
|
|
163
|
+
return {"Authorization": f"Bearer {token['access_token']}"}
|
|
164
|
+
return None
|
|
84
165
|
|
|
85
166
|
@cached_property
|
|
86
167
|
def overview(self) -> Dict[str, Any]:
|
|
87
|
-
"""Get an overview of
|
|
168
|
+
"""Get an overview of all databrowser flavours
|
|
169
|
+
and search keys including custom flavours."""
|
|
170
|
+
headers = self._get_headers
|
|
88
171
|
try:
|
|
89
|
-
res = requests.get(
|
|
172
|
+
res = requests.get(
|
|
173
|
+
f"{self.databrowser_url}/overview",
|
|
174
|
+
headers=headers,
|
|
175
|
+
timeout=15
|
|
176
|
+
)
|
|
177
|
+
data = cast(Dict[str, Any], res.json())
|
|
178
|
+
if not headers:
|
|
179
|
+
data["Note"] = (
|
|
180
|
+
"Displaying only global flavours. "
|
|
181
|
+
"Authenticate to see custom user flavours as well."
|
|
182
|
+
)
|
|
183
|
+
return data
|
|
90
184
|
except requests.exceptions.ConnectionError:
|
|
91
185
|
raise ValueError(
|
|
92
186
|
f"Could not connect to {self.databrowser_url}"
|
|
93
187
|
) from None
|
|
94
|
-
return cast(Dict[str, Any], res.json())
|
|
95
188
|
|
|
96
|
-
def
|
|
189
|
+
def _get_databrowser_params_from_config(self) -> Dict[str, str]:
|
|
97
190
|
"""Get the config file order."""
|
|
98
191
|
|
|
99
192
|
eval_conf = self.get_dirs(user=False) / "evaluation_system.conf"
|
|
@@ -111,26 +204,20 @@ class Config:
|
|
|
111
204
|
}
|
|
112
205
|
for config_path, config_type in paths.items():
|
|
113
206
|
if config_path.is_file():
|
|
114
|
-
|
|
207
|
+
config_data = self._read_config(config_path, config_type)
|
|
208
|
+
host = config_data.get("host", "")
|
|
209
|
+
flavour = config_data.get("flavour", "")
|
|
115
210
|
if host:
|
|
116
|
-
return
|
|
211
|
+
return {
|
|
212
|
+
"host": host,
|
|
213
|
+
"flavour": flavour
|
|
214
|
+
}
|
|
117
215
|
raise ValueError(
|
|
118
216
|
"No databrowser host configured, please use a"
|
|
119
217
|
" configuration defining a databrowser host or"
|
|
120
218
|
" set a host name using the `host` key"
|
|
121
219
|
)
|
|
122
220
|
|
|
123
|
-
@cached_property
|
|
124
|
-
def flavour(self) -> str:
|
|
125
|
-
"""Get the flavour."""
|
|
126
|
-
flavours = self.overview.get("flavours", [])
|
|
127
|
-
if self._flavour not in flavours:
|
|
128
|
-
raise ValueError(
|
|
129
|
-
f"Search {self._flavour} not available, select from"
|
|
130
|
-
f" {','.join(flavours)}"
|
|
131
|
-
)
|
|
132
|
-
return self._flavour
|
|
133
|
-
|
|
134
221
|
@property
|
|
135
222
|
def search_url(self) -> str:
|
|
136
223
|
"""Define the data search endpoint."""
|
|
@@ -172,7 +259,7 @@ class Config:
|
|
|
172
259
|
|
|
173
260
|
def get_api_url(self, url: Optional[str]) -> str:
|
|
174
261
|
"""Construct the databrowser url from a given hostname."""
|
|
175
|
-
url = url or self.
|
|
262
|
+
url = url or self._get_databrowser_params_from_config().get("host", "")
|
|
176
263
|
scheme, hostname = self._split_url(url)
|
|
177
264
|
hostname, _, port = hostname.partition(":")
|
|
178
265
|
if port:
|
|
@@ -207,7 +294,6 @@ class Config:
|
|
|
207
294
|
|
|
208
295
|
class UserDataHandler:
|
|
209
296
|
"""Class for processing user data.
|
|
210
|
-
|
|
211
297
|
This class is used for processing user data and extracting metadata
|
|
212
298
|
from the data files.
|
|
213
299
|
"""
|
|
@@ -17,3 +17,8 @@
|
|
|
17
17
|
## You can set a port by separating <hostname:port>
|
|
18
18
|
## for example freva.example.org:7777
|
|
19
19
|
# host = ""
|
|
20
|
+
|
|
21
|
+
##
|
|
22
|
+
## The default flavour to use when accessing freva. You can simply list
|
|
23
|
+
## the flavours you want to use and choose one as default.
|
|
24
|
+
# default_flavour = "cmip6"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
freva_client/__init__.py,sha256=6C-njQv12FgsYk2cWv2y3PPBZYfO99qPPeAFmyQhZyA,916
|
|
2
|
+
freva_client/__main__.py,sha256=JVj12puT4o8JfhKLAggR2-NKCZa3wKwsYGi4HQ61DOQ,149
|
|
3
|
+
freva_client/auth.py,sha256=yq_W8DjYA3W2b5HLKTplg5s_prJR9um9NV4uxHMIS2M,10897
|
|
4
|
+
freva_client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
freva_client/query.py,sha256=Dm6Wy9U8ofzZd4v50Sjhl4Guh7njrClSnNNV5_7UdM0,49433
|
|
6
|
+
freva_client/cli/__init__.py,sha256=NgTqBZGdozmTZtJduJUMrZj-opGw2KoT20tg6sc_xqo,149
|
|
7
|
+
freva_client/cli/auth_cli.py,sha256=lM6QNcewh9Cz7DtHO4Wu6iv7GapwplyjhDRfmIlzyJ0,1950
|
|
8
|
+
freva_client/cli/cli_app.py,sha256=QH6cb9XZTx8S9L0chPWOVd9Jn4GD1hd3sENAhdzmLZU,887
|
|
9
|
+
freva_client/cli/cli_parser.py,sha256=EyzvTr70TBCD0mO2P3mVNtuEeEbNk2OdrhKSEHuu6NE,5101
|
|
10
|
+
freva_client/cli/cli_utils.py,sha256=9h2hlBQA-D3n-JlFIC1DSuzEEv73Bpu8lXLAqZBDaYI,1946
|
|
11
|
+
freva_client/cli/databrowser_cli.py,sha256=LpK6NvHWsbHVeJvOHG_su0dh9FHkLxTmY4K8P75S8A0,39397
|
|
12
|
+
freva_client/utils/__init__.py,sha256=ySHn-3CZBwfZW2s0EpZ3INxUWOw1V4LOlKIxSLYr52U,1000
|
|
13
|
+
freva_client/utils/auth_utils.py,sha256=6didULriVK72D_Ptj8UV5wRrMl_w1C9vIoietpy3cG8,7206
|
|
14
|
+
freva_client/utils/databrowser_utils.py,sha256=Hv39Q8Crd63l2U-lBJfppMn14BUm6ECY0ASsgP_8du8,17801
|
|
15
|
+
freva_client/utils/logger.py,sha256=vjBbNb9KvyMriBPpgIoJjlQFCEj3DLqkJu8tYxfp2xI,2494
|
|
16
|
+
freva_client-2509.0.0.data/data/share/freva/freva.toml,sha256=5xXWBqw0W6JyWfRMaSNnKDOGkDznx6NFaJvggh-cUPU,899
|
|
17
|
+
freva_client-2509.0.0.dist-info/entry_points.txt,sha256=zGyEwHrH_kAGLsCXv00y7Qnp-WjXkUuIomHkfGMCxtA,53
|
|
18
|
+
freva_client-2509.0.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
19
|
+
freva_client-2509.0.0.dist-info/METADATA,sha256=gf2-uq7wTLtweozJYjxkFXkBOh-d8b2s1ys15nYv_rc,2487
|
|
20
|
+
freva_client-2509.0.0.dist-info/RECORD,,
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
freva_client/__init__.py,sha256=Yh9B2N-5bZJgwcHOxXDMg5hEOgetBoU3TmbsE6IVLQk,851
|
|
2
|
-
freva_client/__main__.py,sha256=JVj12puT4o8JfhKLAggR2-NKCZa3wKwsYGi4HQ61DOQ,149
|
|
3
|
-
freva_client/auth.py,sha256=p1itCygbLgEaCFTPWgqoId3S9PteP1lrziULziKzPG4,10453
|
|
4
|
-
freva_client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
freva_client/query.py,sha256=SQx5c4lNwP7ZiP1u6uN7pbUyvdjv74s2vQ3kRAa_708,41906
|
|
6
|
-
freva_client/cli/__init__.py,sha256=NgTqBZGdozmTZtJduJUMrZj-opGw2KoT20tg6sc_xqo,149
|
|
7
|
-
freva_client/cli/auth_cli.py,sha256=hCgTxN930uTj5Z0NqItAyejiUI9E-1oSv_lWOVoHNUo,1780
|
|
8
|
-
freva_client/cli/cli_app.py,sha256=QH6cb9XZTx8S9L0chPWOVd9Jn4GD1hd3sENAhdzmLZU,887
|
|
9
|
-
freva_client/cli/cli_parser.py,sha256=EyzvTr70TBCD0mO2P3mVNtuEeEbNk2OdrhKSEHuu6NE,5101
|
|
10
|
-
freva_client/cli/cli_utils.py,sha256=Ev9UxM4S1ZDbZSAGHFe5dMjVGot75w3muNKH3P80bHY,842
|
|
11
|
-
freva_client/cli/databrowser_cli.py,sha256=eUd-lbSQaRyczLKQQLY_eKU3VRf6Zg5HaHMQbe2Ib5Q,32242
|
|
12
|
-
freva_client/utils/__init__.py,sha256=ySHn-3CZBwfZW2s0EpZ3INxUWOw1V4LOlKIxSLYr52U,1000
|
|
13
|
-
freva_client/utils/auth_utils.py,sha256=Tma8aPi32zhJ-z2o6gEe1LvpBFlkn-t__UgZONcuARY,6032
|
|
14
|
-
freva_client/utils/databrowser_utils.py,sha256=X9M71mBfc8lauyLh3t1CCxhu_96Ya_NVkMNdxmWTsjE,15109
|
|
15
|
-
freva_client/utils/logger.py,sha256=vjBbNb9KvyMriBPpgIoJjlQFCEj3DLqkJu8tYxfp2xI,2494
|
|
16
|
-
freva_client-2508.0.0.data/data/share/freva/freva.toml,sha256=64Rh4qvWc9TaGJMXMi8tZW14FnESt5Z24y17BfD2VyM,736
|
|
17
|
-
freva_client-2508.0.0.dist-info/entry_points.txt,sha256=zGyEwHrH_kAGLsCXv00y7Qnp-WjXkUuIomHkfGMCxtA,53
|
|
18
|
-
freva_client-2508.0.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
19
|
-
freva_client-2508.0.0.dist-info/METADATA,sha256=8oAcxK-rhgUnBSSmUUsVcEJTNlzOcnWCkS91UaqhMqk,2487
|
|
20
|
-
freva_client-2508.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|