rda-python-common 3.0.26__tar.gz → 3.0.28__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.26/src/rda_python_common.egg-info → rda_python_common-3.0.28}/PKG-INFO +2 -2
  2. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/README.md +1 -1
  3. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/pyproject.toml +1 -1
  4. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common/__init__.py +1 -1
  5. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common/pg_dbi.py +1 -0
  6. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common/pg_file.py +86 -0
  7. {rda_python_common-3.0.26 → rda_python_common-3.0.28/src/rda_python_common.egg-info}/PKG-INFO +2 -2
  8. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/LICENSE +0 -0
  9. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/setup.cfg +0 -0
  10. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common/PgCMD.py +0 -0
  11. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common/PgDBI.py +0 -0
  12. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common/PgFile.py +0 -0
  13. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common/PgLOG.py +0 -0
  14. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common/PgLock.py +0 -0
  15. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common/PgOPT.py +0 -0
  16. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common/PgSIG.py +0 -0
  17. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common/PgSplit.py +0 -0
  18. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common/PgUtil.py +0 -0
  19. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common/pg_cmd.py +0 -0
  20. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common/pg_lock.py +0 -0
  21. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common/pg_log.py +0 -0
  22. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common/pg_opt.py +0 -0
  23. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common/pg_password.py +0 -0
  24. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common/pg_sig.py +0 -0
  25. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common/pg_split.py +0 -0
  26. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common/pg_util.py +0 -0
  27. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common/pgpassword.py +0 -0
  28. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common/pgpassword.usg +0 -0
  29. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common.egg-info/SOURCES.txt +0 -0
  30. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common.egg-info/dependency_links.txt +0 -0
  31. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common.egg-info/entry_points.txt +0 -0
  32. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common.egg-info/requires.txt +0 -0
  33. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/src/rda_python_common.egg-info/top_level.txt +0 -0
  34. {rda_python_common-3.0.26 → rda_python_common-3.0.28}/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.26
3
+ Version: 3.0.28
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.26`). If the import
192
+ You should see the installed version (currently `3.0.28`). 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.26`). If the import
168
+ You should see the installed version (currently `3.0.28`). 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.26"
7
+ version = "3.0.28"
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.26"
25
+ __version__ = "3.0.28"
26
26
 
27
27
  __all__ = [
28
28
  "PgLOG",
@@ -2176,6 +2176,7 @@ class PgDBI(PgLOG):
2176
2176
 
2177
2177
  pgrec = self.pgget("dsowner, dssgrp", "specialist, lstname, fstname",
2178
2178
  "specialist = logname AND dsid = '{}' AND priority = 1".format(dsid), logact)
2179
+ if pgrec == self.FAILURE: return None # query error; do not cache the fallback
2179
2180
  if pgrec:
2180
2181
  if pgrec['specialist'] == "datahelp" or pgrec['specialist'] == "dss":
2181
2182
  pgrec['lstname'] = "Help"
@@ -410,6 +410,92 @@ class PgFile(PgUtil, PgSIG):
410
410
  self.errlog("Error Execute: {}\n{}".format(cmd, buf), 'O', loop, logact)
411
411
  return self.FAILURE
412
412
 
413
+ # Upload a local directory to object under a key prefix
414
+ # todir - target object key prefix, without the trailing '/'
415
+ # fromdir - source local directory name
416
+ def local_copy_object_directory(self, todir, fromdir, bucket = None, meta = None, logact = 0):
417
+ """Upload every file under a local directory to an object key prefix.
418
+
419
+ The whole directory goes up in one recursive call, the upload twin of
420
+ object_copy_local_directory(): one process per file costs several seconds
421
+ of start-up each, which dominates for a directory of many files. Each file
422
+ lands at '{todir}/{path relative to fromdir}'.
423
+
424
+ Nothing is uploaded when every file is already under the prefix at its local
425
+ size (unless OVRIDE is set); otherwise the whole directory is uploaded again,
426
+ since the recursive call cannot be limited to the missing files.
427
+
428
+ Args:
429
+ todir (str): Object key prefix (destination path in the bucket); not empty.
430
+ fromdir (str): Source local directory path.
431
+ bucket (str | None): Target bucket; defaults to PGLOG['OBJCTBKT'].
432
+ meta (dict | None): Extra metadata key/value pairs to attach to each object.
433
+ logact (int): Logging action flags; default 0.
434
+
435
+ Returns:
436
+ int: self.SUCCESS on success, self.FAILURE on error.
437
+ """
438
+ if not bucket: bucket = self.PGLOG['OBJCTBKT']
439
+ if meta is None: meta = {}
440
+ if 'user' not in meta: meta['user'] = self.PGLOG['CURUID']
441
+ if 'group' not in meta: meta['group'] = self.PGLOG['GDEXGRP']
442
+ uinfo = json.dumps(meta)
443
+ ms = re.match(r'^(.+)/$', fromdir)
444
+ if ms: fromdir = ms.group(1)
445
+ ms = re.match(r'^(.+)/$', todir)
446
+ if ms: todir = ms.group(1)
447
+ # the same walk isd_s3_cli makes to pick the files: no symlinked directories
448
+ lfiles = {}
449
+ for path, dirs, files in os.walk(fromdir):
450
+ for fname in files:
451
+ lfile = op.join(path, fname)
452
+ lfiles[op.relpath(lfile, fromdir)] = op.getsize(lfile)
453
+ if not lfiles:
454
+ return self.lmsg(fromdir, "{} to copy to {}-{}".format(self.PGLOG['MISSFILE'], self.OHOST, todir), logact)
455
+ prefix = todir + '/'
456
+ if not logact&self.OVRIDE:
457
+ flist = self.object_glob(todir, bucket, 0, logact)
458
+ if flist == self.FAILURE: return self.FAILURE
459
+ miss = self.object_upload_missing(prefix, lfiles, flist)
460
+ if not miss:
461
+ self.pglog("{}-{}-{}: all {} file(s) exist already, skip uploading".format(
462
+ self.OHOST, bucket, todir, len(lfiles)), logact&~self.EXITLG)
463
+ return self.SUCCESS
464
+ # -db too: an upload goes to the default bucket whatever -b says
465
+ cmd = "{} -db {} um -ld {} -b {} -kp {} -r -md '{}'".format(
466
+ self.OBJCTCMD, bucket, fromdir, bucket, todir, uinfo)
467
+ buf = self.pgsystem(cmd, logact, self.CMDBTH)
468
+ flist = self.object_glob(todir, bucket, 0, logact)
469
+ if flist == self.FAILURE: return self.FAILURE
470
+ miss = self.object_upload_missing(prefix, lfiles, flist)
471
+ if miss:
472
+ return self.errlog("{}-{}: Error upload {} of {} file(s) to {}-{}\n{}".format(
473
+ fromdir, miss[0], len(miss), len(lfiles), self.OHOST, todir, buf), 'O', 1, logact)
474
+ self.pglog("{}: {} file(s) uploaded to {}-{}-{}".format(
475
+ fromdir, len(lfiles), self.OHOST, bucket, todir), logact&~self.EXITLG)
476
+ return self.SUCCESS
477
+
478
+ # List the local files not uploaded under an object key prefix yet
479
+ # prefix - target object key prefix, including the trailing '/'
480
+ # lfiles - local file sizes, keyed by path relative to the source directory
481
+ # flist - object_glob() info, keyed by object key
482
+ def object_upload_missing(self, prefix, lfiles, flist):
483
+ """Return the local files that have no object of the same size under a key prefix.
484
+
485
+ Args:
486
+ prefix (str): Object key prefix including the trailing '/'.
487
+ lfiles (dict): Mapping of relative path → local file size.
488
+ flist (dict): Mapping of key → object info from object_glob().
489
+
490
+ Returns:
491
+ list: The relative paths still to upload.
492
+ """
493
+ miss = []
494
+ for name in lfiles:
495
+ oinfo = flist.get(prefix + name)
496
+ if not (oinfo and oinfo['data_size'] == lfiles[name]): miss.append(name)
497
+ return miss
498
+
413
499
  # Copy multiple files from a Globus endpoint to another
414
500
  # tofiles - target file name list, echo name leading with /dsnnn.n/ on Quasar and
415
501
  # leading with /data/ or /decsdata/ on local glade disk
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rda_python_common
3
- Version: 3.0.26
3
+ Version: 3.0.28
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.26`). If the import
192
+ You should see the installed version (currently `3.0.28`). 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