cloud-files 4.25.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cloud-files
3
- Version: 4.25.0
3
+ Version: 4.27.0
4
4
  Summary: Fast access to cloud storage and local FS.
5
5
  Home-page: https://github.com/seung-lab/cloud-files/
6
6
  Author: William Silversmith
@@ -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=YUf_-7DS8-2mCKOWT6mMKxl1glxMTEGomhpCnebtsy8,44801
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=Qqhjv2GIVw3ibaTgPpiGK97i7aBDgU9O0bBuOiLI2KM,36117
8
+ cloudfiles/interfaces.py,sha256=4ICm7tnS8HHK5IKw-HYD653V-qndprPRK8e1cxUYKgA,36782
9
9
  cloudfiles/lib.py,sha256=fEqL5APu_WQhl2yxqQbwE7msHdu7U8pstAJw6LgoKO0,5142
10
- cloudfiles/paths.py,sha256=tqR9XnRdAKopTJqSM6V5xrMo1xfmBdl9b5DpBLZnoB0,9998
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=3BSV2Hn8FGGn4QCs5FP3eGs4WEs5cIXRBsXuF0eIgIY,4918
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.25.0.dist-info/AUTHORS,sha256=7E2vC894bbLPO_kvUuEB2LFZZbIxZn23HabxH7x0Hgo,266
20
- cloud_files-4.25.0.dist-info/LICENSE,sha256=Jna4xYE8CCQmaxjr5Fs-wmUBnIQJ1DGcNn9MMjbkprk,1538
21
- cloud_files-4.25.0.dist-info/METADATA,sha256=wXrUnPFfMt3vMXfcxvqMpOB6fVsHRV1cEhDgFr4LdqQ,26804
22
- cloud_files-4.25.0.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
23
- cloud_files-4.25.0.dist-info/entry_points.txt,sha256=xlirb1FVhn1mbcv4IoyMEGumDqKOA4VMVd3drsRQxIg,51
24
- cloud_files-4.25.0.dist-info/pbr.json,sha256=v8YZnfyUynJ66Bxxf0b3fjSVxyAmHqmTI4XJ4BP25pg,46
25
- cloud_files-4.25.0.dist-info/top_level.txt,sha256=xPyrST3okJbsmdCF5IC2gYAVxg_aD5AYVTnNo8UuoZU,26
26
- cloud_files-4.25.0.dist-info/RECORD,,
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 http_credentials, CLOUD_FILES_DIR, CLOUD_FILES_LOCK_DIR
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
- with self.session.head(key) as resp:
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 = { "Range": "bytes={}-{}".format(start, end) }
768
- resp = self.session.get(key, headers=headers)
769
- else:
770
- resp = self.session.get(key)
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
- with self.session.get(key, stream=True) as resp:
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,10 +26,16 @@ 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
- ALLOWED_FORMATS = [ 'graphene', 'precomputed', 'boss', 'n5' ]
33
+ ALLOWED_FORMATS = [
34
+ 'graphene', 'precomputed', 'boss',
35
+ 'n5', 'zarr', 'zarr2', 'zarr3',
36
+ 'brainmaps', 'deepzoom', 'nggraph',
37
+ 'render', 'vtk', 'nifti', 'dvid',
38
+ ]
33
39
 
34
40
  def update_aliases_from_file():
35
41
  global ALIASES_FROM_FILE
@@ -64,7 +70,13 @@ def cloudpath_error(cloudpath):
64
70
  def mkregexp():
65
71
  fmt_capture = r'|'.join(ALLOWED_FORMATS)
66
72
  fmt_capture = "(?:(?P<fmt>{})://)".format(fmt_capture)
67
- proto_capture = r'|'.join(ALLOWED_PROTOCOLS)
73
+
74
+ allowed_protos = [
75
+ p.replace('+', r'\+')
76
+ for p in ALLOWED_PROTOCOLS
77
+ ]
78
+
79
+ proto_capture = r'|'.join(allowed_protos)
68
80
  proto_capture = "(?:(?P<proto>{})://)".format(proto_capture)
69
81
  regexp = "{}?{}?".format(fmt_capture, proto_capture)
70
82
  return regexp
@@ -147,11 +159,16 @@ for alias, host in OFFICIAL_ALIASES.items():
147
159
  ## Other Path Library Functions
148
160
 
149
161
  def normalize(path):
150
- proto = get_protocol(path)
162
+ fmt, proto, endpoint, cloudpath, alias = extract_format_protocol(
163
+ path, allow_defaults=False
164
+ )
165
+
151
166
  if proto is None:
152
167
  proto = "file"
153
- path = toabs(path)
154
- return f"file://{path}"
168
+ cloudpath = toabs(cloudpath)
169
+ fmt = f"{fmt}://" if fmt else ""
170
+ path = f"{fmt}{proto}://{cloudpath}"
171
+
155
172
  return path
156
173
 
157
174
  def dirname(path):
@@ -264,7 +281,7 @@ def pop_protocol(cloudpath):
264
281
 
265
282
  return (protocol, cloudpath)
266
283
 
267
- def extract_format_protocol(cloudpath:str) -> tuple:
284
+ def extract_format_protocol(cloudpath:str, allow_defaults=True) -> tuple:
268
285
  error = UnsupportedProtocolError(cloudpath_error(cloudpath))
269
286
 
270
287
  alias, cloudpath = resolve_alias(cloudpath)
@@ -276,12 +293,18 @@ def extract_format_protocol(cloudpath:str) -> tuple:
276
293
  groups = m.groups()
277
294
  cloudpath = re.sub(CLOUDPATH_REGEXP, '', cloudpath, count=1)
278
295
 
279
- fmt = m.group('fmt') or 'precomputed'
296
+ fmt = m.group('fmt')
297
+ if not fmt and allow_defaults:
298
+ fmt = 'precomputed'
280
299
  proto = m.group('proto')
281
300
  endpoint = None
282
301
 
283
- if proto in ('http', 'https'):
284
- cloudpath = proto + "://" + cloudpath
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
285
308
  parse = urllib.parse.urlparse(cloudpath)
286
309
  endpoint = parse.scheme + "://" + parse.netloc
287
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": "7dcc163", "is_release": true}