Skip to content

Commit ae45082

Browse files
WIP
1 parent 28e4bf1 commit ae45082

1 file changed

Lines changed: 82 additions & 53 deletions

File tree

pyathena/filesystem/s3.py

Lines changed: 82 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ def _head_bucket(self, bucket, refresh: bool = False) -> Optional[S3Object]:
174174
bucket=bucket,
175175
key=None,
176176
version_id=None,
177+
delimiter=None,
177178
)
178179
self.dircache[bucket] = file
179180
else:
@@ -207,6 +208,7 @@ def _head_object(
207208
bucket=bucket,
208209
key=key,
209210
version_id=version_id,
211+
delimiter=None,
210212
)
211213
self.dircache[path] = file
212214
else:
@@ -234,6 +236,7 @@ def _ls_buckets(self, refresh: bool = False) -> List[S3Object]:
234236
bucket=b["Name"],
235237
key=None,
236238
version_id=None,
239+
delimiter=None,
237240
)
238241
for b in response["Buckets"]
239242
]
@@ -254,58 +257,63 @@ def _ls_dirs(
254257
bucket, key, version_id = self.parse_path(path)
255258
if key:
256259
prefix = f"{key}/{prefix if prefix else ''}"
257-
if path not in self.dircache or refresh:
258-
files: List[S3Object] = []
259-
while True:
260-
request: Dict[Any, Any] = {
261-
"Bucket": bucket,
262-
"Prefix": prefix,
263-
"Delimiter": delimiter,
264-
}
265-
if next_token:
266-
request.update({"ContinuationToken": next_token})
267-
if max_keys:
268-
request.update({"MaxKeys": max_keys})
269-
response = self._call(
270-
self._client.list_objects_v2,
271-
**request,
272-
)
273-
files.extend(
274-
S3Object(
275-
init={
276-
"ContentLength": 0,
277-
"ContentType": None,
278-
"StorageClass": S3StorageClass.S3_STORAGE_CLASS_DIRECTORY,
279-
"ETag": None,
280-
"LastModified": None,
281-
},
282-
type=S3ObjectType.S3_OBJECT_TYPE_DIRECTORY,
283-
bucket=bucket,
284-
key=c["Prefix"][:-1].rstrip("/"),
285-
version_id=version_id,
286-
)
287-
for c in response.get("CommonPrefixes", [])
288-
)
289-
files.extend(
290-
S3Object(
291-
init=c,
292-
type=S3ObjectType.S3_OBJECT_TYPE_FILE,
293-
bucket=bucket,
294-
key=c["Key"],
295-
)
296-
for c in response.get("Contents", [])
297-
)
298-
next_token = response.get("NextContinuationToken")
299-
if not next_token:
300-
break
301-
if files:
302-
self.dircache[path] = files
303-
else:
260+
261+
if path in self.dircache and not refresh:
304262
cache = self.dircache[path]
305263
if not isinstance(cache, list):
306-
files = [cache]
264+
caches = [cache]
307265
else:
308-
files = cache
266+
caches = cache
267+
if all([f.delimiter == delimiter for f in caches]):
268+
return caches
269+
270+
files: List[S3Object] = []
271+
while True:
272+
request: Dict[Any, Any] = {
273+
"Bucket": bucket,
274+
"Prefix": prefix,
275+
"Delimiter": delimiter,
276+
}
277+
if next_token:
278+
request.update({"ContinuationToken": next_token})
279+
if max_keys:
280+
request.update({"MaxKeys": max_keys})
281+
response = self._call(
282+
self._client.list_objects_v2,
283+
**request,
284+
)
285+
files.extend(
286+
S3Object(
287+
init={
288+
"ContentLength": 0,
289+
"ContentType": None,
290+
"StorageClass": S3StorageClass.S3_STORAGE_CLASS_DIRECTORY,
291+
"ETag": None,
292+
"LastModified": None,
293+
},
294+
type=S3ObjectType.S3_OBJECT_TYPE_DIRECTORY,
295+
bucket=bucket,
296+
key=c["Prefix"][:-1].rstrip("/"),
297+
version_id=version_id,
298+
delimiter=delimiter,
299+
)
300+
for c in response.get("CommonPrefixes", [])
301+
)
302+
files.extend(
303+
S3Object(
304+
init=c,
305+
type=S3ObjectType.S3_OBJECT_TYPE_FILE,
306+
bucket=bucket,
307+
key=c["Key"],
308+
delimiter=delimiter,
309+
)
310+
for c in response.get("Contents", [])
311+
)
312+
next_token = response.get("NextContinuationToken")
313+
if not next_token:
314+
break
315+
if files:
316+
self.dircache[path] = files
309317
return files
310318

311319
def ls(
@@ -340,6 +348,7 @@ def info(self, path: str, **kwargs) -> S3Object:
340348
bucket=bucket,
341349
key=None,
342350
version_id=None,
351+
delimiter=None,
343352
)
344353
if not refresh:
345354
caches: Union[List[S3Object], S3Object] = self._ls_from_cache(path)
@@ -366,6 +375,7 @@ def info(self, path: str, **kwargs) -> S3Object:
366375
bucket=bucket,
367376
key=key.rstrip("/") if key else None,
368377
version_id=version_id,
378+
delimiter=None,
369379
)
370380
if key:
371381
object_info = self._head_object(path, refresh=refresh, version_id=version_id)
@@ -402,31 +412,50 @@ def info(self, path: str, **kwargs) -> S3Object:
402412
bucket=bucket,
403413
key=key.rstrip("/") if key else None,
404414
version_id=version_id,
415+
delimiter=None,
405416
)
406417
else:
407418
raise FileNotFoundError(path)
408419

409-
def find(
420+
def _find(
410421
self,
411422
path: str,
412423
maxdepth: Optional[int] = None,
413424
withdirs: Optional[bool] = None,
414-
detail: bool = False,
415425
**kwargs,
416-
) -> Union[Dict[str, S3Object], List[str]]:
417-
# TODO: Support maxdepth and withdirs
426+
) -> List[S3Object]:
418427
path = self._strip_protocol(path)
419428
if path in ["", "/"]:
420429
raise ValueError("Cannot traverse all files in S3.")
421430
bucket, key, _ = self.parse_path(path)
422431
prefix = kwargs.pop("prefix", "")
432+
if maxdepth:
433+
return super().find(
434+
path=path,
435+
maxdepth=maxdepth,
436+
withdirs=withdirs,
437+
detail=True,
438+
**kwargs
439+
).values()
423440

424441
files = self._ls_dirs(path, prefix=prefix, delimiter="")
425442
if not files and key:
426443
try:
427444
files = [self.info(path)]
428445
except FileNotFoundError:
429446
files = []
447+
return files
448+
449+
def find(
450+
self,
451+
path: str,
452+
maxdepth: Optional[int] = None,
453+
withdirs: Optional[bool] = None,
454+
detail: bool = False,
455+
**kwargs,
456+
) -> Union[Dict[str, S3Object], List[str]]:
457+
# TODO: Support withdirs
458+
files = self._find(path=path, maxdepth=maxdepth, withdirs=withdirs, **kwargs)
430459
if detail:
431460
return {f.name: f for f in files}
432461
else:

0 commit comments

Comments
 (0)