|
22 | 22 | import logging |
23 | 23 | import os |
24 | 24 | import threading |
25 | | -from collections.abc import Callable |
| 25 | +from collections.abc import Callable, Iterator |
26 | 26 | from copy import copy |
| 27 | +from datetime import datetime, timezone |
27 | 28 | from functools import lru_cache |
28 | 29 | from typing import ( |
29 | 30 | TYPE_CHECKING, |
|
86 | 87 | S3_SIGNER_ENDPOINT_DEFAULT, |
87 | 88 | S3_SIGNER_URI, |
88 | 89 | S3_SSE_KMS_KEY_ID, |
| 90 | + FileEntry, |
89 | 91 | FileIO, |
90 | 92 | InputFile, |
91 | 93 | InputStream, |
@@ -491,6 +493,40 @@ def delete(self, location: str | InputFile | OutputFile) -> None: |
491 | 493 | fs = self._get_fs_from_uri(uri, str_location) |
492 | 494 | fs.rm(str_location) |
493 | 495 |
|
| 496 | + @override |
| 497 | + def list_prefix(self, location: str) -> Iterator[FileEntry]: |
| 498 | + """Recursively list every file under the given location. |
| 499 | +
|
| 500 | + Args: |
| 501 | + location (str): A URI or a path to recursively list. |
| 502 | +
|
| 503 | + Returns: |
| 504 | + Iterator[FileEntry]: The metadata of every file under the location. |
| 505 | + """ |
| 506 | + uri = urlparse(location) |
| 507 | + fs = self._get_fs_from_uri(uri, location) |
| 508 | + # On Windows a drive letter parses as a URI scheme, so local paths are reported as-is. |
| 509 | + scheme = "" if _is_local_path(location) else uri.scheme |
| 510 | + |
| 511 | + for path, info in fs.find(location, detail=True).items(): |
| 512 | + if info.get("type", "file") != "file": |
| 513 | + continue |
| 514 | + |
| 515 | + mtime = info.get("mtime") or info.get("LastModified") or info.get("last_modified") |
| 516 | + last_modified: datetime | None |
| 517 | + if isinstance(mtime, datetime): |
| 518 | + last_modified = mtime |
| 519 | + elif isinstance(mtime, (int, float)): |
| 520 | + last_modified = datetime.fromtimestamp(mtime, tz=timezone.utc) |
| 521 | + else: |
| 522 | + last_modified = None |
| 523 | + |
| 524 | + yield FileEntry( |
| 525 | + location=path if scheme in ("", "file") else f"{scheme}://{path}", |
| 526 | + size=int(info.get("size") or 0), |
| 527 | + last_modified=last_modified, |
| 528 | + ) |
| 529 | + |
494 | 530 | def _get_fs_from_uri(self, uri: "ParseResult", location: str = "") -> AbstractFileSystem: |
495 | 531 | """Get a filesystem from a parsed URI, using hostname for ADLS account resolution.""" |
496 | 532 | if _is_local_path(location): |
|
0 commit comments