dcicutils 8.8.6.1b10__py3-none-any.whl → 8.8.6.1b12__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.
@@ -1,3 +1,4 @@
1
+ from __future__ import annotations
1
2
  import contextlib
2
3
  import functools
3
4
  import glob
@@ -7,7 +8,7 @@ import re
7
8
  import requests
8
9
  import subprocess
9
10
 
10
- from typing import Optional
11
+ from typing import Callable, Optional
11
12
  from .exceptions import InvalidParameterError
12
13
  from .lang_utils import there_are
13
14
  from .misc_utils import INPUT, PRINT, environ_bool, print_error_message, decorator
@@ -384,3 +385,70 @@ def script_catch_errors():
384
385
  message = str(e) # Note: We ignore the type, which isn't intended to be shown.
385
386
  PRINT(message)
386
387
  exit(1)
388
+
389
+
390
+ class Question:
391
+ """
392
+ Supports asking the user (via stdin) a yes/no question, possibly repeatedly; and after
393
+ some maximum number times of the same answer in a row (consecutively), then asks them
394
+ if they want to automatically give that same answer to any/all subsequent questions.
395
+ Supports static/global list of such Question instances, hashed (only) by the question text.
396
+ """
397
+ _static_instances = {}
398
+
399
+ @staticmethod
400
+ def instance(question: Optional[str] = None,
401
+ max: Optional[int] = None, printf: Optional[Callable] = None) -> Question:
402
+ question = question if isinstance(question, str) else ""
403
+ if not (instance := Question._static_instances.get(question)):
404
+ Question._static_instances[question] = (instance := Question(question, max=max, printf=printf))
405
+ return instance
406
+
407
+ @staticmethod
408
+ def yes(question: Optional[str] = None,
409
+ max: Optional[int] = None, printf: Optional[Callable] = None) -> bool:
410
+ return Question.instance(question, max=max, printf=printf).ask()
411
+
412
+ def __init__(self, question: Optional[str] = None,
413
+ max: Optional[int] = None, printf: Optional[Callable] = None) -> None:
414
+ self._question = question if isinstance(question, str) else ""
415
+ self._max = max if isinstance(max, int) and max > 0 else None
416
+ self._print = printf if callable(printf) else print
417
+ self._yes_consecutive_count = 0
418
+ self._no_consecutive_count = 0
419
+ self._yes_automatic = False
420
+ self._no_automatic = False
421
+
422
+ def ask(self, question: Optional[str] = None) -> bool:
423
+
424
+ def question_automatic(value: str) -> bool:
425
+ nonlocal self
426
+ RARROW = "▶"
427
+ LARROW = "◀"
428
+ if yes_or_no(f"{RARROW}{RARROW}{RARROW}"
429
+ f" Do you want to answer {value} to all such questions?"
430
+ f" {LARROW}{LARROW}{LARROW}"):
431
+ return True
432
+ self._yes_consecutive_count = 0
433
+ self._no_consecutive_count = 0
434
+
435
+ if self._yes_automatic:
436
+ return True
437
+ elif self._no_automatic:
438
+ return False
439
+ elif yes_or_no((question if isinstance(question, str) else "") or self._question or "Undefined question"):
440
+ self._yes_consecutive_count += 1
441
+ self._no_consecutive_count = 0
442
+ if (self._no_consecutive_count == 0) and self._max and (self._yes_consecutive_count >= self._max):
443
+ # Have reached the maximum number of consecutive YES answers; ask if YES to all subsequent.
444
+ if question_automatic("YES"):
445
+ self._yes_automatic = True
446
+ return True
447
+ else:
448
+ self._no_consecutive_count += 1
449
+ self._yes_consecutive_count = 0
450
+ if (self._yes_consecutive_count == 0) and self._max and (self._no_consecutive_count >= self._max):
451
+ # Have reached the maximum number of consecutive NO answers; ask if NO to all subsequent.
452
+ if question_automatic("NO"):
453
+ self._no_automatic = True
454
+ return False
dcicutils/portal_utils.py CHANGED
@@ -407,6 +407,7 @@ class Portal:
407
407
 
408
408
  @function_cache(maxsize=100, serialize_key=True)
409
409
  def get_identifying_paths(self, portal_object: dict, portal_type: Optional[Union[str, dict]] = None,
410
+ first_only: bool = False,
410
411
  lookup_strategy: Optional[Union[Callable, bool]] = None) -> List[str]:
411
412
  """
412
413
  Returns the list of the identifying Portal (URL) paths for the given Portal object. Favors any uuid
@@ -467,7 +468,10 @@ class Portal:
467
468
  # And note the disction of just using /{uuid} here rather than /{type}/{uuid} as in the else
468
469
  # statement below is not really necessary; just here for emphasis that this is all that's needed.
469
470
  #
470
- results.append(f"/{identifying_value}")
471
+ if first_only is True:
472
+ results.append(f"/{portal_type}/{identifying_value}")
473
+ else:
474
+ results.append(f"/{identifying_value}")
471
475
  elif isinstance(identifying_value, list):
472
476
  for identifying_value_item in identifying_value:
473
477
  if identifying_value_item:
@@ -497,12 +501,15 @@ class Portal:
497
501
  if is_lookup_subtypes(lookup_options):
498
502
  for subtype_name in self.get_schema_subtype_names(portal_type):
499
503
  results.append(f"/{subtype_name}/{identifying_value}")
504
+ if (first_only is True) and results:
505
+ return results
500
506
  return results
501
507
 
502
508
  @function_cache(maxsize=100, serialize_key=True)
503
509
  def get_identifying_path(self, portal_object: dict, portal_type: Optional[Union[str, dict]] = None,
504
510
  lookup_strategy: Optional[Union[Callable, bool]] = None) -> Optional[str]:
505
- if identifying_paths := self.get_identifying_paths(portal_object, portal_type, lookup_strategy):
511
+ if identifying_paths := self.get_identifying_paths(portal_object, portal_type, first_only=True,
512
+ lookup_strategy=lookup_strategy):
506
513
  return identifying_paths[0]
507
514
  return None
508
515
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dcicutils
3
- Version: 8.8.6.1b10
3
+ Version: 8.8.6.1b12
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
@@ -5,7 +5,7 @@ dcicutils/bundle_utils.py,sha256=ZVQcqlt7Yly8-YbL3A-5DW859_hMWpTL6dXtknEYZIw,346
5
5
  dcicutils/captured_output.py,sha256=0hP7sPwleMaYXQAvCfJOxG8Z8T_JJYy8ADp8A5ZoblE,3295
6
6
  dcicutils/cloudformation_utils.py,sha256=MtWJrSTXyiImgbPHgRvfH9bWso20ZPLTFJAfhDQSVj4,13786
7
7
  dcicutils/codebuild_utils.py,sha256=CKpmhJ-Z8gYbkt1I2zyMlKtFdsg7T8lqrx3V5ieta-U,1155
8
- dcicutils/command_utils.py,sha256=JExll5TMqIcmuiGvuS8q4XDUvoEfi2oSH0E2FVF6suU,15285
8
+ dcicutils/command_utils.py,sha256=1_h18LGX86sLAkRkH33HNmBkwMb7v2wAh3jL01hzceU,18487
9
9
  dcicutils/common.py,sha256=YE8Mt5-vaZWWz4uaChSVhqGFbFtW5QKtnIyOr4zG4vM,3955
10
10
  dcicutils/contribution_scripts.py,sha256=0k5Gw1TumcD5SAcXVkDd6-yvuMEw-jUp5Kfb7FJH6XQ,2015
11
11
  dcicutils/contribution_utils.py,sha256=vYLS1JUB3sKd24BUxZ29qUBqYeQBLK9cwo8x3k64uPg,25653
@@ -48,7 +48,7 @@ dcicutils/misc_utils.py,sha256=zHwsxxEn24muLBP7mDvMa8I9VdMejwW8HMuCL5xbhhw,10769
48
48
  dcicutils/obfuscation_utils.py,sha256=fo2jOmDRC6xWpYX49u80bVNisqRRoPskFNX3ymFAmjw,5963
49
49
  dcicutils/opensearch_utils.py,sha256=V2exmFYW8Xl2_pGFixF4I2Cc549Opwe4PhFi5twC0M8,1017
50
50
  dcicutils/portal_object_utils.py,sha256=Az3n1aL-PQkN5gOFE6ZqC2XkYsqiwKlq7-tZggs1QN4,11062
51
- dcicutils/portal_utils.py,sha256=LMewNfPBSYBZuFQyJ4TWUBAO0oQUCyFfL4fxeNd50XA,44254
51
+ dcicutils/portal_utils.py,sha256=cDMaqEW3aSDwhjJlsaVS4yEpYsmWo6yVXnLA9f4J_JY,44621
52
52
  dcicutils/progress_bar.py,sha256=UT7lxb-rVF_gp4yjY2Tg4eun1naaH__hB4_v3O85bcE,19468
53
53
  dcicutils/project_utils.py,sha256=qPdCaFmWUVBJw4rw342iUytwdQC0P-XKpK4mhyIulMM,31250
54
54
  dcicutils/qa_checkers.py,sha256=cdXjeL0jCDFDLT8VR8Px78aS10hwNISOO5G_Zv2TZ6M,20534
@@ -73,8 +73,8 @@ dcicutils/trace_utils.py,sha256=g8kwV4ebEy5kXW6oOrEAUsurBcCROvwtZqz9fczsGRE,1769
73
73
  dcicutils/validation_utils.py,sha256=cMZIU2cY98FYtzK52z5WUYck7urH6JcqOuz9jkXpqzg,14797
74
74
  dcicutils/variant_utils.py,sha256=2H9azNx3xAj-MySg-uZ2SFqbWs4kZvf61JnK6b-h4Qw,4343
75
75
  dcicutils/zip_utils.py,sha256=_Y9EmL3D2dUZhxucxHvrtmmlbZmK4FpSsHEb7rGSJLU,3265
76
- dcicutils-8.8.6.1b10.dist-info/LICENSE.txt,sha256=qnwSmfnEWMl5l78VPDEzAmEbLVrRqQvfUQiHT0ehrOo,1102
77
- dcicutils-8.8.6.1b10.dist-info/METADATA,sha256=IZMgaX7IPVYSBG8--yorVXR_4HaUfjNebiY1MDRFAPk,3440
78
- dcicutils-8.8.6.1b10.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
79
- dcicutils-8.8.6.1b10.dist-info/entry_points.txt,sha256=51Q4F_2V10L0282W7HFjP4jdzW4K8lnWDARJQVFy_hw,270
80
- dcicutils-8.8.6.1b10.dist-info/RECORD,,
76
+ dcicutils-8.8.6.1b12.dist-info/LICENSE.txt,sha256=qnwSmfnEWMl5l78VPDEzAmEbLVrRqQvfUQiHT0ehrOo,1102
77
+ dcicutils-8.8.6.1b12.dist-info/METADATA,sha256=YjYoBsWaNZWQLcuG2lQWxpOo5ZrJfm3eD66dIdSaNTI,3440
78
+ dcicutils-8.8.6.1b12.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
79
+ dcicutils-8.8.6.1b12.dist-info/entry_points.txt,sha256=51Q4F_2V10L0282W7HFjP4jdzW4K8lnWDARJQVFy_hw,270
80
+ dcicutils-8.8.6.1b12.dist-info/RECORD,,