rda-python-common 3.0.17__tar.gz → 3.0.19__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.17/src/rda_python_common.egg-info → rda_python_common-3.0.19}/PKG-INFO +2 -2
  2. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/README.md +1 -1
  3. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/pyproject.toml +1 -1
  4. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common/__init__.py +1 -1
  5. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common/pg_cmd.py +8 -0
  6. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common/pg_file.py +21 -5
  7. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common/pg_log.py +13 -5
  8. {rda_python_common-3.0.17 → rda_python_common-3.0.19/src/rda_python_common.egg-info}/PKG-INFO +2 -2
  9. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/LICENSE +0 -0
  10. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/setup.cfg +0 -0
  11. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common/PgCMD.py +0 -0
  12. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common/PgDBI.py +0 -0
  13. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common/PgFile.py +0 -0
  14. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common/PgLOG.py +0 -0
  15. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common/PgLock.py +0 -0
  16. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common/PgOPT.py +0 -0
  17. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common/PgSIG.py +0 -0
  18. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common/PgSplit.py +0 -0
  19. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common/PgUtil.py +0 -0
  20. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common/pg_dbi.py +0 -0
  21. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common/pg_lock.py +0 -0
  22. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common/pg_opt.py +0 -0
  23. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common/pg_password.py +0 -0
  24. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common/pg_sig.py +0 -0
  25. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common/pg_split.py +0 -0
  26. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common/pg_util.py +0 -0
  27. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common/pgpassword.py +0 -0
  28. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common/pgpassword.usg +0 -0
  29. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common.egg-info/SOURCES.txt +0 -0
  30. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common.egg-info/dependency_links.txt +0 -0
  31. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common.egg-info/entry_points.txt +0 -0
  32. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common.egg-info/requires.txt +0 -0
  33. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/src/rda_python_common.egg-info/top_level.txt +0 -0
  34. {rda_python_common-3.0.17 → rda_python_common-3.0.19}/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.17
3
+ Version: 3.0.19
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.17`). If the import
192
+ You should see the installed version (currently `3.0.19`). 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.17`). If the import
168
+ You should see the installed version (currently `3.0.19`). 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.17"
7
+ version = "3.0.19"
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.17"
25
+ __version__ = "3.0.19"
26
26
 
27
27
  __all__ = [
28
28
  "PgLOG",
@@ -207,6 +207,14 @@ class PgCMD(PgLock):
207
207
  self.pglog(cmsg + "is Running, No restart", self.LOGWRN)
208
208
  sys.exit(0)
209
209
  if cidx > 0:
210
+ # a report parked in einfo by the previous run is only mailed by the dscheck
211
+ # daemon, which skips any record that is already running. restarting the record
212
+ # here would strand that report until the next run overwrites it, so leave the
213
+ # record alone and let the daemon mail it first - it clears einfo when it does
214
+ if pgrec['einfo']:
215
+ self.pglog(cmsg + "has a report to email, No restart", self.LOGWRN)
216
+ self.lock_dscheck(cidx, 0, logact)
217
+ sys.exit(0)
210
218
  if not hosts and pgrec['hostname']:
211
219
  hosts = pgrec['hostname']
212
220
  self.set_one_boption('hostname', hosts, 0)
@@ -105,7 +105,8 @@ class PgFile(PgUtil, PgSIG):
105
105
  }
106
106
  self.TARSTR = '|'.join(self.PGTARS)
107
107
  self.DELDIRS = {}
108
- self.TASKIDS = {} # cache unfinished
108
+ self.TASKIDS = {} # cache unfinished
109
+ self.QCANCEL = {} # taskid -> why a task got cancelled, reported by the waiting caller
109
110
  self.LHOST = "localhost"
110
111
  self.OHOST = self.PGLOG['OBJCTSTR']
111
112
  self.BHOST = self.PGLOG['BACKUPNM']
@@ -532,7 +533,11 @@ class PgFile(PgUtil, PgSIG):
532
533
  time.sleep(self.PGSIG['ETIME'])
533
534
  lp += 1
534
535
  if task['stat'] == 'S' or task['stat'] == 'A': break
535
- if task['stat'] == 'F' and not syserr: break
536
+ if task['stat'] == 'F' and not syserr:
537
+ # nothing waits on this task, so report the cancellation here instead
538
+ if task['id'] in self.QCANCEL:
539
+ self.errlog("{}: Cancel Task due to {}".format(task['id'], self.QCANCEL.pop(task['id'])), 'B', 1, logact)
540
+ break
536
541
  errmsg = "Error Execute: " + cmd
537
542
  if qstr: errmsg += " with stdin:\n" + qstr
538
543
  if syserr:
@@ -581,8 +586,16 @@ class PgFile(PgUtil, PgSIG):
581
586
  detail = ms.group(1)
582
587
  if detail not in astats:
583
588
  if logact&self.NOWAIT:
584
- errmsg = "{}: Cancel Task due to {}:\n{}".format(taskid, detail, buf)
585
- self.errlog(errmsg, 'B', 1, logact)
589
+ # record why, and let the caller waiting on this task report it as a
590
+ # single error naming the file; dumping the whole get-task output here
591
+ # doubled every failure into two error entries, the first 17 lines long
592
+ reason = detail
593
+ 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))
597
+ self.QCANCEL[taskid] = reason
598
+ self.pglog("{}: Cancel Task due to {}".format(taskid, reason), self.LOGWRN)
586
599
  ccmd = f"{bcmd} cancel-task {taskid}"
587
600
  self.pgsystem(ccmd, logact, 7)
588
601
  else:
@@ -640,7 +653,10 @@ class PgFile(PgUtil, PgSIG):
640
653
  del self.TASKIDS[ckey]
641
654
  else:
642
655
  status = self.QSTATS[stat] if stat in self.QSTATS else 'UNKNOWN'
643
- self.errlog("{}: Status '{}' for Task {}".format(ckey, status, taskid), 'B', 1, logact)
656
+ errmsg = "{}: Status '{}' for Task {}".format(ckey, status, taskid)
657
+ if taskid in self.QCANCEL:
658
+ errmsg += " - " + self.QCANCEL.pop(taskid)
659
+ self.errlog(errmsg, 'B', 1, logact)
644
660
  ret = self.FAILURE
645
661
  break
646
662
  return ret
@@ -320,13 +320,18 @@ class PgLOG:
320
320
  msg = emlmsg
321
321
  for ekey in entries:
322
322
  entry = entries[ekey][0]
323
- ms = re.search(r'(^|\n)({}: *(.*)\n)'.format(entry), emlmsg, re.I)
323
+ ms = re.search(r'(^|\n)({}: *(.*)\n)'.format(entry), msg, re.I)
324
324
  if ms:
325
- vals = ms.groups()
326
- msg = msg.replace(vals[1], '')
327
- if vals[2]: entries[ekey][2] = vals[2]
325
+ # cut out this header line only; a replace() of the matched text would also
326
+ # strip an identical line from anywhere in the message body
327
+ msg = msg[:ms.start(2)] + msg[ms.end(2):]
328
+ if ms.group(3): entries[ekey][2] = ms.group(3)
328
329
  elif entries[ekey][1]:
329
330
  return self.pglog("{}Missing Entry '{}' for sending email".format(logmsg, entry), logact|self.ERRLOG)
331
+ if not msg.strip():
332
+ # send_python_email() mails the buffered EMLMSG when it is given no body, which
333
+ # would send an unrelated message to this recipient and empty the buffer
334
+ return self.pglog("{}Empty message body for sending email".format(logmsg), logact|self.ERRLOG)
330
335
  ret = self.send_python_email(entries['sb'][2], entries['to'][2], msg, entries['fr'][2], entries['cc'][2], logact)
331
336
  if ret == self.SUCCESS or not self.PGLOG['EMLSEND']: return ret
332
337
  # try commandline sendmail
@@ -386,7 +391,10 @@ class PgLOG:
386
391
  self.PGLOG['EMLMSG'] = ''
387
392
  else:
388
393
  return ''
389
- docc = False if cc else True
394
+ # only an omitted cc means 'use CCDADDR and carbon copy the sender'; an explicitly
395
+ # empty cc suppresses the Cc entirely, and used to leak the sender into the global
396
+ # CCDADDR anyway, carbon copying it on every later email of the same process
397
+ docc = True if cc is None else False
390
398
  if not sender:
391
399
  sender = self.PGLOG['CURUID']
392
400
  if sender != self.PGLOG['COMMONUSER']: docc = False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rda_python_common
3
- Version: 3.0.17
3
+ Version: 3.0.19
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.17`). If the import
192
+ You should see the installed version (currently `3.0.19`). 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