rda-python-common 3.0.15__tar.gz → 3.0.17__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.15/src/rda_python_common.egg-info → rda_python_common-3.0.17}/PKG-INFO +2 -2
  2. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/README.md +1 -1
  3. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/pyproject.toml +1 -1
  4. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common/__init__.py +1 -1
  5. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common/pg_file.py +73 -13
  6. {rda_python_common-3.0.15 → rda_python_common-3.0.17/src/rda_python_common.egg-info}/PKG-INFO +2 -2
  7. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/LICENSE +0 -0
  8. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/setup.cfg +0 -0
  9. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common/PgCMD.py +0 -0
  10. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common/PgDBI.py +0 -0
  11. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common/PgFile.py +0 -0
  12. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common/PgLOG.py +0 -0
  13. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common/PgLock.py +0 -0
  14. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common/PgOPT.py +0 -0
  15. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common/PgSIG.py +0 -0
  16. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common/PgSplit.py +0 -0
  17. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common/PgUtil.py +0 -0
  18. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common/pg_cmd.py +0 -0
  19. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common/pg_dbi.py +0 -0
  20. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common/pg_lock.py +0 -0
  21. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common/pg_log.py +0 -0
  22. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common/pg_opt.py +0 -0
  23. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common/pg_password.py +0 -0
  24. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common/pg_sig.py +0 -0
  25. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common/pg_split.py +0 -0
  26. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common/pg_util.py +0 -0
  27. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common/pgpassword.py +0 -0
  28. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common/pgpassword.usg +0 -0
  29. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common.egg-info/SOURCES.txt +0 -0
  30. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common.egg-info/dependency_links.txt +0 -0
  31. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common.egg-info/entry_points.txt +0 -0
  32. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common.egg-info/requires.txt +0 -0
  33. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/src/rda_python_common.egg-info/top_level.txt +0 -0
  34. {rda_python_common-3.0.15 → rda_python_common-3.0.17}/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.15
3
+ Version: 3.0.17
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.15`). If the import
192
+ You should see the installed version (currently `3.0.17`). 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.15`). If the import
168
+ You should see the installed version (currently `3.0.17`). 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.15"
7
+ version = "3.0.17"
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.15"
25
+ __version__ = "3.0.17"
26
26
 
27
27
  __all__ = [
28
28
  "PgLOG",
@@ -766,7 +766,9 @@ class PgFile(PgUtil, PgSIG):
766
766
  """Download a file from the object store to the local filesystem.
767
767
 
768
768
  Changes to the target directory, downloads using isd_s3_cli, verifies size,
769
- sets permissions, and renames if needed. Retries once on failure.
769
+ sets permissions, and renames if needed. Retries once on failure. A key that
770
+ is a prefix of multiple objects, such as a zarr store, is downloaded as a
771
+ directory by object_copy_local_directory().
770
772
 
771
773
  Args:
772
774
  tofile (str): Destination local file path.
@@ -783,6 +785,28 @@ class PgFile(PgUtil, PgSIG):
783
785
  if not finfo:
784
786
  if finfo != None: return ret
785
787
  return self.lmsg(fromfile, "{}-{} to copy to {}".format(self.OHOST, self.PGLOG['MISSFILE'], tofile), logact)
788
+ if not finfo['isfile']: return self.object_copy_local_directory(tofile, fromfile, bucket, logact)
789
+ return self.object_get_local(tofile, fromfile, finfo['data_size'], bucket, logact)
790
+
791
+ # Download a single object of a known size to a local file
792
+ # tofile - target file name
793
+ # fromfile - source object key name
794
+ # fsize - size of the object, to verify the download against
795
+ # bucket - bucket name on Object store
796
+ def object_get_local(self, tofile, fromfile, fsize, bucket, logact = 0):
797
+ """Download one object key to a local file and verify its size.
798
+
799
+ Args:
800
+ tofile (str): Destination local file path.
801
+ fromfile (str): Object key (source path in the bucket).
802
+ fsize (int): Expected size of the object.
803
+ bucket (str): Source bucket.
804
+ logact (int): Logging action flags; default 0.
805
+
806
+ Returns:
807
+ int: self.SUCCESS on success, self.FAILURE on error.
808
+ """
809
+ ret = self.FAILURE
786
810
  ocmd = self.OBJCTCMD
787
811
  cmd = "{} go -k {} -b {}".format(ocmd, fromfile, bucket)
788
812
  fromname = op.basename(fromfile)
@@ -797,7 +821,7 @@ class PgFile(PgUtil, PgSIG):
797
821
  buf = self.pgsystem(cmd, logact, self.CMDBTH)
798
822
  info = self.check_local_file(fromname, 143, logact|self.PFSIZE) # 1+2+4+8+128
799
823
  if info:
800
- if info['data_size'] == finfo['data_size']:
824
+ if info['data_size'] == fsize:
801
825
  self.set_local_mode(fromfile, info['isfile'], 0, info['mode'], info['logname'], logact)
802
826
  if toname == fromname or self.move_local_file(toname, fromname, logact):
803
827
  ret = self.SUCCESS
@@ -811,6 +835,41 @@ class PgFile(PgUtil, PgSIG):
811
835
  self.change_local_directory(odir, logact)
812
836
  return ret
813
837
 
838
+ # Copy an object directory, a key prefix holding multiple objects, to local
839
+ # todir - target local directory name
840
+ # fromdir - source object key prefix
841
+ # bucket - bucket name on Object store
842
+ def object_copy_local_directory(self, todir, fromdir, bucket = None, logact = 0):
843
+ """Download every object under a key prefix into a local directory.
844
+
845
+ The object store has no directories; a zarr store and the like is a set of
846
+ objects sharing a key prefix. Each object is downloaded individually and
847
+ keeps its position relative to the prefix under todir.
848
+
849
+ Args:
850
+ todir (str): Destination local directory path.
851
+ fromdir (str): Object key prefix (source path in the bucket).
852
+ bucket (str | None): Source bucket; defaults to PGLOG['OBJCTBKT'].
853
+ logact (int): Logging action flags; default 0.
854
+
855
+ Returns:
856
+ int: self.SUCCESS on success, self.FAILURE on error.
857
+ """
858
+ if not bucket: bucket = self.PGLOG['OBJCTBKT']
859
+ ms = re.match(r'^(.+)/$', fromdir)
860
+ if ms: fromdir = ms.group(1)
861
+ flist = self.object_glob(fromdir, bucket, 0, logact)
862
+ if flist == self.FAILURE: return self.FAILURE
863
+ prefix = fromdir + '/'
864
+ keys = [key for key in flist if key.startswith(prefix)]
865
+ if not keys:
866
+ return self.lmsg(fromdir, "{}-{} to copy to {}".format(self.OHOST, self.PGLOG['MISSFILE'], todir), logact)
867
+ plen = len(prefix)
868
+ for key in keys:
869
+ tofile = "{}/{}".format(todir, key[plen:])
870
+ if not self.object_get_local(tofile, key, flist[key]['data_size'], bucket, logact): return self.FAILURE
871
+ return self.SUCCESS
872
+
814
873
  # Copy a remote file to object
815
874
  # tofile - target object file name
816
875
  # fromfile - source remote file name
@@ -2298,22 +2357,23 @@ class PgFile(PgUtil, PgSIG):
2298
2357
  if re.match(r'^\[\]', buf): break
2299
2358
  if re.match(r'^\[\{', buf):
2300
2359
  ary = json.loads(buf)
2301
- hash = ary[0]
2360
+ cnt = len(ary)
2361
+ isdir = (cnt > 1 or ary[0]['Key'] != file)
2362
+ # a key prefix has no timestamp of its own; report the newest member's
2363
+ hash = max(ary, key = lambda a: a['LastModified']) if isdir else ary[0]
2302
2364
  uhash = None
2303
2365
  if ucmd:
2304
2366
  ubuf = self.pgsystem(ucmd, self.LOGWRN, self.CMDRET)
2305
2367
  if ubuf and re.match(r'^\{', ubuf): uhash = json.loads(ubuf)
2306
2368
  ret = self.object_file_stat(hash, uhash, opt)
2307
- if ret:
2308
- cnt = len(ary)
2309
- if cnt > 1 or hash['Key'] != file:
2310
- ret['count'] = cnt
2311
- ret['fname'] = op.basename(file)
2312
- ret['isfile'] = 0
2313
- size = 0
2314
- for a in ary:
2315
- size += int(a['Size'])
2316
- ret['data_size'] = size
2369
+ if ret and isdir:
2370
+ ret['count'] = cnt
2371
+ ret['fname'] = op.basename(file)
2372
+ ret['isfile'] = 0
2373
+ size = 0
2374
+ for a in ary:
2375
+ size += int(a['Size'])
2376
+ ret['data_size'] = size
2317
2377
  uhash = None
2318
2378
  break
2319
2379
  if opt&64: return self.FAILURE
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rda_python_common
3
- Version: 3.0.15
3
+ Version: 3.0.17
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.15`). If the import
192
+ You should see the installed version (currently `3.0.17`). 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