anemoi-utils 0.4.12__py3-none-any.whl → 0.4.14__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 anemoi-utils might be problematic. Click here for more details.
- anemoi/utils/__init__.py +1 -0
- anemoi/utils/__main__.py +12 -2
- anemoi/utils/_version.py +9 -4
- anemoi/utils/caching.py +138 -13
- anemoi/utils/checkpoints.py +81 -13
- anemoi/utils/cli.py +83 -7
- anemoi/utils/commands/__init__.py +4 -0
- anemoi/utils/commands/config.py +19 -2
- anemoi/utils/commands/requests.py +18 -2
- anemoi/utils/compatibility.py +6 -5
- anemoi/utils/config.py +254 -23
- anemoi/utils/dates.py +204 -50
- anemoi/utils/devtools.py +68 -7
- anemoi/utils/grib.py +30 -9
- anemoi/utils/grids.py +85 -8
- anemoi/utils/hindcasts.py +25 -8
- anemoi/utils/humanize.py +357 -52
- anemoi/utils/logs.py +31 -3
- anemoi/utils/mars/__init__.py +46 -12
- anemoi/utils/mars/requests.py +15 -1
- anemoi/utils/provenance.py +189 -32
- anemoi/utils/registry.py +234 -44
- anemoi/utils/remote/__init__.py +386 -38
- anemoi/utils/remote/s3.py +252 -29
- anemoi/utils/remote/ssh.py +140 -8
- anemoi/utils/s3.py +77 -4
- anemoi/utils/sanitise.py +52 -7
- anemoi/utils/testing.py +182 -0
- anemoi/utils/text.py +218 -54
- anemoi/utils/timer.py +91 -15
- {anemoi_utils-0.4.12.dist-info → anemoi_utils-0.4.14.dist-info}/METADATA +8 -4
- anemoi_utils-0.4.14.dist-info/RECORD +38 -0
- {anemoi_utils-0.4.12.dist-info → anemoi_utils-0.4.14.dist-info}/WHEEL +1 -1
- anemoi_utils-0.4.12.dist-info/RECORD +0 -37
- {anemoi_utils-0.4.12.dist-info → anemoi_utils-0.4.14.dist-info}/entry_points.txt +0 -0
- {anemoi_utils-0.4.12.dist-info → anemoi_utils-0.4.14.dist-info/licenses}/LICENSE +0 -0
- {anemoi_utils-0.4.12.dist-info → anemoi_utils-0.4.14.dist-info}/top_level.txt +0 -0
anemoi/utils/text.py
CHANGED
|
@@ -8,16 +8,21 @@
|
|
|
8
8
|
# nor does it submit to any jurisdiction.
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
"""Text utilities"""
|
|
11
|
+
"""Text utilities."""
|
|
12
12
|
|
|
13
13
|
import re
|
|
14
14
|
from collections import defaultdict
|
|
15
|
+
from typing import Any
|
|
16
|
+
from typing import List
|
|
17
|
+
from typing import Optional
|
|
18
|
+
from typing import Tuple
|
|
19
|
+
from typing import Union
|
|
15
20
|
|
|
16
21
|
# https://en.wikipedia.org/wiki/Box-drawing_character
|
|
17
22
|
|
|
18
23
|
|
|
19
24
|
def dotted_line(width=84) -> str:
|
|
20
|
-
"""Return a dotted line using '┈'
|
|
25
|
+
"""Return a dotted line using '┈'.
|
|
21
26
|
|
|
22
27
|
>>> dotted_line(40)
|
|
23
28
|
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
|
|
@@ -40,12 +45,35 @@ def dotted_line(width=84) -> str:
|
|
|
40
45
|
_ansi_escape = re.compile(r"\x1b\[([0-9;]*[mGKH])")
|
|
41
46
|
|
|
42
47
|
|
|
43
|
-
def _has_ansi_escape(s):
|
|
48
|
+
def _has_ansi_escape(s: str) -> bool:
|
|
49
|
+
"""Check if a string contains ANSI escape codes.
|
|
50
|
+
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
s : str
|
|
54
|
+
The string to check.
|
|
55
|
+
|
|
56
|
+
Returns
|
|
57
|
+
-------
|
|
58
|
+
bool
|
|
59
|
+
True if the string contains ANSI escape codes, False otherwise.
|
|
60
|
+
"""
|
|
44
61
|
return _ansi_escape.search(s) is not None
|
|
45
62
|
|
|
46
63
|
|
|
47
|
-
def _split_tokens(s):
|
|
48
|
-
"""Split a string into a list of visual characters with their
|
|
64
|
+
def _split_tokens(s: str) -> List[Tuple[str, int]]:
|
|
65
|
+
"""Split a string into a list of visual characters with their lengths.
|
|
66
|
+
|
|
67
|
+
Parameters
|
|
68
|
+
----------
|
|
69
|
+
s : str
|
|
70
|
+
The string to split.
|
|
71
|
+
|
|
72
|
+
Returns
|
|
73
|
+
-------
|
|
74
|
+
list of tuple
|
|
75
|
+
A list of tuples where each tuple contains a visual character and its length.
|
|
76
|
+
"""
|
|
49
77
|
from wcwidth import wcswidth
|
|
50
78
|
|
|
51
79
|
initial = s
|
|
@@ -76,8 +104,19 @@ def _split_tokens(s):
|
|
|
76
104
|
return out
|
|
77
105
|
|
|
78
106
|
|
|
79
|
-
def visual_len(s):
|
|
80
|
-
"""Compute the length of a string as it appears on the terminal.
|
|
107
|
+
def visual_len(s: Union[str, List[Tuple[str, int]]]) -> int:
|
|
108
|
+
"""Compute the length of a string as it appears on the terminal.
|
|
109
|
+
|
|
110
|
+
Parameters
|
|
111
|
+
----------
|
|
112
|
+
s : str or list of tuple
|
|
113
|
+
The string or list of visual characters with their lengths.
|
|
114
|
+
|
|
115
|
+
Returns
|
|
116
|
+
-------
|
|
117
|
+
int
|
|
118
|
+
The visual length of the string.
|
|
119
|
+
"""
|
|
81
120
|
if isinstance(s, str):
|
|
82
121
|
s = _split_tokens(s)
|
|
83
122
|
assert isinstance(s, (tuple, list)), (type(s), s)
|
|
@@ -92,8 +131,8 @@ def visual_len(s):
|
|
|
92
131
|
return n
|
|
93
132
|
|
|
94
133
|
|
|
95
|
-
def boxed(text, min_width=80, max_width=80) -> str:
|
|
96
|
-
"""Put a box around a text
|
|
134
|
+
def boxed(text: str, min_width: int = 80, max_width: int = 80) -> str:
|
|
135
|
+
"""Put a box around a text.
|
|
97
136
|
|
|
98
137
|
>>> boxed("Hello,\\nWorld!", max_width=40)
|
|
99
138
|
┌──────────────────────────────────────────┐
|
|
@@ -114,12 +153,11 @@ def boxed(text, min_width=80, max_width=80) -> str:
|
|
|
114
153
|
-------
|
|
115
154
|
str
|
|
116
155
|
A boxed version of the input text
|
|
117
|
-
|
|
118
156
|
"""
|
|
119
157
|
|
|
120
158
|
lines = []
|
|
121
159
|
for line in text.split("\n"):
|
|
122
|
-
line = line.
|
|
160
|
+
line = line.rstrip()
|
|
123
161
|
line = _split_tokens(line)
|
|
124
162
|
lines.append(line)
|
|
125
163
|
|
|
@@ -158,68 +196,157 @@ def boxed(text, min_width=80, max_width=80) -> str:
|
|
|
158
196
|
return "\n".join(box)
|
|
159
197
|
|
|
160
198
|
|
|
161
|
-
def bold(text):
|
|
199
|
+
def bold(text: str) -> str:
|
|
200
|
+
"""Make the text bold.
|
|
201
|
+
|
|
202
|
+
Parameters
|
|
203
|
+
----------
|
|
204
|
+
text : str
|
|
205
|
+
The text to make bold.
|
|
206
|
+
|
|
207
|
+
Returns
|
|
208
|
+
-------
|
|
209
|
+
str
|
|
210
|
+
The bold text.
|
|
211
|
+
"""
|
|
162
212
|
from termcolor import colored
|
|
163
213
|
|
|
164
214
|
return colored(text, attrs=["bold"])
|
|
165
215
|
|
|
166
216
|
|
|
167
|
-
def red(text):
|
|
217
|
+
def red(text: str) -> str:
|
|
218
|
+
"""Make the text red.
|
|
219
|
+
|
|
220
|
+
Parameters
|
|
221
|
+
----------
|
|
222
|
+
text : str
|
|
223
|
+
The text to make red.
|
|
224
|
+
|
|
225
|
+
Returns
|
|
226
|
+
-------
|
|
227
|
+
str
|
|
228
|
+
The red text.
|
|
229
|
+
"""
|
|
168
230
|
from termcolor import colored
|
|
169
231
|
|
|
170
232
|
return colored(text, "red")
|
|
171
233
|
|
|
172
234
|
|
|
173
|
-
def green(text):
|
|
235
|
+
def green(text: str) -> str:
|
|
236
|
+
"""Make the text green.
|
|
237
|
+
|
|
238
|
+
Parameters
|
|
239
|
+
----------
|
|
240
|
+
text : str
|
|
241
|
+
The text to make green.
|
|
242
|
+
|
|
243
|
+
Returns
|
|
244
|
+
-------
|
|
245
|
+
str
|
|
246
|
+
The green text.
|
|
247
|
+
"""
|
|
174
248
|
from termcolor import colored
|
|
175
249
|
|
|
176
250
|
return colored(text, "green")
|
|
177
251
|
|
|
178
252
|
|
|
179
|
-
def blue(text):
|
|
253
|
+
def blue(text: str) -> str:
|
|
254
|
+
"""Make the text blue.
|
|
255
|
+
|
|
256
|
+
Parameters
|
|
257
|
+
----------
|
|
258
|
+
text : str
|
|
259
|
+
The text to make blue.
|
|
260
|
+
|
|
261
|
+
Returns
|
|
262
|
+
-------
|
|
263
|
+
str
|
|
264
|
+
The blue text.
|
|
265
|
+
"""
|
|
180
266
|
from termcolor import colored
|
|
181
267
|
|
|
182
268
|
return colored(text, "blue")
|
|
183
269
|
|
|
184
270
|
|
|
185
271
|
class Tree:
|
|
186
|
-
"""Tree data structure.
|
|
272
|
+
"""Tree data structure.
|
|
187
273
|
|
|
188
|
-
|
|
274
|
+
Parameters
|
|
275
|
+
----------
|
|
276
|
+
actor : Any
|
|
277
|
+
The actor associated with the tree node.
|
|
278
|
+
parent : Tree, optional
|
|
279
|
+
The parent tree node, by default None.
|
|
280
|
+
"""
|
|
281
|
+
|
|
282
|
+
def __init__(self, actor: Any, parent: Optional["Tree"] = None):
|
|
189
283
|
self._actor = actor
|
|
190
284
|
self._kids = []
|
|
191
285
|
self._parent = parent
|
|
192
286
|
|
|
193
|
-
def adopt(self, kid):
|
|
287
|
+
def adopt(self, kid: "Tree") -> None:
|
|
288
|
+
"""Adopt a child tree node.
|
|
289
|
+
|
|
290
|
+
Parameters
|
|
291
|
+
----------
|
|
292
|
+
kid : Tree
|
|
293
|
+
The child tree node to adopt.
|
|
294
|
+
"""
|
|
194
295
|
kid._parent._kids.remove(kid)
|
|
195
296
|
self._kids.append(kid)
|
|
196
297
|
kid._parent = self
|
|
197
298
|
# assert False
|
|
198
299
|
|
|
199
|
-
def forget(self):
|
|
300
|
+
def forget(self) -> None:
|
|
301
|
+
"""Forget the current tree node."""
|
|
200
302
|
self._parent._kids.remove(self)
|
|
201
303
|
self._parent = None
|
|
202
304
|
|
|
203
305
|
@property
|
|
204
|
-
def is_leaf(self):
|
|
306
|
+
def is_leaf(self) -> bool:
|
|
307
|
+
"""Bool: True if the tree node is a leaf, False otherwise."""
|
|
205
308
|
return len(self._kids) == 0
|
|
206
309
|
|
|
207
310
|
@property
|
|
208
|
-
def key(self):
|
|
311
|
+
def key(self) -> Tuple:
|
|
312
|
+
"""Tuple: The key of the tree node."""
|
|
209
313
|
return tuple(sorted(self._actor.as_dict().items()))
|
|
210
314
|
|
|
211
315
|
@property
|
|
212
|
-
def _text(self):
|
|
316
|
+
def _text(self) -> str:
|
|
317
|
+
"""Str: The text representation of the tree node."""
|
|
213
318
|
return self._actor.summary
|
|
214
319
|
|
|
215
320
|
@property
|
|
216
|
-
def summary(self):
|
|
321
|
+
def summary(self) -> str:
|
|
322
|
+
"""Str: The summary of the tree node."""
|
|
217
323
|
return self._actor.summary
|
|
218
324
|
|
|
219
|
-
def as_dict(self):
|
|
325
|
+
def as_dict(self) -> dict:
|
|
326
|
+
"""Convert the tree node to a dictionary.
|
|
327
|
+
|
|
328
|
+
Returns
|
|
329
|
+
-------
|
|
330
|
+
dict
|
|
331
|
+
The dictionary representation of the tree node.
|
|
332
|
+
"""
|
|
220
333
|
return self._actor.as_dict()
|
|
221
334
|
|
|
222
|
-
def node(self, actor, insert=False):
|
|
335
|
+
def node(self, actor: Any, insert: bool = False) -> "Tree":
|
|
336
|
+
"""Create a new tree node.
|
|
337
|
+
|
|
338
|
+
Parameters
|
|
339
|
+
----------
|
|
340
|
+
actor : Any
|
|
341
|
+
The actor associated with the new tree node.
|
|
342
|
+
insert : bool, optional
|
|
343
|
+
Whether to insert the new tree node at the beginning, by default False.
|
|
344
|
+
|
|
345
|
+
Returns
|
|
346
|
+
-------
|
|
347
|
+
Tree
|
|
348
|
+
The new tree node.
|
|
349
|
+
"""
|
|
223
350
|
node = Tree(actor, self)
|
|
224
351
|
if insert:
|
|
225
352
|
self._kids.insert(0, node)
|
|
@@ -227,7 +354,8 @@ class Tree:
|
|
|
227
354
|
self._kids.append(node)
|
|
228
355
|
return node
|
|
229
356
|
|
|
230
|
-
def print(self):
|
|
357
|
+
def print(self) -> None:
|
|
358
|
+
"""Print the tree."""
|
|
231
359
|
padding = []
|
|
232
360
|
|
|
233
361
|
while self._factorise():
|
|
@@ -235,14 +363,28 @@ class Tree:
|
|
|
235
363
|
|
|
236
364
|
self._print(padding)
|
|
237
365
|
|
|
238
|
-
def _leaves(self, result):
|
|
366
|
+
def _leaves(self, result: List["Tree"]) -> None:
|
|
367
|
+
"""Collect all leaf nodes.
|
|
368
|
+
|
|
369
|
+
Parameters
|
|
370
|
+
----------
|
|
371
|
+
result : list of Tree
|
|
372
|
+
The list to collect the leaf nodes.
|
|
373
|
+
"""
|
|
239
374
|
if self.is_leaf:
|
|
240
375
|
result.append(self)
|
|
241
376
|
else:
|
|
242
377
|
for kid in self._kids:
|
|
243
378
|
kid._leaves(result)
|
|
244
379
|
|
|
245
|
-
def _factorise(self):
|
|
380
|
+
def _factorise(self) -> bool:
|
|
381
|
+
"""Factorise the tree.
|
|
382
|
+
|
|
383
|
+
Returns
|
|
384
|
+
-------
|
|
385
|
+
bool
|
|
386
|
+
True if the tree was factorised, False otherwise.
|
|
387
|
+
"""
|
|
246
388
|
if len(self._kids) == 0:
|
|
247
389
|
return False
|
|
248
390
|
|
|
@@ -290,7 +432,14 @@ class Tree:
|
|
|
290
432
|
|
|
291
433
|
return result
|
|
292
434
|
|
|
293
|
-
def _print(self, padding):
|
|
435
|
+
def _print(self, padding: List[str]) -> None:
|
|
436
|
+
"""Print the tree with padding.
|
|
437
|
+
|
|
438
|
+
Parameters
|
|
439
|
+
----------
|
|
440
|
+
padding : list of str
|
|
441
|
+
The padding for each level of the tree.
|
|
442
|
+
"""
|
|
294
443
|
for i, p in enumerate(padding[:-1]):
|
|
295
444
|
if p == " └":
|
|
296
445
|
padding[i] = " "
|
|
@@ -308,7 +457,19 @@ class Tree:
|
|
|
308
457
|
|
|
309
458
|
padding.pop()
|
|
310
459
|
|
|
311
|
-
def to_json(self, depth=0):
|
|
460
|
+
def to_json(self, depth: int = 0) -> dict:
|
|
461
|
+
"""Convert the tree to a JSON serializable dictionary.
|
|
462
|
+
|
|
463
|
+
Parameters
|
|
464
|
+
----------
|
|
465
|
+
depth : int, optional
|
|
466
|
+
The depth of the tree, by default 0.
|
|
467
|
+
|
|
468
|
+
Returns
|
|
469
|
+
-------
|
|
470
|
+
dict
|
|
471
|
+
The JSON serializable dictionary representation of the tree.
|
|
472
|
+
"""
|
|
312
473
|
while self._factorise():
|
|
313
474
|
pass
|
|
314
475
|
|
|
@@ -319,20 +480,20 @@ class Tree:
|
|
|
319
480
|
}
|
|
320
481
|
|
|
321
482
|
|
|
322
|
-
def table(rows, header, align, margin=0) -> str:
|
|
323
|
-
"""Format a table
|
|
483
|
+
def table(rows: List[List[Any]], header: List[str], align: List[str], margin: int = 0) -> str:
|
|
484
|
+
"""Format a table.
|
|
324
485
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
486
|
+
>>> table([['Aa', 12, 5],
|
|
487
|
+
['B', 120, 1],
|
|
488
|
+
['C', 9, 123]],
|
|
489
|
+
['C1', 'C2', 'C3'],
|
|
490
|
+
['<', '>', '>'])
|
|
491
|
+
C1 │ C2 │ C3
|
|
492
|
+
───┼─────┼────
|
|
493
|
+
Aa │ 12 │ 5
|
|
494
|
+
B │ 120 │ 1
|
|
495
|
+
C │ 9 │ 123
|
|
496
|
+
───┴─────┴────
|
|
336
497
|
|
|
337
498
|
Parameters
|
|
338
499
|
----------
|
|
@@ -397,26 +558,29 @@ def table(rows, header, align, margin=0) -> str:
|
|
|
397
558
|
return "\n".join(result)
|
|
398
559
|
|
|
399
560
|
|
|
400
|
-
def progress(done, todo, width=80) -> str:
|
|
401
|
-
"""
|
|
402
|
-
|
|
403
|
-
>>> print(progress(10, 100,width=50))
|
|
404
|
-
█████▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
|
|
561
|
+
def progress(done: int, todo: int, width: int = 80) -> str:
|
|
562
|
+
"""Generates a progress bar string.
|
|
405
563
|
|
|
406
564
|
Parameters
|
|
407
565
|
----------
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
566
|
+
|
|
567
|
+
done : int
|
|
568
|
+
The number of tasks completed.
|
|
569
|
+
todo : int
|
|
570
|
+
The total number of tasks.
|
|
412
571
|
width : int, optional
|
|
413
|
-
|
|
572
|
+
The width of the progress bar, by default 80.
|
|
414
573
|
|
|
415
574
|
Returns
|
|
416
575
|
-------
|
|
417
576
|
str
|
|
418
|
-
|
|
577
|
+
A string representing the progress bar.
|
|
419
578
|
|
|
579
|
+
Example
|
|
580
|
+
-------
|
|
581
|
+
|
|
582
|
+
>>> print(progress(10, 100,width=50))
|
|
583
|
+
█████▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
|
|
420
584
|
"""
|
|
421
585
|
done = min(int(done / todo * width + 0.5), width)
|
|
422
586
|
return green("█" * done) + red("█" * (width - done))
|
anemoi/utils/timer.py
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import logging
|
|
14
14
|
import time
|
|
15
15
|
from collections import defaultdict
|
|
16
|
+
from typing import Any
|
|
16
17
|
|
|
17
18
|
from .humanize import seconds_to_human
|
|
18
19
|
|
|
@@ -20,21 +21,53 @@ LOGGER = logging.getLogger(__name__)
|
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class Timer:
|
|
23
|
-
"""Context manager to measure elapsed time.
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
"""Context manager to measure elapsed time.
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
title : str
|
|
29
|
+
The title of the timer.
|
|
30
|
+
logger : logging.Logger, optional
|
|
31
|
+
The logger to use for logging the elapsed time, by default LOGGER.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, title: str, logger: logging.Logger = LOGGER):
|
|
35
|
+
"""Initialize the Timer.
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
title : str
|
|
40
|
+
The title of the timer.
|
|
41
|
+
logger : logging.Logger, optional
|
|
42
|
+
The logger to use for logging the elapsed time, by default LOGGER.
|
|
43
|
+
"""
|
|
26
44
|
self.title = title
|
|
27
45
|
self.start = time.time()
|
|
28
46
|
self.logger = logger
|
|
29
47
|
|
|
30
|
-
def __enter__(self):
|
|
48
|
+
def __enter__(self) -> "Timer":
|
|
49
|
+
"""Enter the runtime context related to this object.
|
|
50
|
+
|
|
51
|
+
Returns
|
|
52
|
+
-------
|
|
53
|
+
Timer
|
|
54
|
+
The Timer object itself.
|
|
55
|
+
"""
|
|
31
56
|
return self
|
|
32
57
|
|
|
33
58
|
@property
|
|
34
|
-
def elapsed(self):
|
|
59
|
+
def elapsed(self) -> float:
|
|
60
|
+
"""Float: The elapsed time in seconds."""
|
|
35
61
|
return time.time() - self.start
|
|
36
62
|
|
|
37
|
-
def __exit__(self, *args):
|
|
63
|
+
def __exit__(self, *args: Any) -> None:
|
|
64
|
+
"""Exit the runtime context related to this object.
|
|
65
|
+
|
|
66
|
+
Parameters
|
|
67
|
+
----------
|
|
68
|
+
*args : Any
|
|
69
|
+
Exception information.
|
|
70
|
+
"""
|
|
38
71
|
self.logger.info("%s: %s.", self.title, seconds_to_human(self.elapsed))
|
|
39
72
|
|
|
40
73
|
|
|
@@ -42,33 +75,76 @@ class _Timer:
|
|
|
42
75
|
"""Internal timer class."""
|
|
43
76
|
|
|
44
77
|
def __init__(self):
|
|
78
|
+
"""Initialize the _Timer."""
|
|
45
79
|
self.elapsed = 0.0
|
|
46
80
|
|
|
47
|
-
def __enter__(self):
|
|
81
|
+
def __enter__(self) -> "_Timer":
|
|
82
|
+
"""Enter the runtime context related to this object.
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
_Timer
|
|
87
|
+
The _Timer object itself.
|
|
88
|
+
"""
|
|
48
89
|
self.start()
|
|
49
90
|
return self
|
|
50
91
|
|
|
51
|
-
def __exit__(self, *args):
|
|
92
|
+
def __exit__(self, *args: Any) -> None:
|
|
93
|
+
"""Exit the runtime context related to this object.
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
*args : Any
|
|
98
|
+
Exception information.
|
|
99
|
+
"""
|
|
52
100
|
self.stop()
|
|
53
101
|
|
|
54
|
-
def start(self):
|
|
102
|
+
def start(self) -> None:
|
|
103
|
+
"""Start the timer."""
|
|
55
104
|
self._start = time.time()
|
|
56
105
|
|
|
57
|
-
def stop(self):
|
|
106
|
+
def stop(self) -> None:
|
|
107
|
+
"""Stop the timer and accumulate the elapsed time."""
|
|
58
108
|
self.elapsed += time.time() - self._start
|
|
59
109
|
|
|
60
110
|
|
|
61
111
|
class Timers:
|
|
62
|
-
"""A collection of timers.
|
|
63
|
-
|
|
64
|
-
|
|
112
|
+
"""A collection of timers.
|
|
113
|
+
|
|
114
|
+
Parameters
|
|
115
|
+
----------
|
|
116
|
+
logger : logging.Logger, optional
|
|
117
|
+
The logger to use for logging the elapsed time, by default LOGGER.
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
def __init__(self, logger: logging.Logger = LOGGER):
|
|
121
|
+
"""Initialize the Timers collection.
|
|
122
|
+
|
|
123
|
+
Parameters
|
|
124
|
+
----------
|
|
125
|
+
logger : logging.Logger, optional
|
|
126
|
+
The logger to use for logging the elapsed time, by default LOGGER.
|
|
127
|
+
"""
|
|
65
128
|
self.logger = logger
|
|
66
129
|
self.timers = defaultdict(_Timer)
|
|
67
130
|
|
|
68
|
-
def __getitem__(self, name):
|
|
131
|
+
def __getitem__(self, name: str) -> _Timer:
|
|
132
|
+
"""Get a timer by name.
|
|
133
|
+
|
|
134
|
+
Parameters
|
|
135
|
+
----------
|
|
136
|
+
name : str
|
|
137
|
+
The name of the timer.
|
|
138
|
+
|
|
139
|
+
Returns
|
|
140
|
+
-------
|
|
141
|
+
_Timer
|
|
142
|
+
The timer with the given name.
|
|
143
|
+
"""
|
|
69
144
|
return self.timers[name]
|
|
70
145
|
|
|
71
|
-
def report(self):
|
|
146
|
+
def report(self) -> None:
|
|
147
|
+
"""Log the elapsed time for all timers."""
|
|
72
148
|
length = max(len(name) for name in self.timers)
|
|
73
149
|
for name, timer in sorted(self.timers.items()):
|
|
74
150
|
self.logger.info("%s: %s.", f"{name:<{length}}", seconds_to_human(timer.elapsed))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: anemoi-utils
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.14
|
|
4
4
|
Summary: A package to hold various functions to support training of ML models on ECMWF data.
|
|
5
5
|
Author-email: "European Centre for Medium-Range Weather Forecasts (ECMWF)" <software.support@ecmwf.int>
|
|
6
6
|
License: Apache License
|
|
@@ -226,13 +226,14 @@ Requires-Python: >=3.9
|
|
|
226
226
|
License-File: LICENSE
|
|
227
227
|
Requires-Dist: aniso8601
|
|
228
228
|
Requires-Dist: importlib-metadata; python_version < "3.10"
|
|
229
|
+
Requires-Dist: multiurl
|
|
229
230
|
Requires-Dist: numpy
|
|
230
231
|
Requires-Dist: python-dateutil
|
|
231
232
|
Requires-Dist: pyyaml
|
|
232
233
|
Requires-Dist: tomli; python_version < "3.11"
|
|
233
234
|
Requires-Dist: tqdm
|
|
234
235
|
Provides-Extra: all
|
|
235
|
-
Requires-Dist: anemoi-utils[grib,provenance,text]; extra == "all"
|
|
236
|
+
Requires-Dist: anemoi-utils[grib,provenance,s3,text]; extra == "all"
|
|
236
237
|
Provides-Extra: dev
|
|
237
238
|
Requires-Dist: anemoi-utils[all,docs,tests]; extra == "dev"
|
|
238
239
|
Provides-Extra: docs
|
|
@@ -240,7 +241,7 @@ Requires-Dist: nbsphinx; extra == "docs"
|
|
|
240
241
|
Requires-Dist: pandoc; extra == "docs"
|
|
241
242
|
Requires-Dist: requests; extra == "docs"
|
|
242
243
|
Requires-Dist: sphinx; extra == "docs"
|
|
243
|
-
Requires-Dist: sphinx-argparse
|
|
244
|
+
Requires-Dist: sphinx-argparse; extra == "docs"
|
|
244
245
|
Requires-Dist: sphinx-rtd-theme; extra == "docs"
|
|
245
246
|
Requires-Dist: termcolor; extra == "docs"
|
|
246
247
|
Provides-Extra: grib
|
|
@@ -248,8 +249,11 @@ Requires-Dist: requests; extra == "grib"
|
|
|
248
249
|
Provides-Extra: provenance
|
|
249
250
|
Requires-Dist: gitpython; extra == "provenance"
|
|
250
251
|
Requires-Dist: nvsmi; extra == "provenance"
|
|
252
|
+
Provides-Extra: s3
|
|
253
|
+
Requires-Dist: boto3<1.36; extra == "s3"
|
|
251
254
|
Provides-Extra: tests
|
|
252
255
|
Requires-Dist: pytest; extra == "tests"
|
|
253
256
|
Provides-Extra: text
|
|
254
257
|
Requires-Dist: termcolor; extra == "text"
|
|
255
258
|
Requires-Dist: wcwidth; extra == "text"
|
|
259
|
+
Dynamic: license-file
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
anemoi/utils/__init__.py,sha256=uVhpF-VjIl_4mMywOVtgTutgsdIsqz-xdkwxeMhzuag,730
|
|
2
|
+
anemoi/utils/__main__.py,sha256=6LlE4MYrPvqqrykxXh7XMi50UZteUY59NeM8P9Zs2dU,910
|
|
3
|
+
anemoi/utils/_version.py,sha256=ggKLQlA5gegh3l4LAMWuwHPmL8zgZ4b0pYtFx7quk78,513
|
|
4
|
+
anemoi/utils/caching.py,sha256=rXbeAmpBcMbbfN4EVblaHWKicsrtx1otER84FEBtz98,6183
|
|
5
|
+
anemoi/utils/checkpoints.py,sha256=N4WpAZXa4etrpSEKhHqUUtG2-x9w3FJMHcLO-dDAXPY,9600
|
|
6
|
+
anemoi/utils/cli.py,sha256=IyZfnSw0u0yYnrjOrzvm2RuuKvDk4cVb8pf8BkaChgA,6209
|
|
7
|
+
anemoi/utils/compatibility.py,sha256=wRBRMmxQP88rNcWiP5gqXliwYQbBv1iCAsDjcCRi5UY,2234
|
|
8
|
+
anemoi/utils/config.py,sha256=4K4_ePeuUPyIMsE7-r77OC87ntFGdVLskB0ow9XNzXI,16353
|
|
9
|
+
anemoi/utils/dates.py,sha256=CnY6JOdpk0T-waPEowMRTkcMzxcN0GcjPVtLkwH_byw,17196
|
|
10
|
+
anemoi/utils/devtools.py,sha256=vivYRQ1l2aMAkggqhpMJcJedfCrRfpy5XMQjZIsMJS0,3596
|
|
11
|
+
anemoi/utils/grib.py,sha256=201WcxjjAl92Y2HX2kZ2S8Qr5dN-oG7nV-vQLaybzP4,3610
|
|
12
|
+
anemoi/utils/grids.py,sha256=edTrMK8hpE9ZBzSfwcRftgk0jljNAK3i8CraadILQoM,4427
|
|
13
|
+
anemoi/utils/hindcasts.py,sha256=iYVIxSNFL2HJcc_k1abCFLkpJFGHT8WKRIR4wcAwA3s,2144
|
|
14
|
+
anemoi/utils/humanize.py,sha256=hCrHr5ppREuJR-tBqRqynqe58BHR6Ga_gCQqgEmmrfU,25301
|
|
15
|
+
anemoi/utils/logs.py,sha256=naTgrmPwWHD4eekFttXftS4gtcAGYHpCqG4iwYprNDA,1804
|
|
16
|
+
anemoi/utils/provenance.py,sha256=tIIgweS0EJyaYzgKwuv3iWny-Gz7N5e5CNWH5MeSYWU,14615
|
|
17
|
+
anemoi/utils/registry.py,sha256=zTnVSCecmrI6SkZhF4ipV7WKiZYHEBJ_5ZwqhRGM4T0,9287
|
|
18
|
+
anemoi/utils/s3.py,sha256=xMT48kbcelcjjqsaU567WI3oZ5eqo88Rlgyx5ECszAU,4074
|
|
19
|
+
anemoi/utils/sanitise.py,sha256=ZYGdSX6qihQANr3pHZjbKnoapnzP1KcrWdW1Ul1mOGk,3668
|
|
20
|
+
anemoi/utils/sanitize.py,sha256=43ZKDcfVpeXSsJ9TFEc9aZnD6oe2cUh151XnDspM98M,462
|
|
21
|
+
anemoi/utils/testing.py,sha256=N1y4dfZLE9zqOhIR3o-933fdAdd9BxDvjcJx7SwFC9A,4803
|
|
22
|
+
anemoi/utils/text.py,sha256=HkzIvi24obDceFLpJEwBJ9PmPrJUkQN2TrElJ-A87gU,14441
|
|
23
|
+
anemoi/utils/timer.py,sha256=_leKMYza2faM7JKlGE7LCNy13rbdPnwaCF7PSrI_NmI,3895
|
|
24
|
+
anemoi/utils/commands/__init__.py,sha256=5u_6EwdqYczIAgJfCwRSyQAYFEqh2ZuHHT57g9g7sdI,808
|
|
25
|
+
anemoi/utils/commands/config.py,sha256=nYvYbjcum9uumRa3gzPfmQCjNZKURXMA2XOUwz9b7ls,1369
|
|
26
|
+
anemoi/utils/commands/requests.py,sha256=AEbssF1OlpbmSwrV5Lj6amCCn0w_-nbajBWTwYV15vA,2059
|
|
27
|
+
anemoi/utils/mars/__init__.py,sha256=b-Lc3L1TAQd9ODs0Z1YSJzgZCO1K_M3DSgx_yd2qXvM,2724
|
|
28
|
+
anemoi/utils/mars/mars.yaml,sha256=R0dujp75lLA4wCWhPeOQnzJ45WZAYLT8gpx509cBFlc,66
|
|
29
|
+
anemoi/utils/mars/requests.py,sha256=VFMHBVAAl0_2lOcMBa1lvaKHctN0lDJsI6_U4BucGew,1142
|
|
30
|
+
anemoi/utils/remote/__init__.py,sha256=-uaYFi4yRYFRf46ubQbJo86GCn6HE5VQrcaoyrmyW28,20704
|
|
31
|
+
anemoi/utils/remote/s3.py,sha256=spQ8l0rwQjLZh9dZu5cOsYIvNwKihQfCJ6YsFYegeqI,17339
|
|
32
|
+
anemoi/utils/remote/ssh.py,sha256=xNtsawh8okytCKRehkRCVExbHZj-CRUQNormEHglfuw,8088
|
|
33
|
+
anemoi_utils-0.4.14.dist-info/licenses/LICENSE,sha256=8HznKF1Vi2IvfLsKNE5A2iVyiri3pRjRPvPC9kxs6qk,11354
|
|
34
|
+
anemoi_utils-0.4.14.dist-info/METADATA,sha256=VLuh3lxCAuACOrJRnE_Dm0zX_pVfIHoppmRrP505pkY,15360
|
|
35
|
+
anemoi_utils-0.4.14.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
|
|
36
|
+
anemoi_utils-0.4.14.dist-info/entry_points.txt,sha256=LENOkn88xzFQo-V59AKoA_F_cfYQTJYtrNTtf37YgHY,60
|
|
37
|
+
anemoi_utils-0.4.14.dist-info/top_level.txt,sha256=DYn8VPs-fNwr7fNH9XIBqeXIwiYYd2E2k5-dUFFqUz0,7
|
|
38
|
+
anemoi_utils-0.4.14.dist-info/RECORD,,
|