rda-python-common 3.0.27__py3-none-any.whl → 3.0.29__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.
- rda_python_common/__init__.py +1 -1
- rda_python_common/pg_file.py +86 -0
- rda_python_common/pg_sig.py +41 -6
- {rda_python_common-3.0.27.dist-info → rda_python_common-3.0.29.dist-info}/METADATA +2 -2
- {rda_python_common-3.0.27.dist-info → rda_python_common-3.0.29.dist-info}/RECORD +9 -9
- {rda_python_common-3.0.27.dist-info → rda_python_common-3.0.29.dist-info}/WHEEL +0 -0
- {rda_python_common-3.0.27.dist-info → rda_python_common-3.0.29.dist-info}/entry_points.txt +0 -0
- {rda_python_common-3.0.27.dist-info → rda_python_common-3.0.29.dist-info}/licenses/LICENSE +0 -0
- {rda_python_common-3.0.27.dist-info → rda_python_common-3.0.29.dist-info}/top_level.txt +0 -0
rda_python_common/__init__.py
CHANGED
rda_python_common/pg_file.py
CHANGED
|
@@ -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
|
rda_python_common/pg_sig.py
CHANGED
|
@@ -52,7 +52,11 @@ class PgSIG(PgDBI):
|
|
|
52
52
|
'MPROC': 1, # default number of multiple processes
|
|
53
53
|
'BPROC': 1, # default number of multiple background processes
|
|
54
54
|
'ETIME': 20, # default error waiting time (in seconds)
|
|
55
|
-
'WTIME': 120, # default waiting time (in seconds)
|
|
55
|
+
'WTIME': 120, # default waiting time (in seconds), the cap of an adaptive wait
|
|
56
|
+
'WTMIN': 5, # shortest adaptive wait (in seconds) for child/background processes
|
|
57
|
+
'WTINC': 5, # seconds added to the adaptive wait after each loop still waiting
|
|
58
|
+
'CWTIME': 0, # current adaptive wait (in seconds), 0 before the first wait
|
|
59
|
+
'WLOOP': -1, # loop count of the last adaptive wait sleep
|
|
56
60
|
'DTIME': 600, # the daemon record refresh time (in seconds)
|
|
57
61
|
'RTIME': 2400, # the web rda config unlocking and unconfigured system down waiting time (in seconds)
|
|
58
62
|
'CTIME': 4800, # the lock cleaning & configued system down waiting time (in seconds)
|
|
@@ -1281,21 +1285,52 @@ class PgSIG(PgDBI):
|
|
|
1281
1285
|
if wtime: time.sleep(wtime)
|
|
1282
1286
|
return wtime
|
|
1283
1287
|
|
|
1284
|
-
# show wait message every
|
|
1288
|
+
# show wait message every 30 loops and then sleep for an adaptive wait time
|
|
1285
1289
|
def show_wait_message(self, loop, msg, logact=None, dowait=0):
|
|
1286
1290
|
"""Log a wait message every 30 loops and optionally sleep.
|
|
1287
1291
|
|
|
1288
1292
|
Args:
|
|
1289
|
-
loop (int): Current loop iteration count.
|
|
1293
|
+
loop (int): Current loop iteration count, 0 for the first sleep of a wait.
|
|
1290
1294
|
msg (str): Message to log.
|
|
1291
1295
|
logact (int, optional): Log action flags. Defaults to ``LOGWRN``.
|
|
1292
|
-
dowait (int, optional): If non-zero, sleep for
|
|
1293
|
-
|
|
1296
|
+
dowait (int, optional): If non-zero, sleep for the adaptive wait time
|
|
1297
|
+
from ``adaptive_wait_time()``, at most ``PGSIG['WTIME']`` seconds.
|
|
1298
|
+
Defaults to 0.
|
|
1294
1299
|
"""
|
|
1295
1300
|
if logact is None: logact = self.LOGWRN
|
|
1296
1301
|
if loop > 0 and (loop%30) == 0:
|
|
1297
1302
|
self.pglog("{} at {}".format(msg, self.current_datetime()), logact)
|
|
1298
|
-
if dowait: time.sleep(self.
|
|
1303
|
+
if dowait: time.sleep(self.adaptive_wait_time(loop))
|
|
1304
|
+
|
|
1305
|
+
# get the adaptive time to sleep for one loop of a wait for processes
|
|
1306
|
+
def adaptive_wait_time(self, loop):
|
|
1307
|
+
"""Return how long to sleep for one loop of a wait for child/background processes.
|
|
1308
|
+
|
|
1309
|
+
The wait starts at ``PGSIG['WTMIN']`` seconds and grows by ``PGSIG['WTINC']``
|
|
1310
|
+
after each loop that still finds no process done, capped at the polling time
|
|
1311
|
+
``PGSIG['WTIME']``, so long-running processes are not checked too often while
|
|
1312
|
+
short ones are not waited on for the full polling time. The current wait
|
|
1313
|
+
carries over to the next wait; it goes back to ``WTMIN`` when the previous
|
|
1314
|
+
wait was over after its first sleep, since the processes are short. Set
|
|
1315
|
+
``PGSIG['WTMIN']`` to ``WTIME`` or larger for the old fixed wait.
|
|
1316
|
+
|
|
1317
|
+
Args:
|
|
1318
|
+
loop (int): Loop count of the current wait, 0 for its first sleep.
|
|
1319
|
+
|
|
1320
|
+
Returns:
|
|
1321
|
+
int: Seconds to sleep.
|
|
1322
|
+
"""
|
|
1323
|
+
wmax = self.PGSIG['WTIME']
|
|
1324
|
+
wmin = min(self.PGSIG['WTMIN'], wmax)
|
|
1325
|
+
wtime = self.PGSIG['CWTIME']
|
|
1326
|
+
if loop == 0:
|
|
1327
|
+
if not wtime or self.PGSIG['WLOOP'] == 0: wtime = wmin
|
|
1328
|
+
else:
|
|
1329
|
+
wtime += self.PGSIG['WTINC']
|
|
1330
|
+
wtime = max(wmin, min(wtime, wmax))
|
|
1331
|
+
self.PGSIG['CWTIME'] = wtime
|
|
1332
|
+
self.PGSIG['WLOOP'] = loop
|
|
1333
|
+
return wtime
|
|
1299
1334
|
|
|
1300
1335
|
# register a time out function to raise a time out error
|
|
1301
1336
|
@contextmanager
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rda_python_common
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.29
|
|
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.
|
|
192
|
+
You should see the installed version (currently `3.0.29`). 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
|
|
|
@@ -7,22 +7,22 @@ rda_python_common/PgOPT.py,sha256=0nQPF19N-1VWwrjFHEeruVSwVDwvFKd4uowPc8dK7e4,56
|
|
|
7
7
|
rda_python_common/PgSIG.py,sha256=0MJPH0fTtSshIsvUXkzQjReHPyCWdzHYOY5LbFSh7-A,34318
|
|
8
8
|
rda_python_common/PgSplit.py,sha256=En7xcdkE4YNRcMX88qXnrUgUOnQU3Kqh8uxRIdlJhEA,9156
|
|
9
9
|
rda_python_common/PgUtil.py,sha256=7hXAznAI8tYh-nJRKBIXejNe2ROQ2Wxn28OAndeg0Iw,48433
|
|
10
|
-
rda_python_common/__init__.py,sha256=
|
|
10
|
+
rda_python_common/__init__.py,sha256=sBVlfOY9ijDUndeN_oeqUihFhQ4l5ZUfT91PeQTPoVE,995
|
|
11
11
|
rda_python_common/pg_cmd.py,sha256=MYldUuM8mSUIT34yKQyUd0i4NL4zdSB8C6Jcc28NRxA,33809
|
|
12
12
|
rda_python_common/pg_dbi.py,sha256=ASIW2vsIjMab2TMfhqJiXS7LQhKKGpKjzNMqo7RCePw,122415
|
|
13
|
-
rda_python_common/pg_file.py,sha256=
|
|
13
|
+
rda_python_common/pg_file.py,sha256=7NipcgMy4t67zbH2MHtRL3vxyY1FHdIgMTQ-kE0H2fA,181122
|
|
14
14
|
rda_python_common/pg_lock.py,sha256=31EaVDjCkcx3-n8-KnzG18R8Pz7Z6KyFsEqcml6Iq5c,32702
|
|
15
15
|
rda_python_common/pg_log.py,sha256=erQN4YYPwiORa3OE0Jt8s05rlWsjtyIs0fJ_KLbNAQ8,82959
|
|
16
16
|
rda_python_common/pg_opt.py,sha256=LxVAf47wi2STkVGSXMvV3IZphEJcoO8bvYXy6kQ5ox4,82403
|
|
17
17
|
rda_python_common/pg_password.py,sha256=X-eIDwdqBhtrhrbDTNWle-0JtWsyIVZdDOZaBu7cFHM,2343
|
|
18
|
-
rda_python_common/pg_sig.py,sha256=
|
|
18
|
+
rda_python_common/pg_sig.py,sha256=wLqg-SxvF-tu-9zlpRsiszOb141L_YbxD-SGVnrCTPg,57828
|
|
19
19
|
rda_python_common/pg_split.py,sha256=aAWKUZPmZ-LQ_fJ3DSKzPKxJw0fMDJ2fgP8ZT629M30,16375
|
|
20
20
|
rda_python_common/pg_util.py,sha256=JL9NoW-VoQvsjXsUr09oBUWYJJNltU4GyQIfWlpkf90,86084
|
|
21
21
|
rda_python_common/pgpassword.py,sha256=aTQaO59QokWrv2EKom4TvKhNW2Mr6aFMMBVG4DCbcKI,5023
|
|
22
22
|
rda_python_common/pgpassword.usg,sha256=Wrle6A8sdlMGx3ZEAmE6TNDPOlG84Whe0T3rCNFzPfY,2013
|
|
23
|
-
rda_python_common-3.0.
|
|
24
|
-
rda_python_common-3.0.
|
|
25
|
-
rda_python_common-3.0.
|
|
26
|
-
rda_python_common-3.0.
|
|
27
|
-
rda_python_common-3.0.
|
|
28
|
-
rda_python_common-3.0.
|
|
23
|
+
rda_python_common-3.0.29.dist-info/licenses/LICENSE,sha256=1dck4EAQwv8QweDWCXDx-4Or0S8YwiCstaso_H57Pno,1097
|
|
24
|
+
rda_python_common-3.0.29.dist-info/METADATA,sha256=51mKJd7UiXR8giX2wKBeFpDIpPd5875s4hEAJ5RifMg,14530
|
|
25
|
+
rda_python_common-3.0.29.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
26
|
+
rda_python_common-3.0.29.dist-info/entry_points.txt,sha256=pZgVNWspcK-F1TbPav7C3C9NdeHDZMm_25fW9weix00,65
|
|
27
|
+
rda_python_common-3.0.29.dist-info/top_level.txt,sha256=KVQmx7D3DD-jsiheqL8HdTrRE14hpRnZY5_ioMArA5k,18
|
|
28
|
+
rda_python_common-3.0.29.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|