dcicutils 8.8.3.1b4__py3-none-any.whl → 8.8.3.1b7__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.
- dcicutils/misc_utils.py +23 -0
- dcicutils/progress_bar.py +59 -28
- {dcicutils-8.8.3.1b4.dist-info → dcicutils-8.8.3.1b7.dist-info}/METADATA +1 -1
- {dcicutils-8.8.3.1b4.dist-info → dcicutils-8.8.3.1b7.dist-info}/RECORD +7 -7
- {dcicutils-8.8.3.1b4.dist-info → dcicutils-8.8.3.1b7.dist-info}/LICENSE.txt +0 -0
- {dcicutils-8.8.3.1b4.dist-info → dcicutils-8.8.3.1b7.dist-info}/WHEEL +0 -0
- {dcicutils-8.8.3.1b4.dist-info → dcicutils-8.8.3.1b7.dist-info}/entry_points.txt +0 -0
dcicutils/misc_utils.py
CHANGED
@@ -2548,6 +2548,29 @@ def normalize_spaces(value: str) -> str:
|
|
2548
2548
|
return re.sub(r"\s+", " ", value).strip()
|
2549
2549
|
|
2550
2550
|
|
2551
|
+
def find_nth_from_end(string: str, substring: str, nth: int) -> int:
|
2552
|
+
"""
|
2553
|
+
Returns the index of the nth occurrence of the given substring within
|
2554
|
+
the given string from the END of the given string; or -1 if not found.
|
2555
|
+
"""
|
2556
|
+
index = -1
|
2557
|
+
string = string[::-1]
|
2558
|
+
for i in range(0, nth):
|
2559
|
+
index = string.find(substring, index + 1)
|
2560
|
+
return len(string) - index - 1 if index >= 0 else -1
|
2561
|
+
|
2562
|
+
|
2563
|
+
def set_nth(string: str, nth: int, replacement: str) -> str:
|
2564
|
+
"""
|
2565
|
+
Sets the nth character of the given string to the given replacement string.
|
2566
|
+
"""
|
2567
|
+
if not isinstance(string, str) or not isinstance(nth, int) or not isinstance(replacement, str):
|
2568
|
+
return string
|
2569
|
+
if nth < 0:
|
2570
|
+
nth += len(string)
|
2571
|
+
return string[:nth] + replacement + string[nth + 1:] if 0 <= nth < len(string) else string
|
2572
|
+
|
2573
|
+
|
2551
2574
|
class JsonLinesReader:
|
2552
2575
|
|
2553
2576
|
def __init__(self, fp, padded=False, padding=None):
|
dcicutils/progress_bar.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
from collections import namedtuple
|
2
|
+
import re
|
2
3
|
from signal import signal, SIGINT
|
3
4
|
import sys
|
4
5
|
import threading
|
@@ -8,6 +9,7 @@ from types import FrameType as frame
|
|
8
9
|
from typing import Callable, List, Optional, Union
|
9
10
|
from contextlib import contextmanager
|
10
11
|
from dcicutils.command_utils import yes_or_no
|
12
|
+
from dcicutils.misc_utils import find_nth_from_end, set_nth
|
11
13
|
|
12
14
|
|
13
15
|
class TQDM(tqdm):
|
@@ -59,6 +61,8 @@ class ProgressBar:
|
|
59
61
|
self._bar = None
|
60
62
|
self._disabled = False
|
61
63
|
self._done = False
|
64
|
+
self._really_done = False
|
65
|
+
self.foo = time.time()
|
62
66
|
self._tidy_output_hack = (tidy_output_hack is True)
|
63
67
|
self._started = time.time()
|
64
68
|
self._stop_requested = False
|
@@ -102,11 +106,9 @@ class ProgressBar:
|
|
102
106
|
return True
|
103
107
|
return False
|
104
108
|
|
105
|
-
def set_total(self, value: int
|
109
|
+
def set_total(self, value: int) -> None:
|
106
110
|
if value == self._total:
|
107
111
|
# If the total has not changed since last set then do nothing.
|
108
|
-
if reset_eta and self._bar is not None:
|
109
|
-
self._bar.reset()
|
110
112
|
return
|
111
113
|
if isinstance(value, int) and value > 0:
|
112
114
|
self._total = value
|
@@ -117,16 +119,6 @@ class ProgressBar:
|
|
117
119
|
self._bar.total = value
|
118
120
|
self._bar.refresh()
|
119
121
|
|
120
|
-
def reset_eta(self) -> None:
|
121
|
-
# Since set_total does nothing if total is the same, provide
|
122
|
-
# a way to reset the ETA if starting over with the same total.
|
123
|
-
if self._bar is not None:
|
124
|
-
progress = self._bar.n
|
125
|
-
self._bar.reset()
|
126
|
-
self._bar.total = self._total
|
127
|
-
self._bar.n = progress
|
128
|
-
self._bar.refresh()
|
129
|
-
|
130
122
|
def set_progress(self, value: int) -> None:
|
131
123
|
if isinstance(value, int) and value >= 0:
|
132
124
|
if (self._bar is not None) or self._initialize():
|
@@ -139,17 +131,29 @@ class ProgressBar:
|
|
139
131
|
self._bar.update(value)
|
140
132
|
self._bar.refresh()
|
141
133
|
|
142
|
-
def
|
143
|
-
|
134
|
+
def reset_eta(self) -> None:
|
135
|
+
# Since set_total does nothing if total is the same, provide
|
136
|
+
# a way to reset the ETA if starting over with the same total.
|
137
|
+
# But NOTE that resetting ETA will ALSO reset the ELAPSED time.
|
144
138
|
if self._bar is not None:
|
145
|
-
self._bar.
|
139
|
+
progress = self._bar.n
|
140
|
+
self._bar.reset()
|
141
|
+
self._bar.total = self._total
|
142
|
+
self._bar.n = progress
|
143
|
+
self._bar.refresh()
|
144
|
+
|
145
|
+
def set_description(self, value: str) -> None:
|
146
|
+
if isinstance(value, str):
|
147
|
+
self._description = self._format_description(value)
|
148
|
+
if self._bar is not None:
|
149
|
+
self._bar.set_description(self._description)
|
146
150
|
|
147
|
-
def done(self) -> None:
|
151
|
+
def done(self, description: Optional[str] = None) -> None:
|
148
152
|
if self._done or self._bar is None:
|
149
153
|
return
|
150
154
|
self._ended = time.time()
|
151
155
|
self.set_progress(self.total)
|
152
|
-
self.
|
156
|
+
self.set_description(description)
|
153
157
|
self._bar.refresh()
|
154
158
|
# FYI: Do NOT do a bar.disable = True before a bar.close() or it messes up output
|
155
159
|
# on multiple calls; found out the hard way; a couple hours will never get back :-/
|
@@ -200,6 +204,12 @@ class ProgressBar:
|
|
200
204
|
def captured_output_for_testing(self) -> Optional[List[str]]:
|
201
205
|
return self._captured_output_for_testing
|
202
206
|
|
207
|
+
@staticmethod
|
208
|
+
def format_captured_output_for_testing(description: str, total: int, progress: int) -> str:
|
209
|
+
percent = round((progress / total) * 100.0)
|
210
|
+
separator = "✓" if percent == 100 else "|"
|
211
|
+
return f"{description} {separator} {percent:>3}% ◀|### | {progress}/{total} | 0.0/s | 00:00 | ETA: 00:00"
|
212
|
+
|
203
213
|
def _format_description(self, value: str) -> str:
|
204
214
|
if not isinstance(value, str):
|
205
215
|
value = ""
|
@@ -259,29 +269,26 @@ class ProgressBar:
|
|
259
269
|
# string in the display string where the progress bar should actually go,
|
260
270
|
# which we do in _format_description. Other minor things too; see below.
|
261
271
|
sys_stdout_write = sys.stdout.write
|
262
|
-
|
272
|
+
last_text = None ; last_captured_output_text = None # noqa
|
263
273
|
def tidy_stdout_write(text: str) -> None: # noqa
|
264
274
|
nonlocal self, sys_stdout_write, sentinel_internal, spina, spini, spinn
|
265
|
-
nonlocal
|
275
|
+
nonlocal last_text, last_captured_output_text
|
266
276
|
def replace_first(value: str, match: str, replacement: str) -> str: # noqa
|
267
277
|
return value[:i] + replacement + value[i + len(match):] if (i := value.find(match)) >= 0 else value
|
268
278
|
def remove_extra_trailing_spaces(text: str) -> str: # noqa
|
269
279
|
while text.endswith(" "):
|
270
280
|
text = text[:-1]
|
271
281
|
return text
|
272
|
-
if not text:
|
282
|
+
if (not text) or (last_text == text) or self._really_done:
|
273
283
|
return
|
274
|
-
|
275
|
-
if ((self._bar.total == last_total) and (self._bar.n == last_progress) and (last_text == text)):
|
276
|
-
return
|
277
|
-
last_total = self._bar.total ; last_progress = self._bar.n ; last_text = text # noqa
|
284
|
+
last_text = text
|
278
285
|
if (self._disabled or self._done) and sentinel_internal in text:
|
279
286
|
# Another hack to really disable output on interrupt; in this case we set
|
280
287
|
# tqdm.disable to True, but output can still dribble out, so if the output
|
281
288
|
# looks like it is from tqdm and we are disabled/done then do no output.
|
282
289
|
return
|
283
290
|
if sentinel_internal in text:
|
284
|
-
spinc = spina[spini % spinn] if not ("100%|" in text) else "
|
291
|
+
spinc = spina[spini % spinn] if not ("100%|" in text) else "✓" ; spini += 1 # noqa
|
285
292
|
text = replace_first(text, sentinel_internal, f" {spinc}")
|
286
293
|
text = replace_first(text, "%|", "% ◀|")
|
287
294
|
text = remove_extra_trailing_spaces(text)
|
@@ -290,9 +297,33 @@ class ProgressBar:
|
|
290
297
|
# the unit we gave, which is empty; idunno; just replace it here.
|
291
298
|
text = replace_first(text, "s/ ", "/s ")
|
292
299
|
sys_stdout_write(text)
|
293
|
-
if self._captured_output_for_testing is not None:
|
294
|
-
self._captured_output_for_testing.append(text)
|
295
300
|
sys.stdout.flush()
|
301
|
+
if self._done:
|
302
|
+
self._really_done = True
|
303
|
+
return
|
304
|
+
if self._captured_output_for_testing is not None:
|
305
|
+
# For testing only we replace vacilliting values in the out like rate,
|
306
|
+
# time elapsed, and ETA with static values; so that something like this:
|
307
|
+
# > Working / 20% ◀|█████████▌ | 1/5 | 536.00/s | 00:01 | ETA: 00:02
|
308
|
+
# becomes something more static like this after calling this function:
|
309
|
+
# > Working | 20% ◀|### | 1/5 | 0.0/s | 00:00 | ETA: 00:00
|
310
|
+
# This function obviously has intimate knowledge of the output; better here than in tests.
|
311
|
+
def replace_time_dependent_values_with_static(text: str) -> str:
|
312
|
+
blocks = "\u2587|\u2588|\u2589|\u258a|\u258b|\u258c|\u258d|\u258e|\u258f"
|
313
|
+
if (n := find_nth_from_end(text, "|", 5)) >= 8:
|
314
|
+
pattern = re.compile(
|
315
|
+
rf"(\s*)(\d*%? ◀\|)(?:\s*{blocks}|#)*\s*(\|\s*\d+/\d+)?(\s*\|\s*)"
|
316
|
+
rf"(?:\d+\.?\d*|\?)(\/s\s*\|\s*)(?:\d+:\d+)?(\s*\|\s*ETA:\s*)(?:\d+:\d+|\?)?")
|
317
|
+
if match := pattern.match(text[n - 6:]):
|
318
|
+
if text[n - 8:n - 7] != "✓": text = set_nth(text, n - 8, "|") # noqa
|
319
|
+
return (text[0:n - 6].replace("\r", "") +
|
320
|
+
match.expand(rf"\g<1>\g<2>### \g<3>\g<4>0.0\g<5>00:00\g<6>00:00"))
|
321
|
+
return text
|
322
|
+
if text != "\n":
|
323
|
+
captured_output_text = replace_time_dependent_values_with_static(text)
|
324
|
+
if captured_output_text != last_captured_output_text:
|
325
|
+
self._captured_output_for_testing.append(captured_output_text)
|
326
|
+
last_captured_output_text = captured_output_text
|
296
327
|
def restore_stdout_write() -> None: # noqa
|
297
328
|
nonlocal sys_stdout_write
|
298
329
|
if sys_stdout_write is not None:
|
@@ -43,12 +43,12 @@ dcicutils/license_policies/park-lab-gpl-pipeline.jsonc,sha256=vLZkwm3Js-kjV44nug
|
|
43
43
|
dcicutils/license_policies/park-lab-pipeline.jsonc,sha256=9qlY0ASy3iUMQlr3gorVcXrSfRHnVGbLhkS427UaRy4,283
|
44
44
|
dcicutils/license_utils.py,sha256=d1cq6iwv5Ju-VjdoINi6q7CPNNL7Oz6rcJdLMY38RX0,46978
|
45
45
|
dcicutils/log_utils.py,sha256=7pWMc6vyrorUZQf-V-M3YC6zrPgNhuV_fzm9xqTPph0,10883
|
46
|
-
dcicutils/misc_utils.py,sha256=
|
46
|
+
dcicutils/misc_utils.py,sha256=a_grjJdiYgEMctwnXy7uDoFtDvCfAv1gjrLxdrOkptM,103041
|
47
47
|
dcicutils/obfuscation_utils.py,sha256=fo2jOmDRC6xWpYX49u80bVNisqRRoPskFNX3ymFAmjw,5963
|
48
48
|
dcicutils/opensearch_utils.py,sha256=V2exmFYW8Xl2_pGFixF4I2Cc549Opwe4PhFi5twC0M8,1017
|
49
49
|
dcicutils/portal_object_utils.py,sha256=gDXRgPsRvqCFwbC8WatsuflAxNiigOnqr0Hi93k3AgE,15422
|
50
50
|
dcicutils/portal_utils.py,sha256=Xm0IqL2dA9C2gx98cPEbvlo81V76bEmpUpxb_8S3VqM,30480
|
51
|
-
dcicutils/progress_bar.py,sha256=
|
51
|
+
dcicutils/progress_bar.py,sha256=j6dZdh5EeZo52GWIBu2JL5254-V6eOhKHHB2eCkgevI,16569
|
52
52
|
dcicutils/project_utils.py,sha256=qPdCaFmWUVBJw4rw342iUytwdQC0P-XKpK4mhyIulMM,31250
|
53
53
|
dcicutils/qa_checkers.py,sha256=cdXjeL0jCDFDLT8VR8Px78aS10hwNISOO5G_Zv2TZ6M,20534
|
54
54
|
dcicutils/qa_utils.py,sha256=TT0SiJWiuxYvbsIyhK9VO4uV_suxhB6CpuC4qPacCzQ,160208
|
@@ -72,8 +72,8 @@ dcicutils/trace_utils.py,sha256=g8kwV4ebEy5kXW6oOrEAUsurBcCROvwtZqz9fczsGRE,1769
|
|
72
72
|
dcicutils/validation_utils.py,sha256=cMZIU2cY98FYtzK52z5WUYck7urH6JcqOuz9jkXpqzg,14797
|
73
73
|
dcicutils/variant_utils.py,sha256=2H9azNx3xAj-MySg-uZ2SFqbWs4kZvf61JnK6b-h4Qw,4343
|
74
74
|
dcicutils/zip_utils.py,sha256=rnjNv_k6L9jT2SjDSgVXp4BEJYLtz9XN6Cl2Fy-tqnM,2027
|
75
|
-
dcicutils-8.8.3.
|
76
|
-
dcicutils-8.8.3.
|
77
|
-
dcicutils-8.8.3.
|
78
|
-
dcicutils-8.8.3.
|
79
|
-
dcicutils-8.8.3.
|
75
|
+
dcicutils-8.8.3.1b7.dist-info/LICENSE.txt,sha256=qnwSmfnEWMl5l78VPDEzAmEbLVrRqQvfUQiHT0ehrOo,1102
|
76
|
+
dcicutils-8.8.3.1b7.dist-info/METADATA,sha256=v8fZzOT3EWHNMI-R_bT-jbA1LTjKnGd1LOGD_oLn1Yg,3356
|
77
|
+
dcicutils-8.8.3.1b7.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
|
78
|
+
dcicutils-8.8.3.1b7.dist-info/entry_points.txt,sha256=51Q4F_2V10L0282W7HFjP4jdzW4K8lnWDARJQVFy_hw,270
|
79
|
+
dcicutils-8.8.3.1b7.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|