dcicutils 8.8.3.1b1__py3-none-any.whl → 8.8.3.1b3__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.
@@ -9,7 +9,7 @@ _real_stderr = sys.stderr
9
9
 
10
10
 
11
11
  @contextmanager
12
- def captured_output(capture: bool = True):
12
+ def captured_output(capture: bool = True, encoding: Optional[str] = None):
13
13
  """
14
14
  Context manager to capture any/all output to stdout or stderr, and not actually output it to stdout
15
15
  or stderr. Yields and object with a get_captured_output() method to get the output captured thus far,
@@ -24,7 +24,7 @@ def captured_output(capture: bool = True):
24
24
 
25
25
  original_stdout = _real_stdout
26
26
  original_stderr = _real_stderr
27
- captured_output = io.StringIO()
27
+ captured_output = io.StringIO() if not encoding else _EncodedStringIO(encoding)
28
28
 
29
29
  def set_original_output() -> None:
30
30
  sys.stdout = original_stdout
@@ -68,3 +68,19 @@ def uncaptured_output():
68
68
  finally:
69
69
  sys.stdout = original_stdout
70
70
  sys.stderr = original_stderr
71
+
72
+
73
+ class _EncodedStringIO:
74
+ def __init__(self, encoding: str = "utf-8"):
75
+ self.encoding = encoding
76
+ self.buffer = io.BytesIO()
77
+ def write(self, s): # noqa
78
+ self.buffer.write(s.encode(self.encoding))
79
+ def flush(self): # noqa
80
+ self.buffer.flush()
81
+ def getvalue(self): # noqa
82
+ return self.buffer.getvalue().decode(self.encoding)
83
+ def __str__(self): # noqa
84
+ return self.getvalue()
85
+ def __repr__(self): # noqa
86
+ return repr(self.getvalue())
dcicutils/progress_bar.py CHANGED
@@ -5,7 +5,7 @@ import threading
5
5
  import time
6
6
  from tqdm import tqdm
7
7
  from types import FrameType as frame
8
- from typing import Callable, Optional, Union
8
+ from typing import Callable, List, Optional, Union
9
9
  from contextlib import contextmanager
10
10
  from dcicutils.command_utils import yes_or_no
11
11
 
@@ -54,12 +54,11 @@ class ProgressBar:
54
54
  interrupt_exit: bool = False,
55
55
  interrupt_exit_message: Optional[Union[Callable, str]] = None,
56
56
  interrupt_message: Optional[str] = None,
57
- printf: Optional[Callable] = None,
58
- tidy_output_hack: bool = True) -> None:
57
+ tidy_output_hack: bool = True,
58
+ capture_output_for_testing: bool = False) -> None:
59
59
  self._bar = None
60
60
  self._disabled = False
61
61
  self._done = False
62
- self._printf = printf if callable(printf) else print
63
62
  self._tidy_output_hack = (tidy_output_hack is True)
64
63
  self._started = time.time()
65
64
  self._stop_requested = False
@@ -90,7 +89,7 @@ class ProgressBar:
90
89
  self._tidy_output_hack = self._define_tidy_output_hack()
91
90
  self._total = total if isinstance(total, int) and total >= 0 else 0
92
91
  self._description = self._format_description(description)
93
- # self._initialize()
92
+ self._captured_output_for_testing = [] if capture_output_for_testing else None
94
93
 
95
94
  def _initialize(self) -> bool:
96
95
  # Do not actually create the tqdm object unless/until we have a positive total.
@@ -197,6 +196,10 @@ class ProgressBar:
197
196
  def duration(self) -> None:
198
197
  return time.time() - self._started
199
198
 
199
+ @property
200
+ def captured_output_for_testing(self) -> Optional[List[str]]:
201
+ return self._captured_output_for_testing
202
+
200
203
  def _format_description(self, value: str) -> str:
201
204
  if not isinstance(value, str):
202
205
  value = ""
@@ -208,8 +211,7 @@ class ProgressBar:
208
211
  def handle_interrupt(signum: int, frame: frame) -> None: # noqa
209
212
  nonlocal self
210
213
  def handle_secondary_interrupt(signum: int, frame: frame) -> None: # noqa
211
- nonlocal self
212
- self._printf("\nEnter 'yes' or 'no' or CTRL-\\ to completely abort ...")
214
+ print("\nEnter 'yes' or 'no' or CTRL-\\ to completely abort ...")
213
215
  self.disable()
214
216
  self._interrupt(self) if self._interrupt else None
215
217
  set_interrupt_handler(handle_secondary_interrupt)
@@ -226,7 +228,7 @@ class ProgressBar:
226
228
  restore_interrupt_handler()
227
229
  if self._interrupt_exit_message:
228
230
  if isinstance(interrupt_exit_message := self._interrupt_exit_message(self), str):
229
- self._printf(interrupt_exit_message)
231
+ print(interrupt_exit_message)
230
232
  exit(1)
231
233
  elif interrupt_stop is False or ((interrupt_stop is None) and (self._interrupt_exit is False)):
232
234
  set_interrupt_handler(handle_interrupt)
@@ -257,10 +259,25 @@ class ProgressBar:
257
259
  # string in the display string where the progress bar should actually go,
258
260
  # which we do in _format_description. Other minor things too; see below.
259
261
  sys_stdout_write = sys.stdout.write
262
+ total_most_recent = None
263
+ progress_most_recent = None
264
+ description_most_recent = None
260
265
  def tidy_stdout_write(text: str) -> None: # noqa
261
266
  nonlocal self, sys_stdout_write, sentinel_internal, spina, spini, spinn
267
+ nonlocal total_most_recent, progress_most_recent, description_most_recent
262
268
  def replace_first(value: str, match: str, replacement: str) -> str: # noqa
263
269
  return value[:i] + replacement + value[i + len(match):] if (i := value.find(match)) >= 0 else value
270
+ def remove_extra_trailing_spaces(text: str) -> str: # noqa
271
+ while text.endswith(" "):
272
+ text = text[:-1]
273
+ return text
274
+ if not text or not self._bar:
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
264
281
  if (self._disabled or self._done) and sentinel_internal in text:
265
282
  # Another hack to really disable output on interrupt; in this case we set
266
283
  # tqdm.disable to True, but output can still dribble out, so if the output
@@ -270,11 +287,14 @@ class ProgressBar:
270
287
  spinc = spina[spini % spinn] if not ("100%|" in text) else "| ✓" ; spini += 1 # noqa
271
288
  text = replace_first(text, sentinel_internal, f" {spinc}")
272
289
  text = replace_first(text, "%|", "% ◀|")
290
+ text = remove_extra_trailing_spaces(text)
273
291
  # Another oddity: for the rate sometimes tqdm intermittently prints
274
292
  # something like "1.54s/" rather than "1.54/s"; something to do with
275
293
  # the unit we gave, which is empty; idunno; just replace it here.
276
294
  text = replace_first(text, "s/ ", "/s ")
277
295
  sys_stdout_write(text)
296
+ if self._captured_output_for_testing is not None:
297
+ self._captured_output_for_testing.append(text)
278
298
  sys.stdout.flush()
279
299
  def restore_stdout_write() -> None: # noqa
280
300
  nonlocal sys_stdout_write
@@ -654,7 +654,9 @@ class Schema(SchemaBase):
654
654
  nonlocal self, typeinfo
655
655
  if self._norefs:
656
656
  if value:
657
- self._resolved_refs.add((f"/{link_to}/{value}", None))
657
+ # Dump the src because this cannot add dictionary to a set; note that
658
+ # this is ONLY used for smaht-submitr/submit-metadata-bundle --info --refs.
659
+ self._resolved_refs.add((f"/{link_to}/{value}", json.dumps(src) if isinstance(src, dict) else None))
658
660
  return value
659
661
  if not value:
660
662
  if (column := typeinfo.get("column")) and column in self.data.get("required", []):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dcicutils
3
- Version: 8.8.3.1b1
3
+ Version: 8.8.3.1b3
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
@@ -2,7 +2,7 @@ dcicutils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  dcicutils/base.py,sha256=gxNEv3DSVUfoX3NToWw7pcCdguxsJ75NDqsPi3wdFG4,5115
3
3
  dcicutils/beanstalk_utils.py,sha256=nHMWfFnZAXFiJh60oVouwbAPMKsQfHnDtkwz_PDE6S4,51434
4
4
  dcicutils/bundle_utils.py,sha256=ZVQcqlt7Yly8-YbL3A-5DW859_hMWpTL6dXtknEYZIw,34669
5
- dcicutils/captured_output.py,sha256=9UorUoA-RSGf7fUILHy73JiZ-mJ1IV-or_JGMyncaMs,2522
5
+ dcicutils/captured_output.py,sha256=2pzkq7vtC47zwsSeXS6cJNxC_LXpUbw6ArudKlJHdb0,3108
6
6
  dcicutils/cloudformation_utils.py,sha256=MtWJrSTXyiImgbPHgRvfH9bWso20ZPLTFJAfhDQSVj4,13786
7
7
  dcicutils/codebuild_utils.py,sha256=CKpmhJ-Z8gYbkt1I2zyMlKtFdsg7T8lqrx3V5ieta-U,1155
8
8
  dcicutils/command_utils.py,sha256=JExll5TMqIcmuiGvuS8q4XDUvoEfi2oSH0E2FVF6suU,15285
@@ -48,7 +48,7 @@ dcicutils/obfuscation_utils.py,sha256=fo2jOmDRC6xWpYX49u80bVNisqRRoPskFNX3ymFAmj
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=tGhqjONjDIjniqKUb8ATQuZmnJjG61XEu-EiY4uKMK4,13554
51
+ dcicutils/progress_bar.py,sha256=hEY3YyBHkmGGwyYLzoQ7YGhMcoBATmGt0h64aWHHZTY,14543
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
@@ -63,7 +63,7 @@ dcicutils/secrets_utils.py,sha256=8dppXAsiHhJzI6NmOcvJV5ldvKkQZzh3Fl-cb8Wm7MI,19
63
63
  dcicutils/sheet_utils.py,sha256=VlmzteONW5VF_Q4vo0yA5vesz1ViUah1MZ_yA1rwZ0M,33629
64
64
  dcicutils/snapshot_utils.py,sha256=ymP7PXH6-yEiXAt75w0ldQFciGNqWBClNxC5gfX2FnY,22961
65
65
  dcicutils/ssl_certificate_utils.py,sha256=F0ifz_wnRRN9dfrfsz7aCp4UDLgHEY8LaK7PjnNvrAQ,9707
66
- dcicutils/structured_data.py,sha256=oNtEB8486n8vz7DkmXAG13aiiXosYDkuA6rjLm_xFdE,58218
66
+ dcicutils/structured_data.py,sha256=YCLGtjsCMXCm5ZM96aiTRQ9OPUuj0lK8O9vSkLx1gv8,58450
67
67
  dcicutils/submitr/progress_constants.py,sha256=5bxyX77ql8qEJearfHEvsvXl7D0GuUODW0T65mbRmnE,2895
68
68
  dcicutils/submitr/ref_lookup_strategy.py,sha256=Js2cVznTmgjciLWBPLCvMiwLIHXjDn3jww-gJPjYuFw,3467
69
69
  dcicutils/task_utils.py,sha256=MF8ujmTD6-O2AC2gRGPHyGdUrVKgtr8epT5XU8WtNjk,8082
@@ -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.1b1.dist-info/LICENSE.txt,sha256=qnwSmfnEWMl5l78VPDEzAmEbLVrRqQvfUQiHT0ehrOo,1102
76
- dcicutils-8.8.3.1b1.dist-info/METADATA,sha256=d1_yVzCwbm3XAKgp5WKTI4sBlQWG-yEIPBLlWpqtxvQ,3356
77
- dcicutils-8.8.3.1b1.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
78
- dcicutils-8.8.3.1b1.dist-info/entry_points.txt,sha256=51Q4F_2V10L0282W7HFjP4jdzW4K8lnWDARJQVFy_hw,270
79
- dcicutils-8.8.3.1b1.dist-info/RECORD,,
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,,