dcicutils 8.8.3.1b3__py3-none-any.whl → 8.8.3.1b5__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 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):
@@ -259,32 +261,29 @@ class ProgressBar:
259
261
  # string in the display string where the progress bar should actually go,
260
262
  # which we do in _format_description. Other minor things too; see below.
261
263
  sys_stdout_write = sys.stdout.write
262
- total_most_recent = None
263
- progress_most_recent = None
264
- description_most_recent = None
264
+ last_total = None ; last_progress = None ; last_text = None # noqa
265
265
  def tidy_stdout_write(text: str) -> None: # noqa
266
266
  nonlocal self, sys_stdout_write, sentinel_internal, spina, spini, spinn
267
- nonlocal total_most_recent, progress_most_recent, description_most_recent
267
+ nonlocal last_total, last_progress, last_text
268
268
  def replace_first(value: str, match: str, replacement: str) -> str: # noqa
269
269
  return value[:i] + replacement + value[i + len(match):] if (i := value.find(match)) >= 0 else value
270
270
  def remove_extra_trailing_spaces(text: str) -> str: # noqa
271
271
  while text.endswith(" "):
272
272
  text = text[:-1]
273
273
  return text
274
- if not text or not self._bar:
274
+ if not text:
275
275
  return
276
- if (self._bar.total == total_most_recent) and (self._bar.n == progress_most_recent):
277
- return
278
- total_most_recent = self._bar.total
279
- progress_most_recent = self._bar.n
280
- description_most_recent = self._description
276
+ if self._bar:
277
+ if ((self._bar.total == last_total) and (self._bar.n == last_progress) and (last_text == text)):
278
+ return
279
+ last_total = self._bar.total ; last_progress = self._bar.n ; last_text = text # noqa
281
280
  if (self._disabled or self._done) and sentinel_internal in text:
282
281
  # Another hack to really disable output on interrupt; in this case we set
283
282
  # tqdm.disable to True, but output can still dribble out, so if the output
284
283
  # looks like it is from tqdm and we are disabled/done then do no output.
285
284
  return
286
285
  if sentinel_internal in text:
287
- spinc = spina[spini % spinn] if not ("100%|" in text) else "| ✓" ; spini += 1 # noqa
286
+ spinc = spina[spini % spinn] if not ("100%|" in text) else "✓" ; spini += 1 # noqa
288
287
  text = replace_first(text, sentinel_internal, f" {spinc}")
289
288
  text = replace_first(text, "%|", "% ◀|")
290
289
  text = remove_extra_trailing_spaces(text)
@@ -293,9 +292,26 @@ class ProgressBar:
293
292
  # the unit we gave, which is empty; idunno; just replace it here.
294
293
  text = replace_first(text, "s/ ", "/s ")
295
294
  sys_stdout_write(text)
296
- if self._captured_output_for_testing is not None:
297
- self._captured_output_for_testing.append(text)
298
295
  sys.stdout.flush()
296
+ if self._captured_output_for_testing is not None:
297
+ # For testing only we replace vacilliting values in the out like rate,
298
+ # time elapsed, and ETA with static values; so that something like this:
299
+ # > Working / 20% ◀|█████████▌ | 1/5 | 536.00/s | 00:01 | ETA: 00:02
300
+ # becomes something more static like this after calling this function:
301
+ # > Working | 20% ◀|### | 1/5 | 0.0/s | 00:00 | ETA: 00:00
302
+ # This function obviously has intimate knowledge of the output; better here than in tests.
303
+ def replace_vacillating_values_with_static(text: str) -> str:
304
+ blocks = "\u2587|\u2588|\u2589|\u258a|\u258b|\u258c|\u258d|\u258e|\u258f"
305
+ if (n := find_nth_from_end(text, "|", 5)) >= 8:
306
+ pattern = re.compile(
307
+ rf"(\s*)(\d*%? ◀\|)(?:\s*{blocks}|#)*\s*(\|\s*\d+/\d+)?(\s*\|\s*)"
308
+ rf"(?:\d+\.?\d*|\?)(\/s\s*\|\s*)(?:\d+:\d+)?(\s*\|\s*ETA:\s*)(?:\d+:\d+|\?)?")
309
+ if match := pattern.match(text[n - 6:]):
310
+ if text[n - 8:n - 7] != "✓": text = set_nth(text, n - 8, "|") # noqa
311
+ return (text[0:n - 6].replace("\r", "") +
312
+ match.expand(rf"\g<1>\g<2>### \g<3>\g<4>0.0\g<5>00:00\g<6>00:00"))
313
+ return text
314
+ self._captured_output_for_testing.append(replace_vacillating_values_with_static(text))
299
315
  def restore_stdout_write() -> None: # noqa
300
316
  nonlocal sys_stdout_write
301
317
  if sys_stdout_write is not None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dcicutils
3
- Version: 8.8.3.1b3
3
+ Version: 8.8.3.1b5
4
4
  Summary: Utility package for interacting with the 4DN Data Portal and other 4DN resources
5
5
  Home-page: https://github.com/4dn-dcic/utils
6
6
  License: MIT
@@ -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=zVc4urdVGgnWjQ4UQlrGH-URAzr2l_PwZWI3u_GJdFE,102210
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=hEY3YyBHkmGGwyYLzoQ7YGhMcoBATmGt0h64aWHHZTY,14543
51
+ dcicutils/progress_bar.py,sha256=BWd-a1Og7SL_FNtIQv8gAkGYMyp6VgwIwTlLClsl-8g,15988
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.1b3.dist-info/LICENSE.txt,sha256=qnwSmfnEWMl5l78VPDEzAmEbLVrRqQvfUQiHT0ehrOo,1102
76
- dcicutils-8.8.3.1b3.dist-info/METADATA,sha256=gphVd0GbW3hvs3vg9d6xDngibRhVMClCQAuOt_3N7Mo,3356
77
- dcicutils-8.8.3.1b3.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
78
- dcicutils-8.8.3.1b3.dist-info/entry_points.txt,sha256=51Q4F_2V10L0282W7HFjP4jdzW4K8lnWDARJQVFy_hw,270
79
- dcicutils-8.8.3.1b3.dist-info/RECORD,,
75
+ dcicutils-8.8.3.1b5.dist-info/LICENSE.txt,sha256=qnwSmfnEWMl5l78VPDEzAmEbLVrRqQvfUQiHT0ehrOo,1102
76
+ dcicutils-8.8.3.1b5.dist-info/METADATA,sha256=Z9Not6bJDezXyZb3afrGADA6mRDuOd2QbETxSbht6DM,3356
77
+ dcicutils-8.8.3.1b5.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
78
+ dcicutils-8.8.3.1b5.dist-info/entry_points.txt,sha256=51Q4F_2V10L0282W7HFjP4jdzW4K8lnWDARJQVFy_hw,270
79
+ dcicutils-8.8.3.1b5.dist-info/RECORD,,