dcicutils 8.8.2__tar.gz → 8.8.2.1b2__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/PKG-INFO +1 -1
- dcicutils-8.8.2.1b2/dcicutils/progress_bar.py +281 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/structured_data.py +1 -1
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/pyproject.toml +1 -1
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/LICENSE.txt +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/README.rst +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/__init__.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/base.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/beanstalk_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/bundle_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/captured_output.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/cloudformation_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/codebuild_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/command_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/common.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/contribution_scripts.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/contribution_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/creds_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/data_readers.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/data_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/datetime_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/deployment_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/diff_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/docker_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/ecr_scripts.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/ecr_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/ecs_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/env_base.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/env_manager.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/env_scripts.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/env_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/env_utils_legacy.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/es_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/exceptions.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/ff_mocks.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/ff_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/file_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/function_cache_decorator.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/glacier_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/jh_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/kibana/dashboards.json +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/kibana/readme.md +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/lang_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/license_policies/c4-infrastructure.jsonc +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/license_policies/c4-python-infrastructure.jsonc +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/license_policies/park-lab-common-server.jsonc +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/license_policies/park-lab-common.jsonc +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/license_policies/park-lab-gpl-pipeline.jsonc +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/license_policies/park-lab-pipeline.jsonc +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/license_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/log_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/misc_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/obfuscation_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/opensearch_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/portal_object_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/portal_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/project_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/qa_checkers.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/qa_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/redis_tools.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/redis_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/s3_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/schema_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/scripts/publish_to_pypi.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/scripts/run_license_checker.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/scripts/view_portal_object.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/secrets_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/sheet_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/snapshot_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/ssl_certificate_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/submitr/progress_constants.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/submitr/ref_lookup_strategy.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/task_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/tmpfile_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/trace_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/validation_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/variant_utils.py +0 -0
- {dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/zip_utils.py +0 -0
@@ -0,0 +1,281 @@
|
|
1
|
+
from collections import namedtuple
|
2
|
+
from signal import signal, SIGINT
|
3
|
+
import sys
|
4
|
+
import threading
|
5
|
+
import time
|
6
|
+
from tqdm import tqdm
|
7
|
+
from types import FrameType as frame
|
8
|
+
from typing import Callable, Optional, Union
|
9
|
+
from contextlib import contextmanager
|
10
|
+
from dcicutils.command_utils import yes_or_no
|
11
|
+
|
12
|
+
|
13
|
+
class TQDM(tqdm):
|
14
|
+
|
15
|
+
"""
|
16
|
+
def moveto(self, n):
|
17
|
+
# Hack/workaround for apparent tqdm bug where (for example) if we use this twice
|
18
|
+
# in a row, and we interrupt (CTRL-C) the first one and abort the task, then the
|
19
|
+
# output for the second usage gets a bit garble; on the wrong line and whatnot;
|
20
|
+
# somehow, state is stuck across usages; can't quite see how from the tqdm code.
|
21
|
+
# This is a bit worrying but so far no other deleterious effects observed.
|
22
|
+
# This looks maybe promising:
|
23
|
+
return
|
24
|
+
"""
|
25
|
+
|
26
|
+
# Nevermind the above; found a more pointed solution from here:
|
27
|
+
# https://stackoverflow.com/questions/41707229/why-is-tqdm-printing-to-a-newline-instead-of-updating-the-same-line
|
28
|
+
# Why in the world would tqdm be maintaining state across instances?? Whatever, this fixes it.
|
29
|
+
def __init__(self, *args, **kwargs):
|
30
|
+
super()._instances.clear() if super()._instances else None
|
31
|
+
super().__init__(*args, **kwargs)
|
32
|
+
|
33
|
+
|
34
|
+
# Wrapper around tqdm command-line progress bar.
|
35
|
+
class ProgressBar:
|
36
|
+
|
37
|
+
@staticmethod
|
38
|
+
@contextmanager
|
39
|
+
def define(*args, **kwargs):
|
40
|
+
progress_bar = None
|
41
|
+
try:
|
42
|
+
progress_bar = ProgressBar(*args, **kwargs)
|
43
|
+
yield progress_bar
|
44
|
+
finally:
|
45
|
+
if progress_bar:
|
46
|
+
progress_bar.done()
|
47
|
+
|
48
|
+
def __init__(self, total: Optional[int] = None,
|
49
|
+
description: Optional[str] = None,
|
50
|
+
catch_interrupt: bool = True,
|
51
|
+
interrupt: Optional[Callable] = None,
|
52
|
+
interrupt_continue: Optional[Callable] = None,
|
53
|
+
interrupt_stop: Optional[Callable] = None,
|
54
|
+
interrupt_exit: bool = False,
|
55
|
+
interrupt_exit_message: Optional[Union[Callable, str]] = None,
|
56
|
+
interrupt_message: Optional[str] = None,
|
57
|
+
printf: Optional[Callable] = None,
|
58
|
+
tidy_output_hack: bool = True) -> None:
|
59
|
+
self._bar = None
|
60
|
+
self._disabled = False
|
61
|
+
self._done = False
|
62
|
+
self._printf = printf if callable(printf) else print
|
63
|
+
self._tidy_output_hack = (tidy_output_hack is True)
|
64
|
+
self._started = time.time()
|
65
|
+
self._stop_requested = False
|
66
|
+
# Interrupt handling. We do not do the actual (signal) interrupt setup
|
67
|
+
# in self._initialiaze as that could be called from a (sub) thread; and in
|
68
|
+
# Python we can only set a signal (SIGINT in our case) on the main thread.
|
69
|
+
self._catch_interrupt = (catch_interrupt is True)
|
70
|
+
self._interrupt = interrupt if callable(interrupt) else None
|
71
|
+
self._interrupt_continue = interrupt_continue if callable(interrupt_continue) else None
|
72
|
+
self._interrupt_stop = interrupt_stop if callable(interrupt_stop) else None
|
73
|
+
if interrupt_exit in [True, False]:
|
74
|
+
if not self._interrupt_stop:
|
75
|
+
self._interrupt_stop = lambda _: interrupt_exit
|
76
|
+
self._interrupt_exit = interrupt_exit
|
77
|
+
else:
|
78
|
+
self._interrupt_exit = None
|
79
|
+
self._interrupt_message = interrupt_message if isinstance(interrupt_message, str) else None
|
80
|
+
if isinstance(interrupt_exit_message, str):
|
81
|
+
self._interrupt_exit_message = lambda bar: interrupt_exit_message
|
82
|
+
elif isinstance(interrupt_exit_message, Callable):
|
83
|
+
self._interrupt_exit_message = interrupt_exit_message
|
84
|
+
else:
|
85
|
+
self._interrupt_exit_message = None
|
86
|
+
self._interrupt_handler = None
|
87
|
+
if self._catch_interrupt:
|
88
|
+
self._interrupt_handler = self._define_interrupt_handler()
|
89
|
+
if self._tidy_output_hack is True:
|
90
|
+
self._tidy_output_hack = self._define_tidy_output_hack()
|
91
|
+
self._total = total if isinstance(total, int) and total >= 0 else 0
|
92
|
+
self._description = self._format_description(description)
|
93
|
+
# self._initialize()
|
94
|
+
|
95
|
+
def _initialize(self) -> bool:
|
96
|
+
# Do not actually create the tqdm object unless/until we have a positive total.
|
97
|
+
if (self._bar is None) and (self._total > 0):
|
98
|
+
bar_format = "{l_bar}{bar}| {n_fmt}/{total_fmt} | {rate_fmt} | {elapsed}{postfix} | ETA: {remaining} "
|
99
|
+
self._bar = TQDM(total=self._total, desc=self._description,
|
100
|
+
dynamic_ncols=True, bar_format=bar_format, unit="", file=sys.stdout)
|
101
|
+
if self._disabled:
|
102
|
+
self._bar.disable = True
|
103
|
+
return True
|
104
|
+
return False
|
105
|
+
|
106
|
+
def set_total(self, value: int, reset_eta: bool = False) -> None:
|
107
|
+
if value == self._total:
|
108
|
+
# If the total has not changed since last set then do nothing.
|
109
|
+
if reset_eta and self._bar is not None:
|
110
|
+
self._bar.reset()
|
111
|
+
return
|
112
|
+
if isinstance(value, int) and value > 0:
|
113
|
+
self._total = value
|
114
|
+
if self._bar is not None:
|
115
|
+
# This reset is needed to get the ETA to reset properly when we reset
|
116
|
+
# the total during the course of a single ProgressBar instance.
|
117
|
+
self._bar.reset()
|
118
|
+
self._bar.total = value
|
119
|
+
|
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
|
+
self._bar.reset()
|
125
|
+
|
126
|
+
def set_progress(self, value: int) -> None:
|
127
|
+
if isinstance(value, int) and value >= 0:
|
128
|
+
if (self._bar is not None) or self._initialize():
|
129
|
+
self._bar.n = value
|
130
|
+
self._bar.refresh()
|
131
|
+
|
132
|
+
def increment_progress(self, value: int) -> None:
|
133
|
+
if isinstance(value, int) and value > 0:
|
134
|
+
if (self._bar is not None) or self._initialize():
|
135
|
+
self._bar.update(value)
|
136
|
+
self._bar.refresh()
|
137
|
+
|
138
|
+
def set_description(self, value: str) -> None:
|
139
|
+
self._description = self._format_description(value)
|
140
|
+
if self._bar is not None:
|
141
|
+
self._bar.set_description(self._description)
|
142
|
+
|
143
|
+
def done(self) -> None:
|
144
|
+
if self._done or self._bar is None:
|
145
|
+
return
|
146
|
+
self._ended = time.time()
|
147
|
+
self._done = True
|
148
|
+
self.set_progress(self.total) # xyzzy
|
149
|
+
self._bar.set_description(self._description)
|
150
|
+
self._bar.refresh()
|
151
|
+
# FYI: Do NOT do a bar.disable = True before a bar.close() or it messes up output
|
152
|
+
# on multiple calls; found out the hard way; a couple hours will never get back :-/
|
153
|
+
self._bar.close()
|
154
|
+
if self._tidy_output_hack:
|
155
|
+
self._tidy_output_hack.restore()
|
156
|
+
if self._interrupt_handler:
|
157
|
+
self._interrupt_handler.restore()
|
158
|
+
|
159
|
+
def disable(self, value: bool = True) -> None:
|
160
|
+
self._disabled = (value is True)
|
161
|
+
if self._bar is not None:
|
162
|
+
self._bar.disable = self._disabled
|
163
|
+
|
164
|
+
def enable(self, value: bool = True) -> None:
|
165
|
+
self.disable(not value)
|
166
|
+
|
167
|
+
@property
|
168
|
+
def total(self) -> int:
|
169
|
+
return self._bar.total if self._bar else 0
|
170
|
+
|
171
|
+
@property
|
172
|
+
def progress(self) -> int:
|
173
|
+
return self._bar.n if self._bar else 0
|
174
|
+
|
175
|
+
@property
|
176
|
+
def disabled(self) -> bool:
|
177
|
+
return self._disabled
|
178
|
+
|
179
|
+
@property
|
180
|
+
def enabled(self) -> bool:
|
181
|
+
return not self.disabled
|
182
|
+
|
183
|
+
@property
|
184
|
+
def stop_requested(self) -> bool:
|
185
|
+
return self._stop_requested
|
186
|
+
|
187
|
+
@property
|
188
|
+
def started(self) -> None:
|
189
|
+
return self._started
|
190
|
+
|
191
|
+
@property
|
192
|
+
def duration(self) -> None:
|
193
|
+
return time.time() - self._started
|
194
|
+
|
195
|
+
def _format_description(self, value: str) -> str:
|
196
|
+
if not isinstance(value, str):
|
197
|
+
value = ""
|
198
|
+
if self._tidy_output_hack and not value.endswith(self._tidy_output_hack.sentinel):
|
199
|
+
value += self._tidy_output_hack.sentinel
|
200
|
+
return value
|
201
|
+
|
202
|
+
def _define_interrupt_handler(self) -> None:
|
203
|
+
def handle_interrupt(signum: int, frame: frame) -> None: # noqa
|
204
|
+
nonlocal self
|
205
|
+
def handle_secondary_interrupt(signum: int, frame: frame) -> None: # noqa
|
206
|
+
nonlocal self
|
207
|
+
self._printf("\nEnter 'yes' or 'no' or CTRL-\\ to completely abort ...")
|
208
|
+
self.disable()
|
209
|
+
self._interrupt(self) if self._interrupt else None
|
210
|
+
set_interrupt_handler(handle_secondary_interrupt)
|
211
|
+
if yes_or_no(f"\nALERT! You have interrupted this {self._interrupt_message or 'process'}."
|
212
|
+
f" Do you want to stop{' (exit)' if self._interrupt_exit else ''}?"):
|
213
|
+
# Here there was an interrupt (CTRL-C) and the user confirmed (yes)
|
214
|
+
# that they want to stop the process; if the interrupt_stop handler
|
215
|
+
# is defined and returns True, then we exit the entire process here,
|
216
|
+
# rather than simply returning, which is the default.
|
217
|
+
if self._interrupt_stop:
|
218
|
+
interrupt_stop = self._interrupt_stop(self)
|
219
|
+
if (interrupt_stop is True) or ((interrupt_stop is None) and (self._interrupt_exit is True)):
|
220
|
+
self.done()
|
221
|
+
restore_interrupt_handler()
|
222
|
+
if self._interrupt_exit_message:
|
223
|
+
if isinstance(interrupt_exit_message := self._interrupt_exit_message(self), str):
|
224
|
+
self._printf(interrupt_exit_message)
|
225
|
+
exit(1)
|
226
|
+
elif interrupt_stop is False or ((interrupt_stop is None) and (self._interrupt_exit is False)):
|
227
|
+
set_interrupt_handler(handle_interrupt)
|
228
|
+
interrupt_continue = self._interrupt_continue(self) if self._interrupt_continue else None
|
229
|
+
if not (interrupt_continue is False):
|
230
|
+
self.enable()
|
231
|
+
return
|
232
|
+
self._stop_requested = True
|
233
|
+
return
|
234
|
+
set_interrupt_handler(handle_interrupt)
|
235
|
+
self._interrupt_continue(self) if self._interrupt_continue else None
|
236
|
+
self.enable()
|
237
|
+
def restore_interrupt_handler() -> None: # noqa
|
238
|
+
nonlocal self, previous_interrupt_handler
|
239
|
+
set_interrupt_handler(previous_interrupt_handler)
|
240
|
+
def set_interrupt_handler(interrupt_handler: Callable) -> Optional[Callable]: # noqa
|
241
|
+
nonlocal self
|
242
|
+
if callable(interrupt_handler) and (threading.current_thread() == threading.main_thread()):
|
243
|
+
return signal(SIGINT, interrupt_handler)
|
244
|
+
return None
|
245
|
+
previous_interrupt_handler = set_interrupt_handler(handle_interrupt)
|
246
|
+
return namedtuple("interrupt_handler", ["restore"])(restore_interrupt_handler)
|
247
|
+
|
248
|
+
def _define_tidy_output_hack(self) -> None:
|
249
|
+
# Some minor tqdm output tidying-up which for annoying anomalies; tqdm forces
|
250
|
+
# a colon (:) before the percentage, e.g. ": 25%|"; and while we're at it do
|
251
|
+
# a little ASCII progress animation, requiring a special ([progress]) sentinal
|
252
|
+
# string in the display string where the progress bar should actually go,
|
253
|
+
# which we do in _format_description. Other minor things too; see below.
|
254
|
+
sys_stdout_write = sys.stdout.write
|
255
|
+
def tidy_stdout_write(text: str) -> None: # noqa
|
256
|
+
nonlocal self, sys_stdout_write, sentinel_internal, spina, spini, spinn
|
257
|
+
def replace_first(value: str, match: str, replacement: str) -> str: # noqa
|
258
|
+
return value[:i] + replacement + value[i + len(match):] if (i := value.find(match)) >= 0 else value
|
259
|
+
if (self._disabled or self._done) and sentinel_internal in text:
|
260
|
+
# Another hack to really disable output on interrupt; in this case we set
|
261
|
+
# tqdm.disable to True, but output can still dribble out, so if the output
|
262
|
+
# looks like it is from tqdm and we are disabled/done then do no output.
|
263
|
+
return
|
264
|
+
if sentinel_internal in text:
|
265
|
+
spinc = spina[spini % spinn] if not ("100%|" in text) else "| ✓" ; spini += 1 # noqa
|
266
|
+
text = replace_first(text, sentinel_internal, f" {spinc}")
|
267
|
+
text = replace_first(text, "%|", "% ◀|")
|
268
|
+
# Another oddity: for the rate sometimes tqdm intermittently prints
|
269
|
+
# something like "1.54s/" rather than "1.54/s"; something to do with
|
270
|
+
# the unit we gave, which is empty; idunno; just replace it here.
|
271
|
+
text = replace_first(text, "s/ ", "/s ")
|
272
|
+
sys_stdout_write(text)
|
273
|
+
sys.stdout.flush()
|
274
|
+
def restore_stdout_write() -> None: # noqa
|
275
|
+
nonlocal sys_stdout_write
|
276
|
+
if sys_stdout_write is not None:
|
277
|
+
sys.stdout.write = sys_stdout_write
|
278
|
+
sys.stdout.write = tidy_stdout_write
|
279
|
+
spina = ["|", "/", "—", "◦", "\\"] ; spini = 0 ; spinn = len(spina) # noqa
|
280
|
+
sentinel = "[progress]" ; sentinel_internal = f"{sentinel}:" # noqa
|
281
|
+
return namedtuple("tidy_output_hack", ["restore", "sentinel"])(restore_stdout_write, sentinel)
|
@@ -335,7 +335,7 @@ class StructuredDataSet:
|
|
335
335
|
self._add(type_name, structured_row)
|
336
336
|
if self._progress:
|
337
337
|
self._progress({
|
338
|
-
PROGRESS.LOAD_ITEM:
|
338
|
+
PROGRESS.LOAD_ITEM: self._nrows,
|
339
339
|
PROGRESS.LOAD_COUNT_REFS: self.ref_total_count,
|
340
340
|
PROGRESS.LOAD_COUNT_REFS_FOUND: self.ref_total_found_count,
|
341
341
|
PROGRESS.LOAD_COUNT_REFS_NOT_FOUND: self.ref_total_notfound_count,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "dcicutils"
|
3
|
-
version = "8.8.2"
|
3
|
+
version = "8.8.2.1b2" # TODO: To become 8.8.3
|
4
4
|
description = "Utility package for interacting with the 4DN Data Portal and other 4DN resources"
|
5
5
|
authors = ["4DN-DCIC Team <support@4dnucleome.org>"]
|
6
6
|
license = "MIT"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/license_policies/c4-python-infrastructure.jsonc
RENAMED
File without changes
|
{dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/license_policies/park-lab-common-server.jsonc
RENAMED
File without changes
|
File without changes
|
{dcicutils-8.8.2 → dcicutils-8.8.2.1b2}/dcicutils/license_policies/park-lab-gpl-pipeline.jsonc
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|