cloud-files 4.26.0__py3-none-any.whl → 4.27.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-4.26.0.dist-info → cloud_files-4.27.0.dist-info}/METADATA +1 -1
- {cloud_files-4.26.0.dist-info → cloud_files-4.27.0.dist-info}/RECORD +12 -12
- cloud_files-4.27.0.dist-info/pbr.json +1 -0
- cloudfiles/cloudfiles.py +2 -1
- cloudfiles/interfaces.py +37 -8
- cloudfiles/paths.py +15 -4
- cloudfiles/secrets.py +18 -0
- cloud_files-4.26.0.dist-info/pbr.json +0 -1
- {cloud_files-4.26.0.dist-info → cloud_files-4.27.0.dist-info}/AUTHORS +0 -0
- {cloud_files-4.26.0.dist-info → cloud_files-4.27.0.dist-info}/LICENSE +0 -0
- {cloud_files-4.26.0.dist-info → cloud_files-4.27.0.dist-info}/WHEEL +0 -0
- {cloud_files-4.26.0.dist-info → cloud_files-4.27.0.dist-info}/entry_points.txt +0 -0
- {cloud_files-4.26.0.dist-info → cloud_files-4.27.0.dist-info}/top_level.txt +0 -0
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
cloudfiles/__init__.py,sha256=pLB4CcV2l3Jgv_ni1520Np1pfzFj8Cpr87vNxFT3rNI,493
|
|
2
2
|
cloudfiles/buckets.py,sha256=eRAYdDfvVpNyJyK5ryDRMwgNJUeEuFBJ6doWU2JkAcA,74
|
|
3
|
-
cloudfiles/cloudfiles.py,sha256=
|
|
3
|
+
cloudfiles/cloudfiles.py,sha256=QlrQlU94gqNhKWyOuP0xe58UEwk2x8wtZ7n9LKiyLpM,44854
|
|
4
4
|
cloudfiles/compression.py,sha256=pqYdpu5vfFv-094BpfZ2pgRjVu7ESM9pAZC09P6E8bY,6150
|
|
5
5
|
cloudfiles/connectionpools.py,sha256=aL8RiSjRepECfgAFmJcz80aJFKbou7hsbuEgugDKwB8,4814
|
|
6
6
|
cloudfiles/exceptions.py,sha256=H2IcMlZoy2Bsn-6wCPwyLDjg66LZCyxtcf3s_p21FDw,770
|
|
7
7
|
cloudfiles/gcs.py,sha256=_njJ7TpqwrHCjPHRGkBN5alCrCWKM2m9qdy5DhxMZ7U,3718
|
|
8
|
-
cloudfiles/interfaces.py,sha256=
|
|
8
|
+
cloudfiles/interfaces.py,sha256=4ICm7tnS8HHK5IKw-HYD653V-qndprPRK8e1cxUYKgA,36782
|
|
9
9
|
cloudfiles/lib.py,sha256=fEqL5APu_WQhl2yxqQbwE7msHdu7U8pstAJw6LgoKO0,5142
|
|
10
|
-
cloudfiles/paths.py,sha256=
|
|
10
|
+
cloudfiles/paths.py,sha256=xadVh5Vw8wAack1cws5dzVIlYQ3r8h8lrP43umUSuT0,10547
|
|
11
11
|
cloudfiles/resumable_tools.py,sha256=pK-VcoPjQ2BjGjvlvH4dDCBf6lNsqHG-weiBgxVFbzA,5838
|
|
12
12
|
cloudfiles/scheduler.py,sha256=DqDANmOpB3NdzFgJDNMMibRIkCrXQqIh2XGL8GWoc9c,3668
|
|
13
|
-
cloudfiles/secrets.py,sha256=
|
|
13
|
+
cloudfiles/secrets.py,sha256=791b5a8nWSBYtlleGzKeoYIR5jl-FI1bw6INRM4Wy-0,5295
|
|
14
14
|
cloudfiles/threaded_queue.py,sha256=Nl4vfXhQ6nDLF8PZpSSBpww0M2zWtcd4DLs3W3BArBw,7082
|
|
15
15
|
cloudfiles/typing.py,sha256=f3ZYkNfN9poxhGu5j-P0KCxjCCqSn9HAg5KiIPkjnCg,416
|
|
16
16
|
cloudfiles_cli/LICENSE,sha256=Jna4xYE8CCQmaxjr5Fs-wmUBnIQJ1DGcNn9MMjbkprk,1538
|
|
17
17
|
cloudfiles_cli/__init__.py,sha256=Wftt3R3F21QsHtWqx49ODuqT9zcSr0em7wk48kcH0WM,29
|
|
18
18
|
cloudfiles_cli/cloudfiles_cli.py,sha256=eETIOK4QyztQcpA4ZRny21SobLtcrPDlzZ_JaKBmmmA,28449
|
|
19
|
-
cloud_files-4.
|
|
20
|
-
cloud_files-4.
|
|
21
|
-
cloud_files-4.
|
|
22
|
-
cloud_files-4.
|
|
23
|
-
cloud_files-4.
|
|
24
|
-
cloud_files-4.
|
|
25
|
-
cloud_files-4.
|
|
26
|
-
cloud_files-4.
|
|
19
|
+
cloud_files-4.27.0.dist-info/AUTHORS,sha256=7E2vC894bbLPO_kvUuEB2LFZZbIxZn23HabxH7x0Hgo,266
|
|
20
|
+
cloud_files-4.27.0.dist-info/LICENSE,sha256=Jna4xYE8CCQmaxjr5Fs-wmUBnIQJ1DGcNn9MMjbkprk,1538
|
|
21
|
+
cloud_files-4.27.0.dist-info/METADATA,sha256=7xdozjXt0yT1OKZV47M9dWeip9PUx9Wj9adUcO0qZ_M,26804
|
|
22
|
+
cloud_files-4.27.0.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
|
|
23
|
+
cloud_files-4.27.0.dist-info/entry_points.txt,sha256=xlirb1FVhn1mbcv4IoyMEGumDqKOA4VMVd3drsRQxIg,51
|
|
24
|
+
cloud_files-4.27.0.dist-info/pbr.json,sha256=C4Xk2iNLylqeAReQ_DjwEN6rVj4PC_6x96XsvMs7138,46
|
|
25
|
+
cloud_files-4.27.0.dist-info/top_level.txt,sha256=xPyrST3okJbsmdCF5IC2gYAVxg_aD5AYVTnNo8UuoZU,26
|
|
26
|
+
cloud_files-4.27.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"git_version": "e4b04bf", "is_release": true}
|
cloudfiles/cloudfiles.py
CHANGED
|
@@ -44,7 +44,7 @@ from .scheduler import schedule_jobs
|
|
|
44
44
|
from .interfaces import (
|
|
45
45
|
FileInterface, HttpInterface,
|
|
46
46
|
S3Interface, GoogleCloudStorageInterface,
|
|
47
|
-
MemoryInterface
|
|
47
|
+
MemoryInterface, CaveInterface,
|
|
48
48
|
)
|
|
49
49
|
|
|
50
50
|
INTERFACES = {
|
|
@@ -54,6 +54,7 @@ INTERFACES = {
|
|
|
54
54
|
'http': HttpInterface,
|
|
55
55
|
'https': HttpInterface,
|
|
56
56
|
'mem': MemoryInterface,
|
|
57
|
+
'middleauth+https': CaveInterface,
|
|
57
58
|
}
|
|
58
59
|
for alias in ALIASES:
|
|
59
60
|
INTERFACES[alias] = S3Interface
|
cloudfiles/interfaces.py
CHANGED
|
@@ -24,7 +24,12 @@ from .compression import COMPRESSION_TYPES
|
|
|
24
24
|
from .connectionpools import S3ConnectionPool, GCloudBucketPool, MemoryPool, MEMORY_DATA
|
|
25
25
|
from .exceptions import MD5IntegrityError, CompressionError
|
|
26
26
|
from .lib import mkdir, sip, md5, validate_s3_multipart_etag
|
|
27
|
-
from .secrets import
|
|
27
|
+
from .secrets import (
|
|
28
|
+
http_credentials,
|
|
29
|
+
cave_credentials,
|
|
30
|
+
CLOUD_FILES_DIR,
|
|
31
|
+
CLOUD_FILES_LOCK_DIR,
|
|
32
|
+
)
|
|
28
33
|
|
|
29
34
|
COMPRESSION_EXTENSIONS = ('.gz', '.br', '.zstd','.bz2','.xz')
|
|
30
35
|
GZIP_TYPES = (True, 'gzip', 1)
|
|
@@ -731,6 +736,9 @@ class HttpInterface(StorageInterface):
|
|
|
731
736
|
if secrets and 'user' in secrets and 'password' in secrets:
|
|
732
737
|
self.session.auth = (secrets['user'], secrets['password'])
|
|
733
738
|
|
|
739
|
+
def default_headers(self):
|
|
740
|
+
return {}
|
|
741
|
+
|
|
734
742
|
def get_path_to_file(self, file_path):
|
|
735
743
|
return posixpath.join(self._path.host, self._path.path, file_path)
|
|
736
744
|
|
|
@@ -749,7 +757,8 @@ class HttpInterface(StorageInterface):
|
|
|
749
757
|
@retry
|
|
750
758
|
def head(self, file_path):
|
|
751
759
|
key = self.get_path_to_file(file_path)
|
|
752
|
-
|
|
760
|
+
headers = self.default_headers()
|
|
761
|
+
with self.session.head(key, headers=headers) as resp:
|
|
753
762
|
resp.raise_for_status()
|
|
754
763
|
return resp.headers
|
|
755
764
|
|
|
@@ -761,13 +770,14 @@ class HttpInterface(StorageInterface):
|
|
|
761
770
|
def get_file(self, file_path, start=None, end=None, part_size=None):
|
|
762
771
|
key = self.get_path_to_file(file_path)
|
|
763
772
|
|
|
773
|
+
headers = self.default_headers()
|
|
764
774
|
if start is not None or end is not None:
|
|
765
775
|
start = int(start) if start is not None else 0
|
|
766
776
|
end = int(end - 1) if end is not None else ''
|
|
767
|
-
headers
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
777
|
+
headers["Range"] = f"bytes={start}-{end}"
|
|
778
|
+
|
|
779
|
+
resp = self.session.get(key, headers=headers)
|
|
780
|
+
|
|
771
781
|
if resp.status_code in (404, 403):
|
|
772
782
|
return (None, None, None, None)
|
|
773
783
|
resp.close()
|
|
@@ -788,7 +798,8 @@ class HttpInterface(StorageInterface):
|
|
|
788
798
|
@retry
|
|
789
799
|
def exists(self, file_path):
|
|
790
800
|
key = self.get_path_to_file(file_path)
|
|
791
|
-
|
|
801
|
+
headers = self.default_headers()
|
|
802
|
+
with self.session.get(key, stream=True, headers=headers) as resp:
|
|
792
803
|
return resp.ok
|
|
793
804
|
|
|
794
805
|
def files_exist(self, file_paths):
|
|
@@ -805,11 +816,15 @@ class HttpInterface(StorageInterface):
|
|
|
805
816
|
if prefix and prefix[-1] != '/':
|
|
806
817
|
prefix += '/'
|
|
807
818
|
|
|
819
|
+
headers = self.default_headers()
|
|
820
|
+
|
|
808
821
|
@retry
|
|
809
822
|
def request(token):
|
|
823
|
+
nonlocal headers
|
|
810
824
|
results = self.session.get(
|
|
811
825
|
f"https://storage.googleapis.com/storage/v1/b/{bucket}/o",
|
|
812
826
|
params={ "prefix": prefix, "pageToken": token },
|
|
827
|
+
headers=headers,
|
|
813
828
|
)
|
|
814
829
|
results.raise_for_status()
|
|
815
830
|
results.close()
|
|
@@ -832,12 +847,13 @@ class HttpInterface(StorageInterface):
|
|
|
832
847
|
baseurl = posixpath.join(self._path.host, self._path.path)
|
|
833
848
|
|
|
834
849
|
directories = ['']
|
|
850
|
+
headers = self.default_headers()
|
|
835
851
|
|
|
836
852
|
while directories:
|
|
837
853
|
directory = directories.pop()
|
|
838
854
|
url = posixpath.join(baseurl, directory)
|
|
839
855
|
|
|
840
|
-
resp = requests.get(url)
|
|
856
|
+
resp = requests.get(url, headers=headers)
|
|
841
857
|
resp.raise_for_status()
|
|
842
858
|
|
|
843
859
|
if 'text/html' not in resp.headers["Content-Type"]:
|
|
@@ -1200,3 +1216,16 @@ class S3Interface(StorageInterface):
|
|
|
1200
1216
|
with S3_BUCKET_POOL_LOCK:
|
|
1201
1217
|
pool = S3_POOL[S3ConnectionPoolParams(service, self._path.bucket, self._request_payer)]
|
|
1202
1218
|
pool.release_connection(self._conn)
|
|
1219
|
+
|
|
1220
|
+
class CaveInterface(HttpInterface):
|
|
1221
|
+
"""
|
|
1222
|
+
CAVE is an internal system that powers proofreading
|
|
1223
|
+
systems in Seung Lab. If you have no idea what this
|
|
1224
|
+
is, don't worry about it.
|
|
1225
|
+
see: https://github.com/CAVEconnectome
|
|
1226
|
+
"""
|
|
1227
|
+
def default_headers(self):
|
|
1228
|
+
cred = cave_credentials()
|
|
1229
|
+
return {
|
|
1230
|
+
"Authorization": f"Bearer {cred['token']}",
|
|
1231
|
+
}
|
cloudfiles/paths.py
CHANGED
|
@@ -26,7 +26,8 @@ ALIASES_FROM_FILE = None
|
|
|
26
26
|
ALIASES = {}
|
|
27
27
|
BASE_ALLOWED_PROTOCOLS = [
|
|
28
28
|
'gs', 'file', 's3',
|
|
29
|
-
'http', 'https', 'mem'
|
|
29
|
+
'http', 'https', 'mem',
|
|
30
|
+
'middleauth+https', 'ngauth+https',
|
|
30
31
|
]
|
|
31
32
|
ALLOWED_PROTOCOLS = list(BASE_ALLOWED_PROTOCOLS)
|
|
32
33
|
ALLOWED_FORMATS = [
|
|
@@ -69,7 +70,13 @@ def cloudpath_error(cloudpath):
|
|
|
69
70
|
def mkregexp():
|
|
70
71
|
fmt_capture = r'|'.join(ALLOWED_FORMATS)
|
|
71
72
|
fmt_capture = "(?:(?P<fmt>{})://)".format(fmt_capture)
|
|
72
|
-
|
|
73
|
+
|
|
74
|
+
allowed_protos = [
|
|
75
|
+
p.replace('+', r'\+')
|
|
76
|
+
for p in ALLOWED_PROTOCOLS
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
proto_capture = r'|'.join(allowed_protos)
|
|
73
80
|
proto_capture = "(?:(?P<proto>{})://)".format(proto_capture)
|
|
74
81
|
regexp = "{}?{}?".format(fmt_capture, proto_capture)
|
|
75
82
|
return regexp
|
|
@@ -292,8 +299,12 @@ def extract_format_protocol(cloudpath:str, allow_defaults=True) -> tuple:
|
|
|
292
299
|
proto = m.group('proto')
|
|
293
300
|
endpoint = None
|
|
294
301
|
|
|
295
|
-
|
|
296
|
-
|
|
302
|
+
tmp_proto = None
|
|
303
|
+
if proto is not None:
|
|
304
|
+
tmp_proto = proto.replace("middleauth+", "").replace("ngauth+", "")
|
|
305
|
+
|
|
306
|
+
if tmp_proto in ('http', 'https'):
|
|
307
|
+
cloudpath = tmp_proto + "://" + cloudpath
|
|
297
308
|
parse = urllib.parse.urlparse(cloudpath)
|
|
298
309
|
endpoint = parse.scheme + "://" + parse.netloc
|
|
299
310
|
cloudpath = cloudpath.replace(endpoint, '', 1)
|
cloudfiles/secrets.py
CHANGED
|
@@ -137,6 +137,24 @@ def aws_credentials(bucket = '', service = 'aws', skip_files=False):
|
|
|
137
137
|
AWS_CREDENTIALS_CACHE[service][bucket] = aws_credentials
|
|
138
138
|
return aws_credentials
|
|
139
139
|
|
|
140
|
+
CAVE_CREDENTIALS = None
|
|
141
|
+
def cave_credentials():
|
|
142
|
+
global CAVE_CREDENTIALS
|
|
143
|
+
default_file_path = 'cave-secret.json'
|
|
144
|
+
path = secretpath(default_file_path)
|
|
145
|
+
|
|
146
|
+
if CAVE_CREDENTIALS:
|
|
147
|
+
return CAVE_CREDENTIALS
|
|
148
|
+
|
|
149
|
+
if os.path.exists(path):
|
|
150
|
+
with open(path, 'rt') as f:
|
|
151
|
+
CAVE_CREDENTIALS = json.loads(f.read())
|
|
152
|
+
else:
|
|
153
|
+
CAVE_CREDENTIALS = None
|
|
154
|
+
|
|
155
|
+
return CAVE_CREDENTIALS
|
|
156
|
+
|
|
157
|
+
|
|
140
158
|
HTTP_CREDENTIALS = None
|
|
141
159
|
def http_credentials():
|
|
142
160
|
global HTTP_CREDENTIALS
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"git_version": "3ae7c76", "is_release": true}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|