dcicutils 8.8.3.1b4__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):
@@ -281,7 +283,7 @@ class ProgressBar:
281
283
  # looks like it is from tqdm and we are disabled/done then do no output.
282
284
  return
283
285
  if sentinel_internal in text:
284
- 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
285
287
  text = replace_first(text, sentinel_internal, f" {spinc}")
286
288
  text = replace_first(text, "%|", "% ◀|")
287
289
  text = remove_extra_trailing_spaces(text)
@@ -290,9 +292,26 @@ class ProgressBar:
290
292
  # the unit we gave, which is empty; idunno; just replace it here.
291
293
  text = replace_first(text, "s/ ", "/s ")
292
294
  sys_stdout_write(text)
293
- if self._captured_output_for_testing is not None:
294
- self._captured_output_for_testing.append(text)
295
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))
296
315
  def restore_stdout_write() -> None: # noqa
297
316
  nonlocal sys_stdout_write
298
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.1b4
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=ZuWJJ7FAkv8ndU1vwcg-0TAG76Tm05m3-d97G4b7YzE,14463
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.1b4.dist-info/LICENSE.txt,sha256=qnwSmfnEWMl5l78VPDEzAmEbLVrRqQvfUQiHT0ehrOo,1102
76
- dcicutils-8.8.3.1b4.dist-info/METADATA,sha256=SU3-GNJsxb2pu-dBBV8wXLAigORCxsI0-oYH6uE2gdc,3356
77
- dcicutils-8.8.3.1b4.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
78
- dcicutils-8.8.3.1b4.dist-info/entry_points.txt,sha256=51Q4F_2V10L0282W7HFjP4jdzW4K8lnWDARJQVFy_hw,270
79
- dcicutils-8.8.3.1b4.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,,