rda-python-common 3.0.19__tar.gz → 3.0.21__tar.gz

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.
Files changed (34) hide show
  1. {rda_python_common-3.0.19/src/rda_python_common.egg-info → rda_python_common-3.0.21}/PKG-INFO +2 -2
  2. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/README.md +1 -1
  3. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/pyproject.toml +1 -1
  4. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common/__init__.py +1 -1
  5. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common/pg_file.py +24 -8
  6. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common/pg_log.py +5 -1
  7. {rda_python_common-3.0.19 → rda_python_common-3.0.21/src/rda_python_common.egg-info}/PKG-INFO +2 -2
  8. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/LICENSE +0 -0
  9. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/setup.cfg +0 -0
  10. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common/PgCMD.py +0 -0
  11. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common/PgDBI.py +0 -0
  12. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common/PgFile.py +0 -0
  13. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common/PgLOG.py +0 -0
  14. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common/PgLock.py +0 -0
  15. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common/PgOPT.py +0 -0
  16. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common/PgSIG.py +0 -0
  17. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common/PgSplit.py +0 -0
  18. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common/PgUtil.py +0 -0
  19. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common/pg_cmd.py +0 -0
  20. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common/pg_dbi.py +0 -0
  21. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common/pg_lock.py +0 -0
  22. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common/pg_opt.py +0 -0
  23. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common/pg_password.py +0 -0
  24. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common/pg_sig.py +0 -0
  25. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common/pg_split.py +0 -0
  26. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common/pg_util.py +0 -0
  27. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common/pgpassword.py +0 -0
  28. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common/pgpassword.usg +0 -0
  29. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common.egg-info/SOURCES.txt +0 -0
  30. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common.egg-info/dependency_links.txt +0 -0
  31. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common.egg-info/entry_points.txt +0 -0
  32. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common.egg-info/requires.txt +0 -0
  33. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/src/rda_python_common.egg-info/top_level.txt +0 -0
  34. {rda_python_common-3.0.19 → rda_python_common-3.0.21}/test/test_common.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rda_python_common
3
- Version: 3.0.19
3
+ Version: 3.0.21
4
4
  Summary: RDA Python common library codes shared by other RDA python packages
5
5
  Author-email: Zaihua Ji <zji@ucar.edu>
6
6
  Project-URL: Homepage, https://github.com/NCAR/rda-python-common
@@ -189,7 +189,7 @@ PgLOG.pglog("hello", PgLOG.LOGWRN)
189
189
  python -c "import rda_python_common; print(rda_python_common.__version__)"
190
190
  ```
191
191
 
192
- You should see the installed version (currently `3.0.19`). If the import
192
+ You should see the installed version (currently `3.0.21`). If the import
193
193
  fails, double-check that the active Python environment is the one where you
194
194
  ran `pip install`.
195
195
 
@@ -165,7 +165,7 @@ PgLOG.pglog("hello", PgLOG.LOGWRN)
165
165
  python -c "import rda_python_common; print(rda_python_common.__version__)"
166
166
  ```
167
167
 
168
- You should see the installed version (currently `3.0.19`). If the import
168
+ You should see the installed version (currently `3.0.21`). If the import
169
169
  fails, double-check that the active Python environment is the one where you
170
170
  ran `pip install`.
171
171
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "rda_python_common"
7
- version = "3.0.19"
7
+ version = "3.0.21"
8
8
  authors = [
9
9
  { name="Zaihua Ji", email="zji@ucar.edu" },
10
10
  ]
@@ -22,7 +22,7 @@ object that existing callers expect.
22
22
 
23
23
  from . import PgLOG, PgUtil, PgDBI, PgFile, PgLock, PgCMD, PgSIG, PgOPT, PgSplit
24
24
 
25
- __version__ = "3.0.19"
25
+ __version__ = "3.0.21"
26
26
 
27
27
  __all__ = [
28
28
  "PgLOG",
@@ -107,6 +107,8 @@ class PgFile(PgUtil, PgSIG):
107
107
  self.DELDIRS = {}
108
108
  self.TASKIDS = {} # cache unfinished
109
109
  self.QCANCEL = {} # taskid -> why a task got cancelled, reported by the waiting caller
110
+ self.QFAULTS = {} # taskid -> polls in a row reporting a transient Globus fault
111
+ self.QFLIMIT = 30 # give a task that long (polls of ETIME) to recover before cancelling
110
112
  self.LHOST = "localhost"
111
113
  self.OHOST = self.PGLOG['OBJCTSTR']
112
114
  self.BHOST = self.PGLOG['BACKUPNM']
@@ -118,7 +120,9 @@ class PgFile(PgUtil, PgSIG):
118
120
  # record how many errors happen for working with HPSS, local or remote machines
119
121
  self.ECNTS = {'D': 0, 'H': 0, 'L': 0, 'R': 0, 'O': 0, 'B': 0}
120
122
  # up limits for how many continuing errors allowed
121
- self.ELMTS = {'D': 20, 'H': 20, 'L': 20, 'R': 20, 'O': 10, 'B': 10}
123
+ # 'B' is 5 because a failed Globus transfer counts once; it used to be logged
124
+ # twice per failure, so the old limit of 10 also gave up after 5 failures
125
+ self.ELMTS = {'D': 20, 'H': 20, 'L': 20, 'R': 20, 'O': 10, 'B': 5}
122
126
  # down storage hostnames & paths
123
127
  self.DHOSTS = {
124
128
  'G': self.PGLOG['GPFSNAME'],
@@ -585,15 +589,18 @@ class PgFile(PgUtil, PgSIG):
585
589
  if ms:
586
590
  detail = ms.group(1)
587
591
  if detail not in astats:
588
- if logact&self.NOWAIT:
592
+ # Globus still calls the task ACTIVE, so this is a transient fault it
593
+ # keeps retrying on its own. Give it QFLIMIT polls in a row to recover
594
+ # before cancelling, counted per task so that a long transfer does not
595
+ # run out of patience simply for having been running a while
596
+ qcnt = self.QFAULTS[taskid] = self.QFAULTS.get(taskid, 0) + 1
597
+ if logact&self.NOWAIT and qcnt > self.QFLIMIT:
589
598
  # record why, and let the caller waiting on this task report it as a
590
599
  # single error naming the file; dumping the whole get-task output here
591
600
  # doubled every failure into two error entries, the first 17 lines long
592
601
  reason = detail
593
602
  ms = re.search(r'Bytes Transferred:\s+(\d+)', buf)
594
- if ms: reason += " after " + self.format_float_value(ms.group(1))
595
- ms = re.search(r'Files:\s+(\d+)', buf)
596
- if ms: reason += " of {} file(s)".format(ms.group(1))
603
+ if ms: reason += " after " + self.format_float_value(ms.group(1)) + " transferred"
597
604
  self.QCANCEL[taskid] = reason
598
605
  self.pglog("{}: Cancel Task due to {}".format(taskid, reason), self.LOGWRN)
599
606
  ccmd = f"{bcmd} cancel-task {taskid}"
@@ -601,6 +608,7 @@ class PgFile(PgUtil, PgSIG):
601
608
  else:
602
609
  time.sleep(self.PGSIG['ETIME'])
603
610
  continue
611
+ self.QFAULTS.pop(taskid, None) # recovered, start the count over
604
612
  break
605
613
  errmsg = "Error Execute: " + cmd
606
614
  if self.PGLOG['SYSERR']:
@@ -608,11 +616,14 @@ class PgFile(PgUtil, PgSIG):
608
616
  (hstat, msg) = self.host_down_status('', self.QHOSTS[endpoint], 1, logact)
609
617
  if hstat: errmsg += "\n" + msg
610
618
  self.errlog(errmsg, 'B', loop, logact)
611
- if ret == 'S' or ret == 'A': self.ECNTS['B'] = 0 # reset error count
619
+ if ret == 'S' or ret == 'A':
620
+ self.ECNTS['B'] = 0 # reset error count
621
+ else:
622
+ self.QFAULTS.pop(taskid, None) # task is over, stop tracking its faults
612
623
  return ret
613
624
 
614
625
  # return SUCCESS if Globus transfer is done; FAILURE otherwise
615
- def check_globus_finished(self, tofile, topoint, logact = 0):
626
+ def check_globus_finished(self, tofile, topoint, logact = 0, fcnt = 0):
616
627
  """Block until a previously submitted Globus task completes.
617
628
 
618
629
  Looks up the task ID in self.TASKIDS using 'endpoint-file' as the key.
@@ -623,6 +634,8 @@ class PgFile(PgUtil, PgSIG):
623
634
  tofile (str): Destination file path used to look up the task key.
624
635
  topoint (str): Destination Globus endpoint name.
625
636
  logact (int): Logging action flags; default 0.
637
+ fcnt (int): File count of a batch task, to name the files the key cannot;
638
+ default 0 for a single-file task.
626
639
 
627
640
  Returns:
628
641
  int: self.SUCCESS on completion, self.FAILURE on error or non-success status.
@@ -653,7 +666,10 @@ class PgFile(PgUtil, PgSIG):
653
666
  del self.TASKIDS[ckey]
654
667
  else:
655
668
  status = self.QSTATS[stat] if stat in self.QSTATS else 'UNKNOWN'
656
- errmsg = "{}: Status '{}' for Task {}".format(ckey, status, taskid)
669
+ # a batch task is keyed by its first file only, so name the whole batch
670
+ # instead of blaming the one file the key happens to carry
671
+ bmsg = " and {} more file(s)".format(fcnt - 1) if fcnt > 1 else ''
672
+ errmsg = "{}{}: Status '{}' for Task {}".format(ckey, bmsg, status, taskid)
657
673
  if taskid in self.QCANCEL:
658
674
  errmsg += " - " + self.QCANCEL.pop(taskid)
659
675
  self.errlog(errmsg, 'B', 1, logact)
@@ -249,9 +249,13 @@ class PgLOG:
249
249
  if self.PGLOG['PRGMSG']:
250
250
  msg = self.PGLOG['PRGMSG'] + "\n" + msg
251
251
  self.PGLOG['PRGMSG'] = ""
252
+ # callers usually end the message with their own '!', so do not add a second
253
+ # one, and drop it before continuing the sentence with the error count
252
254
  if self.PGLOG['ERRCNT'] == 0:
253
- if not msg.endswith('\n'): msg += "!\n"
255
+ if not msg.endswith('\n'):
256
+ msg += "\n" if msg.endswith('!') else "!\n"
254
257
  else:
258
+ if msg.endswith('!'): msg = msg[:-1]
255
259
  if self.PGLOG['ERRCNT'] == 1:
256
260
  msg += " with 1 Error:\n"
257
261
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rda_python_common
3
- Version: 3.0.19
3
+ Version: 3.0.21
4
4
  Summary: RDA Python common library codes shared by other RDA python packages
5
5
  Author-email: Zaihua Ji <zji@ucar.edu>
6
6
  Project-URL: Homepage, https://github.com/NCAR/rda-python-common
@@ -189,7 +189,7 @@ PgLOG.pglog("hello", PgLOG.LOGWRN)
189
189
  python -c "import rda_python_common; print(rda_python_common.__version__)"
190
190
  ```
191
191
 
192
- You should see the installed version (currently `3.0.19`). If the import
192
+ You should see the installed version (currently `3.0.21`). If the import
193
193
  fails, double-check that the active Python environment is the one where you
194
194
  ran `pip install`.
195
195