dcicutils 8.8.3.1b5__py3-none-any.whl → 8.8.3.1b8__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/progress_bar.py CHANGED
@@ -61,6 +61,8 @@ class ProgressBar:
61
61
  self._bar = None
62
62
  self._disabled = False
63
63
  self._done = False
64
+ self._really_done = False
65
+ self.foo = time.time()
64
66
  self._tidy_output_hack = (tidy_output_hack is True)
65
67
  self._started = time.time()
66
68
  self._stop_requested = False
@@ -104,11 +106,9 @@ class ProgressBar:
104
106
  return True
105
107
  return False
106
108
 
107
- def set_total(self, value: int, reset_eta: bool = False) -> None:
109
+ def set_total(self, value: int) -> None:
108
110
  if value == self._total:
109
111
  # If the total has not changed since last set then do nothing.
110
- if reset_eta and self._bar is not None:
111
- self._bar.reset()
112
112
  return
113
113
  if isinstance(value, int) and value > 0:
114
114
  self._total = value
@@ -119,16 +119,6 @@ class ProgressBar:
119
119
  self._bar.total = value
120
120
  self._bar.refresh()
121
121
 
122
- def reset_eta(self) -> None:
123
- # Since set_total does nothing if total is the same, provide
124
- # a way to reset the ETA if starting over with the same total.
125
- if self._bar is not None:
126
- progress = self._bar.n
127
- self._bar.reset()
128
- self._bar.total = self._total
129
- self._bar.n = progress
130
- self._bar.refresh()
131
-
132
122
  def set_progress(self, value: int) -> None:
133
123
  if isinstance(value, int) and value >= 0:
134
124
  if (self._bar is not None) or self._initialize():
@@ -141,17 +131,29 @@ class ProgressBar:
141
131
  self._bar.update(value)
142
132
  self._bar.refresh()
143
133
 
144
- def set_description(self, value: str) -> None:
145
- self._description = self._format_description(value)
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.
146
138
  if self._bar is not None:
147
- self._bar.set_description(self._description)
139
+ progress = self._bar.n
140
+ self._bar.reset()
141
+ self._bar.total = self._total
142
+ self._bar.n = progress
143
+ self._bar.refresh()
148
144
 
149
- def done(self) -> None:
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)
150
+
151
+ def done(self, description: Optional[str] = None) -> None:
150
152
  if self._done or self._bar is None:
151
153
  return
152
154
  self._ended = time.time()
153
155
  self.set_progress(self.total)
154
- self._bar.set_description(self._description)
156
+ self.set_description(description)
155
157
  self._bar.refresh()
156
158
  # FYI: Do NOT do a bar.disable = True before a bar.close() or it messes up output
157
159
  # on multiple calls; found out the hard way; a couple hours will never get back :-/
@@ -202,6 +204,12 @@ class ProgressBar:
202
204
  def captured_output_for_testing(self) -> Optional[List[str]]:
203
205
  return self._captured_output_for_testing
204
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
+
205
213
  def _format_description(self, value: str) -> str:
206
214
  if not isinstance(value, str):
207
215
  value = ""
@@ -261,22 +269,19 @@ class ProgressBar:
261
269
  # string in the display string where the progress bar should actually go,
262
270
  # which we do in _format_description. Other minor things too; see below.
263
271
  sys_stdout_write = sys.stdout.write
264
- last_total = None ; last_progress = None ; last_text = None # noqa
272
+ last_text = None ; last_captured_output_text = None # noqa
265
273
  def tidy_stdout_write(text: str) -> None: # noqa
266
274
  nonlocal self, sys_stdout_write, sentinel_internal, spina, spini, spinn
267
- nonlocal last_total, last_progress, last_text
275
+ nonlocal last_text, last_captured_output_text
268
276
  def replace_first(value: str, match: str, replacement: str) -> str: # noqa
269
277
  return value[:i] + replacement + value[i + len(match):] if (i := value.find(match)) >= 0 else value
270
278
  def remove_extra_trailing_spaces(text: str) -> str: # noqa
271
279
  while text.endswith(" "):
272
280
  text = text[:-1]
273
281
  return text
274
- if not text:
282
+ if (not text) or (last_text == text) or self._really_done:
275
283
  return
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
284
+ last_text = text
280
285
  if (self._disabled or self._done) and sentinel_internal in text:
281
286
  # Another hack to really disable output on interrupt; in this case we set
282
287
  # tqdm.disable to True, but output can still dribble out, so if the output
@@ -293,6 +298,9 @@ class ProgressBar:
293
298
  text = replace_first(text, "s/ ", "/s ")
294
299
  sys_stdout_write(text)
295
300
  sys.stdout.flush()
301
+ if self._done:
302
+ self._really_done = True
303
+ return
296
304
  if self._captured_output_for_testing is not None:
297
305
  # For testing only we replace vacilliting values in the out like rate,
298
306
  # time elapsed, and ETA with static values; so that something like this:
@@ -300,7 +308,7 @@ class ProgressBar:
300
308
  # becomes something more static like this after calling this function:
301
309
  # > Working | 20% ◀|### | 1/5 | 0.0/s | 00:00 | ETA: 00:00
302
310
  # This function obviously has intimate knowledge of the output; better here than in tests.
303
- def replace_vacillating_values_with_static(text: str) -> str:
311
+ def replace_time_dependent_values_with_static(text: str) -> str:
304
312
  blocks = "\u2587|\u2588|\u2589|\u258a|\u258b|\u258c|\u258d|\u258e|\u258f"
305
313
  if (n := find_nth_from_end(text, "|", 5)) >= 8:
306
314
  pattern = re.compile(
@@ -311,7 +319,11 @@ class ProgressBar:
311
319
  return (text[0:n - 6].replace("\r", "") +
312
320
  match.expand(rf"\g<1>\g<2>### \g<3>\g<4>0.0\g<5>00:00\g<6>00:00"))
313
321
  return text
314
- self._captured_output_for_testing.append(replace_vacillating_values_with_static(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
315
327
  def restore_stdout_write() -> None: # noqa
316
328
  nonlocal sys_stdout_write
317
329
  if sys_stdout_write is not None:
@@ -154,7 +154,32 @@ class StructuredDataSet:
154
154
 
155
155
  @property
156
156
  def resolved_refs_with_uuids(self) -> List[str]:
157
- return list([{"path": resolved_ref[0], "uuid": resolved_ref[1]} for resolved_ref in self._resolved_refs])
157
+ return list([{"path": resolved_ref[0],
158
+ "uuid": resolved_ref[1] if len(resolved_ref) >= 2 else None}
159
+ for resolved_ref in self._resolved_refs])
160
+
161
+ @property
162
+ def unchecked_refs(self) -> List[str]:
163
+ """
164
+ Returns list of unchecked (for existence) references, grouped by reference path;
165
+ each object in the list has a path property and a srcs property which is a list of
166
+ src objects containing the type, column and row of the reference to the reference.
167
+ Note that this is only populated if the norefs option is specified.
168
+ """
169
+ def load_json(value: str) -> Optional[dict]:
170
+ try:
171
+ return json.loads(value)
172
+ except Exception:
173
+ return None
174
+ result = []
175
+ if self._norefs:
176
+ for ref in self._resolved_refs:
177
+ if len(ref) >= 3 and (ref_path := ref[0]) and (ref_src := load_json(ref[2])):
178
+ if existing_ref := [item for item in result if item.get("path") == ref_path]:
179
+ existing_ref[0]["srcs"].append(ref_src)
180
+ else:
181
+ result.append({"path": ref_path, "srcs": [ref_src]})
182
+ return result
158
183
 
159
184
  @property
160
185
  def upload_files(self) -> List[str]:
@@ -653,10 +678,14 @@ class Schema(SchemaBase):
653
678
  def map_ref(value: str, link_to: str, portal: Optional[Portal], src: Optional[str]) -> Any:
654
679
  nonlocal self, typeinfo
655
680
  if self._norefs:
681
+ # Here the caller has specified the (StructuredDataSet) norefs option
682
+ # which means we do not check for the existence of references at all.
656
683
  if value:
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))
684
+ # Dump the src as a JSON string because a dictionary cannot be added to a set; note
685
+ # that this is ONLY used for smaht-submitr/submit-metadata-bundle --info --refs.
686
+ # This info can be gotten at using StructureDataSet.unchecked_refs.
687
+ self._resolved_refs.add((f"/{link_to}/{value}", None,
688
+ json.dumps(src) if isinstance(src, dict) else None))
660
689
  return value
661
690
  if not value:
662
691
  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.1b5
3
+ Version: 8.8.3.1b8
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
@@ -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=BWd-a1Og7SL_FNtIQv8gAkGYMyp6VgwIwTlLClsl-8g,15988
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
@@ -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=YCLGtjsCMXCm5ZM96aiTRQ9OPUuj0lK8O9vSkLx1gv8,58450
66
+ dcicutils/structured_data.py,sha256=Ho97KAqPgfn9QVfIMpjp0gS1FXLAnEJu6usdVi2lGHc,59927
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.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,,
75
+ dcicutils-8.8.3.1b8.dist-info/LICENSE.txt,sha256=qnwSmfnEWMl5l78VPDEzAmEbLVrRqQvfUQiHT0ehrOo,1102
76
+ dcicutils-8.8.3.1b8.dist-info/METADATA,sha256=saFsKdu4mbE4RBPh2331yy2nC-ZUcE5n3fT740wn9iY,3356
77
+ dcicutils-8.8.3.1b8.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
78
+ dcicutils-8.8.3.1b8.dist-info/entry_points.txt,sha256=51Q4F_2V10L0282W7HFjP4jdzW4K8lnWDARJQVFy_hw,270
79
+ dcicutils-8.8.3.1b8.dist-info/RECORD,,