cloud-files 5.7.0__py3-none-any.whl → 6.0.0__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.
- {cloud_files-5.7.0.dist-info → cloud_files-6.0.0.dist-info}/METADATA +1 -1
- {cloud_files-5.7.0.dist-info → cloud_files-6.0.0.dist-info}/RECORD +12 -12
- cloud_files-6.0.0.dist-info/pbr.json +1 -0
- cloudfiles/cloudfiles.py +56 -1
- cloudfiles/interfaces.py +123 -37
- cloudfiles/paths.py +1 -1
- cloudfiles_cli/cloudfiles_cli.py +36 -4
- cloud_files-5.7.0.dist-info/pbr.json +0 -1
- {cloud_files-5.7.0.dist-info → cloud_files-6.0.0.dist-info}/AUTHORS +0 -0
- {cloud_files-5.7.0.dist-info → cloud_files-6.0.0.dist-info}/LICENSE +0 -0
- {cloud_files-5.7.0.dist-info → cloud_files-6.0.0.dist-info}/WHEEL +0 -0
- {cloud_files-5.7.0.dist-info → cloud_files-6.0.0.dist-info}/entry_points.txt +0 -0
- {cloud_files-5.7.0.dist-info → cloud_files-6.0.0.dist-info}/top_level.txt +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
cloudfiles/__init__.py,sha256=pLB4CcV2l3Jgv_ni1520Np1pfzFj8Cpr87vNxFT3rNI,493
|
|
2
|
-
cloudfiles/cloudfiles.py,sha256=
|
|
2
|
+
cloudfiles/cloudfiles.py,sha256=SlSkGKCBdnne7vE7Y5_tvsMcFNx_coUWMtDajEnbHfY,58093
|
|
3
3
|
cloudfiles/compression.py,sha256=WXJHnoNLJ_NWyoY9ygZmFA2qMou35_9xS5dzF7-2H-M,6262
|
|
4
4
|
cloudfiles/connectionpools.py,sha256=aL8RiSjRepECfgAFmJcz80aJFKbou7hsbuEgugDKwB8,4814
|
|
5
5
|
cloudfiles/exceptions.py,sha256=N0oGQNG-St6RvnT8e5p_yC_E61q2kgAe2scwAL0F49c,843
|
|
6
6
|
cloudfiles/gcs.py,sha256=unqu5KxGKaPq6N4QeHSpCDdtnK1BzPOAerTZ8FLt2_4,3820
|
|
7
|
-
cloudfiles/interfaces.py,sha256=
|
|
7
|
+
cloudfiles/interfaces.py,sha256=Kg5t2-tWD0EoJ0qK-wid7zdxLgs7q0mDduPxAzyUUL0,47499
|
|
8
8
|
cloudfiles/lib.py,sha256=HHjCvjmOjA0nZWSvHGoSeYpxqd6FAG8xk8LM212LAUA,5382
|
|
9
9
|
cloudfiles/monitoring.py,sha256=N5Xq0PYZK1OxoYtwBFsnnfaq7dElTgY8Rn2Ez_I3aoo,20897
|
|
10
|
-
cloudfiles/paths.py,sha256=
|
|
10
|
+
cloudfiles/paths.py,sha256=FLdShqkOg1XlkHurU9eiKzLadx2JFYG1EmleCpOFsYQ,12229
|
|
11
11
|
cloudfiles/resumable_tools.py,sha256=NyuSoGh1SaP5akrHCpd9kgy2-JruEWrHW9lvJxV7jpE,6711
|
|
12
12
|
cloudfiles/scheduler.py,sha256=ioqBT5bMPCVHEHlnL-SW_wHulxGgjeThiKHlnaDOydo,3831
|
|
13
13
|
cloudfiles/secrets.py,sha256=IuYKHmmvFmQTyG2Zcmbx7e8U2LIv-woG5d8qyOlyCD8,5431
|
|
@@ -16,12 +16,12 @@ cloudfiles/threaded_queue.py,sha256=Nl4vfXhQ6nDLF8PZpSSBpww0M2zWtcd4DLs3W3BArBw,
|
|
|
16
16
|
cloudfiles/typing.py,sha256=f3ZYkNfN9poxhGu5j-P0KCxjCCqSn9HAg5KiIPkjnCg,416
|
|
17
17
|
cloudfiles_cli/LICENSE,sha256=Jna4xYE8CCQmaxjr5Fs-wmUBnIQJ1DGcNn9MMjbkprk,1538
|
|
18
18
|
cloudfiles_cli/__init__.py,sha256=Wftt3R3F21QsHtWqx49ODuqT9zcSr0em7wk48kcH0WM,29
|
|
19
|
-
cloudfiles_cli/cloudfiles_cli.py,sha256=
|
|
20
|
-
cloud_files-
|
|
21
|
-
cloud_files-
|
|
22
|
-
cloud_files-
|
|
23
|
-
cloud_files-
|
|
24
|
-
cloud_files-
|
|
25
|
-
cloud_files-
|
|
26
|
-
cloud_files-
|
|
27
|
-
cloud_files-
|
|
19
|
+
cloudfiles_cli/cloudfiles_cli.py,sha256=jHbQasZb5DB_g8nGxS3Y0ekAdIPcSVrHN5mvEedUl0k,38908
|
|
20
|
+
cloud_files-6.0.0.dist-info/AUTHORS,sha256=BFVmobgAhaVFI5fqbuqAY5XmBQxe09ZZAsAOTy87hKQ,318
|
|
21
|
+
cloud_files-6.0.0.dist-info/LICENSE,sha256=Jna4xYE8CCQmaxjr5Fs-wmUBnIQJ1DGcNn9MMjbkprk,1538
|
|
22
|
+
cloud_files-6.0.0.dist-info/METADATA,sha256=SJw22OqzxSN3BvyacUjQgJ1trdAWs4mJv9hC0LYKQZk,30530
|
|
23
|
+
cloud_files-6.0.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
24
|
+
cloud_files-6.0.0.dist-info/entry_points.txt,sha256=xlirb1FVhn1mbcv4IoyMEGumDqKOA4VMVd3drsRQxIg,51
|
|
25
|
+
cloud_files-6.0.0.dist-info/pbr.json,sha256=P1Yg68JWbSeMCxsbPR-QhAUj2p8rzNNuqgMHtcFAveo,46
|
|
26
|
+
cloud_files-6.0.0.dist-info/top_level.txt,sha256=xPyrST3okJbsmdCF5IC2gYAVxg_aD5AYVTnNo8UuoZU,26
|
|
27
|
+
cloud_files-6.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"git_version": "38a2b59", "is_release": true}
|
cloudfiles/cloudfiles.py
CHANGED
|
@@ -18,6 +18,7 @@ import platform
|
|
|
18
18
|
import posixpath
|
|
19
19
|
import re
|
|
20
20
|
import shutil
|
|
21
|
+
import threading
|
|
21
22
|
import types
|
|
22
23
|
import time
|
|
23
24
|
|
|
@@ -1007,6 +1008,40 @@ class CloudFiles:
|
|
|
1007
1008
|
return results
|
|
1008
1009
|
return first(results.values())
|
|
1009
1010
|
|
|
1011
|
+
def subtree_size(self, prefix:GetPathType = "") -> dict[str,int]:
|
|
1012
|
+
"""High performance size calculation for directory trees."""
|
|
1013
|
+
prefix, return_multiple = toiter(prefix, is_iter=True)
|
|
1014
|
+
total_bytes = 0
|
|
1015
|
+
total_files = 0
|
|
1016
|
+
|
|
1017
|
+
total = totalfn(prefix, None)
|
|
1018
|
+
|
|
1019
|
+
lock = threading.Lock()
|
|
1020
|
+
|
|
1021
|
+
def size_thunk(prefix):
|
|
1022
|
+
nonlocal total_bytes
|
|
1023
|
+
nonlocal total_files
|
|
1024
|
+
nonlocal lock
|
|
1025
|
+
|
|
1026
|
+
with self._get_connection() as conn:
|
|
1027
|
+
subtree_files, subtree_bytes = conn.subtree_size(prefix)
|
|
1028
|
+
with lock:
|
|
1029
|
+
total_files += subtree_files
|
|
1030
|
+
total_bytes += subtree_bytes
|
|
1031
|
+
|
|
1032
|
+
schedule_jobs(
|
|
1033
|
+
fns=( partial(size_thunk, path) for path in prefix ),
|
|
1034
|
+
concurrency=self.num_threads,
|
|
1035
|
+
progress=self.progress,
|
|
1036
|
+
green=self.green,
|
|
1037
|
+
total=total,
|
|
1038
|
+
)
|
|
1039
|
+
|
|
1040
|
+
return {
|
|
1041
|
+
"N": total_files,
|
|
1042
|
+
"num_bytes": total_bytes,
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1010
1045
|
@parallelize(desc="Delete")
|
|
1011
1046
|
def delete(
|
|
1012
1047
|
self, paths:GetPathType, total:Optional[int] = None,
|
|
@@ -1666,6 +1701,12 @@ class CloudFiles:
|
|
|
1666
1701
|
return os.path.join(*paths)
|
|
1667
1702
|
return posixpath.join(*paths)
|
|
1668
1703
|
|
|
1704
|
+
@property
|
|
1705
|
+
def sep(self) -> str:
|
|
1706
|
+
if self._path.protocol == "file":
|
|
1707
|
+
return os.sep
|
|
1708
|
+
return posixpath.sep
|
|
1709
|
+
|
|
1669
1710
|
def dirname(self, path:str) -> str:
|
|
1670
1711
|
if self._path.protocol == "file":
|
|
1671
1712
|
return os.path.dirname(path)
|
|
@@ -1706,11 +1747,17 @@ class CloudFiles:
|
|
|
1706
1747
|
|
|
1707
1748
|
class CloudFile:
|
|
1708
1749
|
def __init__(
|
|
1709
|
-
self,
|
|
1750
|
+
self,
|
|
1751
|
+
path:str,
|
|
1752
|
+
cache_meta:bool = False,
|
|
1710
1753
|
secrets:SecretsType = None,
|
|
1711
1754
|
composite_upload_threshold:int = int(1e8),
|
|
1712
1755
|
locking:bool = True,
|
|
1713
1756
|
lock_dir:Optional[str] = None,
|
|
1757
|
+
endpoint:Optional[str] = None,
|
|
1758
|
+
no_sign_request:bool = False,
|
|
1759
|
+
request_payer:Optional[str] = None,
|
|
1760
|
+
use_https:bool = False,
|
|
1714
1761
|
):
|
|
1715
1762
|
path = paths.normalize(path)
|
|
1716
1763
|
self.cf = CloudFiles(
|
|
@@ -1719,6 +1766,10 @@ class CloudFile:
|
|
|
1719
1766
|
composite_upload_threshold=composite_upload_threshold,
|
|
1720
1767
|
locking=locking,
|
|
1721
1768
|
lock_dir=lock_dir,
|
|
1769
|
+
use_https=use_https,
|
|
1770
|
+
endpoint=endpoint,
|
|
1771
|
+
request_payer=request_payer,
|
|
1772
|
+
no_sign_request=no_sign_request,
|
|
1722
1773
|
)
|
|
1723
1774
|
self.filename = paths.basename(path)
|
|
1724
1775
|
|
|
@@ -1726,6 +1777,10 @@ class CloudFile:
|
|
|
1726
1777
|
self._size:Optional[int] = None
|
|
1727
1778
|
self._head = None
|
|
1728
1779
|
|
|
1780
|
+
@property
|
|
1781
|
+
def sep(self) -> str:
|
|
1782
|
+
return self.cf.sep
|
|
1783
|
+
|
|
1729
1784
|
@property
|
|
1730
1785
|
def protocol(self):
|
|
1731
1786
|
return self.cf.protocol
|
cloudfiles/interfaces.py
CHANGED
|
@@ -48,6 +48,7 @@ MEM_POOL = None
|
|
|
48
48
|
|
|
49
49
|
S3_ACLS = {
|
|
50
50
|
"tigerdata": "private",
|
|
51
|
+
"nokura": "public-read",
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
S3ConnectionPoolParams = namedtuple('S3ConnectionPoolParams', 'service bucket_name request_payer')
|
|
@@ -303,6 +304,22 @@ class FileInterface(StorageInterface):
|
|
|
303
304
|
|
|
304
305
|
return self.io_with_lock(do_size, path, exclusive=False)
|
|
305
306
|
|
|
307
|
+
def subtree_size(self, prefix:str = "") -> tuple[int,int]:
|
|
308
|
+
total_bytes = 0
|
|
309
|
+
total_files = 0
|
|
310
|
+
|
|
311
|
+
subdir = self.get_path_to_file("")
|
|
312
|
+
if prefix:
|
|
313
|
+
subdir = os.path.join(subdir, os.path.dirname(prefix))
|
|
314
|
+
|
|
315
|
+
for root, dirs, files in os.walk(subdir):
|
|
316
|
+
for f in files:
|
|
317
|
+
path = os.path.join(root, f)
|
|
318
|
+
total_files += 1
|
|
319
|
+
total_bytes += os.path.getsize(path)
|
|
320
|
+
|
|
321
|
+
return (total_files, total_bytes)
|
|
322
|
+
|
|
306
323
|
def exists(self, file_path):
|
|
307
324
|
path = self.get_path_to_file(file_path)
|
|
308
325
|
def do_exists():
|
|
@@ -580,8 +597,7 @@ class MemoryInterface(StorageInterface):
|
|
|
580
597
|
|
|
581
598
|
Returns: iterator
|
|
582
599
|
"""
|
|
583
|
-
layer_path = self.get_path_to_file("")
|
|
584
|
-
path = os.path.join(layer_path, prefix) + '*'
|
|
600
|
+
layer_path = self.get_path_to_file("")
|
|
585
601
|
|
|
586
602
|
remove = layer_path
|
|
587
603
|
if len(remove) and remove[-1] != '/':
|
|
@@ -615,6 +631,23 @@ class MemoryInterface(StorageInterface):
|
|
|
615
631
|
filenames.sort()
|
|
616
632
|
return iter(filenames)
|
|
617
633
|
|
|
634
|
+
def subtree_size(self, prefix:str = "") -> tuple[int,int]:
|
|
635
|
+
layer_path = self.get_path_to_file("")
|
|
636
|
+
|
|
637
|
+
remove = layer_path
|
|
638
|
+
if len(remove) and remove[-1] != '/':
|
|
639
|
+
remove += '/'
|
|
640
|
+
|
|
641
|
+
total_bytes = 0
|
|
642
|
+
total_files = 0
|
|
643
|
+
for filename, binary in self._data.items():
|
|
644
|
+
f_prefix = f.removeprefix(remove)[:len(prefix)]
|
|
645
|
+
if f_prefix == prefix:
|
|
646
|
+
total_bytes += len(binary)
|
|
647
|
+
total_files += 1
|
|
648
|
+
|
|
649
|
+
return (total_files, total_bytes)
|
|
650
|
+
|
|
618
651
|
class GoogleCloudStorageInterface(StorageInterface):
|
|
619
652
|
exists_batch_size = Batch._MAX_BATCH_SIZE
|
|
620
653
|
delete_batch_size = Batch._MAX_BATCH_SIZE
|
|
@@ -798,6 +831,7 @@ class GoogleCloudStorageInterface(StorageInterface):
|
|
|
798
831
|
except google.cloud.exceptions.NotFound:
|
|
799
832
|
pass
|
|
800
833
|
|
|
834
|
+
|
|
801
835
|
@retry
|
|
802
836
|
def list_files(self, prefix, flat=False):
|
|
803
837
|
"""
|
|
@@ -815,35 +849,46 @@ class GoogleCloudStorageInterface(StorageInterface):
|
|
|
815
849
|
blobs = self._bucket.list_blobs(
|
|
816
850
|
prefix=path,
|
|
817
851
|
delimiter=delimiter,
|
|
852
|
+
page_size=2500,
|
|
853
|
+
fields="items(name),nextPageToken",
|
|
818
854
|
)
|
|
819
855
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
# This awkward construction is necessary
|
|
823
|
-
# because the request that populates prefixes
|
|
824
|
-
# isn't made until the iterator is activated.
|
|
825
|
-
if first and blobs.prefixes:
|
|
856
|
+
for page in blobs.pages:
|
|
857
|
+
if page.prefixes:
|
|
826
858
|
yield from (
|
|
827
859
|
item.removeprefix(path)
|
|
828
|
-
for item in
|
|
860
|
+
for item in page.prefixes
|
|
829
861
|
)
|
|
830
|
-
first = False
|
|
831
862
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
863
|
+
for blob in page:
|
|
864
|
+
filename = blob.name.removeprefix(layer_path)
|
|
865
|
+
if not filename:
|
|
866
|
+
continue
|
|
867
|
+
elif not flat and filename[-1] != '/':
|
|
868
|
+
yield filename
|
|
869
|
+
elif flat and '/' not in blob.name.removeprefix(path):
|
|
870
|
+
yield filename
|
|
839
871
|
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
872
|
+
|
|
873
|
+
@retry
|
|
874
|
+
def subtree_size(self, prefix:str = "") -> tuple[int,int]:
|
|
875
|
+
layer_path = self.get_path_to_file("")
|
|
876
|
+
path = posixpath.join(layer_path, prefix)
|
|
877
|
+
|
|
878
|
+
blobs = self._bucket.list_blobs(
|
|
879
|
+
prefix=path,
|
|
880
|
+
page_size=5000,
|
|
881
|
+
fields="items(name,size),nextPageToken",
|
|
882
|
+
)
|
|
883
|
+
|
|
884
|
+
total_bytes = 0
|
|
885
|
+
total_files = 0
|
|
886
|
+
for page in blobs.pages:
|
|
887
|
+
for blob in page:
|
|
888
|
+
total_bytes += blob.size
|
|
889
|
+
total_files += 1
|
|
890
|
+
|
|
891
|
+
return (total_files, total_bytes)
|
|
847
892
|
|
|
848
893
|
def release_connection(self):
|
|
849
894
|
global GC_POOL
|
|
@@ -892,6 +937,8 @@ class HttpInterface(StorageInterface):
|
|
|
892
937
|
key = self.get_path_to_file(file_path)
|
|
893
938
|
headers = self.default_headers()
|
|
894
939
|
with self.session.head(key, headers=headers) as resp:
|
|
940
|
+
if resp.status_code in (404, 403):
|
|
941
|
+
return None
|
|
895
942
|
resp.raise_for_status()
|
|
896
943
|
return resp.headers
|
|
897
944
|
|
|
@@ -899,6 +946,9 @@ class HttpInterface(StorageInterface):
|
|
|
899
946
|
headers = self.head(file_path)
|
|
900
947
|
return int(headers["Content-Length"])
|
|
901
948
|
|
|
949
|
+
def subtree_size(self, prefix:str = "") -> tuple[int,int]:
|
|
950
|
+
raise NotImplementedError()
|
|
951
|
+
|
|
902
952
|
@retry
|
|
903
953
|
def get_file(self, file_path, start=None, end=None, part_size=None):
|
|
904
954
|
key = self.get_path_to_file(file_path)
|
|
@@ -909,24 +959,20 @@ class HttpInterface(StorageInterface):
|
|
|
909
959
|
end = int(end - 1) if end is not None else ''
|
|
910
960
|
headers["Range"] = f"bytes={start}-{end}"
|
|
911
961
|
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
962
|
+
with self.session.get(key, headers=headers, stream=True) as resp:
|
|
963
|
+
if resp.status_code in (404, 403):
|
|
964
|
+
return (None, None, None, None)
|
|
965
|
+
resp.raise_for_status()
|
|
966
|
+
resp.raw.decode_content = False
|
|
967
|
+
content = resp.raw.read()
|
|
968
|
+
content_encoding = resp.headers.get('Content-Encoding', None)
|
|
918
969
|
|
|
919
970
|
# Don't check MD5 for http because the etag can come in many
|
|
920
971
|
# forms from either GCS, S3 or another service entirely. We
|
|
921
972
|
# probably won't figure out how to decode it right.
|
|
922
973
|
# etag = resp.headers.get('etag', None)
|
|
923
|
-
content_encoding = resp.headers.get('Content-Encoding', None)
|
|
924
|
-
|
|
925
|
-
# requests automatically decodes these
|
|
926
|
-
if content_encoding in (None, '', 'gzip', 'deflate', 'br'):
|
|
927
|
-
content_encoding = None
|
|
928
974
|
|
|
929
|
-
return (
|
|
975
|
+
return (content, content_encoding, None, None)
|
|
930
976
|
|
|
931
977
|
@retry
|
|
932
978
|
def save_file(self, src, dest, resumable) -> tuple[bool, int]:
|
|
@@ -1027,7 +1073,6 @@ class HttpInterface(StorageInterface):
|
|
|
1027
1073
|
)
|
|
1028
1074
|
|
|
1029
1075
|
for res in results.get("items", []):
|
|
1030
|
-
print(res["name"])
|
|
1031
1076
|
yield res["name"].removeprefix(strip)
|
|
1032
1077
|
|
|
1033
1078
|
token = results.get("nextPageToken", None)
|
|
@@ -1500,6 +1545,47 @@ class S3Interface(StorageInterface):
|
|
|
1500
1545
|
for filename in iterate(resp):
|
|
1501
1546
|
yield filename
|
|
1502
1547
|
|
|
1548
|
+
def subtree_size(self, prefix:str = "") -> tuple[int,int]:
|
|
1549
|
+
layer_path = self.get_path_to_file("")
|
|
1550
|
+
path = posixpath.join(layer_path, prefix)
|
|
1551
|
+
|
|
1552
|
+
@retry
|
|
1553
|
+
def s3lst(path, continuation_token=None):
|
|
1554
|
+
kwargs = {
|
|
1555
|
+
'Bucket': self._path.bucket,
|
|
1556
|
+
'Prefix': path,
|
|
1557
|
+
**self._additional_attrs
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
if continuation_token:
|
|
1561
|
+
kwargs['ContinuationToken'] = continuation_token
|
|
1562
|
+
|
|
1563
|
+
return self._conn.list_objects_v2(**kwargs)
|
|
1564
|
+
|
|
1565
|
+
resp = s3lst(path)
|
|
1566
|
+
|
|
1567
|
+
def iterate(resp):
|
|
1568
|
+
if 'Contents' not in resp.keys():
|
|
1569
|
+
resp['Contents'] = []
|
|
1570
|
+
|
|
1571
|
+
for item in resp['Contents']:
|
|
1572
|
+
yield item.get('Size', 0)
|
|
1573
|
+
|
|
1574
|
+
total_bytes = 0
|
|
1575
|
+
total_files = 0
|
|
1576
|
+
for num_bytes in iterate(resp):
|
|
1577
|
+
total_files += 1
|
|
1578
|
+
total_bytes += num_bytes
|
|
1579
|
+
|
|
1580
|
+
while resp['IsTruncated'] and resp['NextContinuationToken']:
|
|
1581
|
+
resp = s3lst(path, resp['NextContinuationToken'])
|
|
1582
|
+
|
|
1583
|
+
for num_bytes in iterate(resp):
|
|
1584
|
+
total_files += 1
|
|
1585
|
+
total_bytes += num_bytes
|
|
1586
|
+
|
|
1587
|
+
return (total_files, total_bytes)
|
|
1588
|
+
|
|
1503
1589
|
def release_connection(self):
|
|
1504
1590
|
global S3_POOL
|
|
1505
1591
|
service = self._path.alias or 's3'
|
cloudfiles/paths.py
CHANGED
|
@@ -22,7 +22,7 @@ PRECOMPUTED_SUFFIX = '|neuroglancer-precomputed:'
|
|
|
22
22
|
|
|
23
23
|
ALIAS_FILE = os.path.join(CLOUD_FILES_DIR, "aliases.json")
|
|
24
24
|
OFFICIAL_ALIASES = {
|
|
25
|
-
"nokura": "s3://https://
|
|
25
|
+
"nokura": "s3://https://c10s.pni.princeton.edu/",
|
|
26
26
|
"matrix": "s3://https://s3-hpcrc.rc.princeton.edu/",
|
|
27
27
|
"tigerdata": "s3://https://td.princeton.edu/",
|
|
28
28
|
}
|
cloudfiles_cli/cloudfiles_cli.py
CHANGED
|
@@ -182,6 +182,19 @@ def get_mfp(path, recursive):
|
|
|
182
182
|
|
|
183
183
|
return (many, flat, prefix, suffix)
|
|
184
184
|
|
|
185
|
+
@main.command("mkdir")
|
|
186
|
+
@click.argument("paths", nargs=-1)
|
|
187
|
+
def _mkdir(paths):
|
|
188
|
+
"""
|
|
189
|
+
Create paths on the local file system.
|
|
190
|
+
"""
|
|
191
|
+
for path in paths:
|
|
192
|
+
path = normalize_path(path)
|
|
193
|
+
protocol = get_protocol(path)
|
|
194
|
+
|
|
195
|
+
if protocol == "file":
|
|
196
|
+
mkdir(path.replace("file://", "", 1))
|
|
197
|
+
|
|
185
198
|
@main.command()
|
|
186
199
|
@click.argument("source", nargs=-1)
|
|
187
200
|
@click.argument("destination", nargs=1)
|
|
@@ -588,6 +601,7 @@ def touch(
|
|
|
588
601
|
ctx, sources,
|
|
589
602
|
progress, no_sign_request,
|
|
590
603
|
):
|
|
604
|
+
"""Create file if it doesn't exist."""
|
|
591
605
|
sources = list(map(normalize_path, sources))
|
|
592
606
|
sources = [ src.replace("precomputed://", "") for src in sources ]
|
|
593
607
|
pbar = tqdm(total=len(sources), desc="Touch", disable=(not progress))
|
|
@@ -788,14 +802,22 @@ def __rm(cloudpath, progress, paths):
|
|
|
788
802
|
@click.option('-c', '--grand-total', is_flag=True, default=False, help="Sum a grand total of all inputs.")
|
|
789
803
|
@click.option('-s', '--summarize', is_flag=True, default=False, help="Sum a total for each input argument.")
|
|
790
804
|
@click.option('-h', '--human-readable', is_flag=True, default=False, help='"Human-readable" output. Use unit suffixes: Bytes, KiB, MiB, GiB, TiB, PiB, and EiB.')
|
|
791
|
-
|
|
805
|
+
@click.option('-N', '--count-files', is_flag=True, default=False, help='Also report the number of files.')
|
|
806
|
+
def du(paths, grand_total, summarize, human_readable, count_files):
|
|
792
807
|
"""Display disk usage statistics."""
|
|
793
808
|
results = []
|
|
809
|
+
|
|
810
|
+
list_data = False
|
|
811
|
+
|
|
794
812
|
for path in paths:
|
|
795
813
|
npath = normalize_path(path)
|
|
796
814
|
if ispathdir(path):
|
|
797
815
|
cf = CloudFiles(npath)
|
|
798
|
-
|
|
816
|
+
if summarize:
|
|
817
|
+
results.append(cf.subtree_size())
|
|
818
|
+
else:
|
|
819
|
+
list_data = True
|
|
820
|
+
results.append(cf.size(cf.list()))
|
|
799
821
|
else:
|
|
800
822
|
cf = CloudFiles(os.path.dirname(npath))
|
|
801
823
|
sz = cf.size(os.path.basename(npath))
|
|
@@ -824,8 +846,15 @@ def du(paths, grand_total, summarize, human_readable):
|
|
|
824
846
|
return f"{(val / 2**60):.2f} EiB"
|
|
825
847
|
|
|
826
848
|
summary = {}
|
|
849
|
+
num_files = 0
|
|
827
850
|
for path, res in zip(paths, results):
|
|
828
|
-
|
|
851
|
+
if list_data:
|
|
852
|
+
summary[path] = sum(res.values())
|
|
853
|
+
num_files += len(res)
|
|
854
|
+
else:
|
|
855
|
+
summary[path] = res["num_bytes"]
|
|
856
|
+
num_files += res["N"]
|
|
857
|
+
|
|
829
858
|
if summarize:
|
|
830
859
|
print(f"{SI(summary[path])}\t{path}")
|
|
831
860
|
|
|
@@ -835,7 +864,10 @@ def du(paths, grand_total, summarize, human_readable):
|
|
|
835
864
|
print(f"{SI(size)}\t{pth}")
|
|
836
865
|
|
|
837
866
|
if grand_total:
|
|
838
|
-
print(f"{SI(sum(summary.values()))}\
|
|
867
|
+
print(f"{SI(sum(summary.values()))}\tbytes total")
|
|
868
|
+
|
|
869
|
+
if count_files:
|
|
870
|
+
print(f"{num_files}\tfiles total")
|
|
839
871
|
|
|
840
872
|
@main.command()
|
|
841
873
|
@click.argument('paths', nargs=-1)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"git_version": "cab2668", "is_release": true}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|