Skip to content

Commit b37b287

Browse files
committed
review comments
1 parent a2af567 commit b37b287

File tree

2 files changed

+129
-46
lines changed

2 files changed

+129
-46
lines changed

singlestoredb/management/files.py

Lines changed: 57 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -424,13 +424,36 @@ def is_dir(self, path: PathLike) -> bool:
424424
def is_file(self, path: PathLike) -> bool:
425425
pass
426426

427+
@overload
428+
@abstractmethod
429+
def listdir(
430+
self,
431+
path: PathLike = '/',
432+
*,
433+
recursive: bool = False,
434+
return_objects: Literal[True],
435+
) -> List[FilesObject]:
436+
pass
437+
438+
@overload
427439
@abstractmethod
428440
def listdir(
429441
self,
430442
path: PathLike = '/',
431443
*,
432444
recursive: bool = False,
433-
) -> Union[List[str], List[Union[str, Dict[str, Any]]]]:
445+
return_objects: Literal[False] = False,
446+
) -> List[str]:
447+
pass
448+
449+
@abstractmethod
450+
def listdir(
451+
self,
452+
path: PathLike = '/',
453+
*,
454+
recursive: bool = False,
455+
return_objects: bool = False,
456+
) -> Union[List[str], List[FilesObject]]:
434457
pass
435458

436459
@abstractmethod
@@ -914,46 +937,44 @@ def is_file(self, path: PathLike) -> bool:
914937
def _listdir(
915938
self, path: PathLike, *,
916939
recursive: bool = False,
917-
return_meta: bool = False,
918-
) -> List[Union[str, Dict[str, Any]]]:
940+
return_objects: bool = False,
941+
) -> List[Union[str, FilesObject]]:
919942
"""
920-
Return the names (or metadata) of files in a directory.
943+
Return the names (or FilesObject instances) of files in a directory.
921944
922945
Parameters
923946
----------
924947
path : Path or str
925948
Path to the folder
926949
recursive : bool, optional
927950
Should folders be listed recursively?
928-
return_meta : bool, optional
929-
If True, return list of dicts with 'path' and 'type'. Otherwise just paths.
951+
return_objects : bool, optional
952+
If True, return list of FilesObject instances. Otherwise just paths.
930953
"""
931954
res = self._manager._get(
932955
f'files/fs/{self._location}/{path}',
933956
).json()
934957

935958
if recursive:
936-
out: List[Union[str, Dict[str, Any]]] = []
959+
out: List[Union[str, FilesObject]] = []
937960
for item in res.get('content') or []:
938-
if return_meta:
939-
out.append(
940-
{'path': item['path'], 'type': item['type']},
941-
)
961+
if return_objects:
962+
out.append(FilesObject.from_dict(item, self))
942963
else:
943964
out.append(item['path'])
944965
if item['type'] == 'directory':
945966
out.extend(
946967
self._listdir(
947968
item['path'],
948969
recursive=recursive,
949-
return_meta=return_meta,
970+
return_objects=return_objects,
950971
),
951972
)
952973
return out
953974

954-
if return_meta:
975+
if return_objects:
955976
return [
956-
{'path': x['path'], 'type': x['type']}
977+
FilesObject.from_dict(x, self)
957978
for x in (res.get('content') or [])
958979
]
959980
return [x['path'] for x in (res.get('content') or [])]
@@ -964,8 +985,8 @@ def listdir(
964985
path: PathLike = '/',
965986
*,
966987
recursive: bool = False,
967-
return_meta: Literal[True] = ...,
968-
) -> List[Dict[str, Any]]:
988+
return_objects: Literal[True],
989+
) -> List[FilesObject]:
969990
...
970991

971992
@overload
@@ -974,7 +995,7 @@ def listdir(
974995
path: PathLike = '/',
975996
*,
976997
recursive: bool = False,
977-
return_meta: Literal[False] = ...,
998+
return_objects: Literal[False] = False,
978999
) -> List[str]:
9791000
...
9801001

@@ -983,8 +1004,8 @@ def listdir(
9831004
path: PathLike = '/',
9841005
*,
9851006
recursive: bool = False,
986-
return_meta: bool = False,
987-
) -> Union[List[str], List[Dict[str, Any]]]:
1007+
return_objects: bool = False,
1008+
) -> Union[List[str], List[FilesObject]]:
9881009
"""
9891010
List the files / folders at the given path.
9901011
@@ -993,39 +1014,39 @@ def listdir(
9931014
path : Path or str, optional
9941015
Path to the file location
9951016
996-
return_meta : bool, optional
997-
If True, return list of dicts with 'path' and 'type'. Otherwise just paths.
1017+
return_objects : bool, optional
1018+
If True, return list of FilesObject instances. Otherwise just paths.
9981019
9991020
Returns
10001021
-------
1001-
List[str] or List[dict]
1022+
List[str] or List[FilesObject]
10021023
10031024
"""
10041025
path = re.sub(r'^(\./|/)+', r'', str(path))
10051026
path = re.sub(r'/+$', r'', path) + '/'
10061027

10071028
# Validate via listing GET; if response lacks 'content', it's not a directory
10081029
try:
1009-
out = self._listdir(path, recursive=recursive, return_meta=return_meta)
1030+
out = self._listdir(path, recursive=recursive, return_objects=return_objects)
10101031
except Exception as exc:
10111032
# If the path doesn't exist or isn't a directory, _listdir will fail
10121033
raise NotADirectoryError(f'path is not a directory: {path}') from exc
10131034

10141035
if path != '/':
10151036
path_n = len(path.split('/')) - 1
1016-
if return_meta:
1017-
result: List[Dict[str, Any]] = []
1037+
if return_objects:
1038+
result: List[FilesObject] = []
10181039
for item in out:
1019-
if isinstance(item, dict):
1020-
rel = '/'.join(item['path'].split('/')[path_n:])
1021-
item['path'] = rel
1040+
if isinstance(item, FilesObject):
1041+
rel = '/'.join(item.path.split('/')[path_n:])
1042+
item.path = rel
10221043
result.append(item)
10231044
return result
10241045
return ['/'.join(str(x).split('/')[path_n:]) for x in out]
10251046

1026-
# _listdir guarantees homogeneous type based on return_meta
1027-
if return_meta:
1028-
return cast(List[Dict[str, Any]], out)
1047+
# _listdir guarantees homogeneous type based on return_objects
1048+
if return_objects:
1049+
return cast(List[FilesObject], out)
10291050
return cast(List[str], out)
10301051

10311052
def download_file(
@@ -1139,13 +1160,13 @@ def download_folder(
11391160
raise OSError('target path already exists; use overwrite=True to replace')
11401161

11411162
# listdir validates directory; no extra info call needed
1142-
entries = self.listdir(path, recursive=True, return_meta=True)
1163+
entries = self.listdir(path, recursive=True, return_objects=True)
11431164
for entry in entries:
1144-
# Each entry is a dict with path relative to root and type
1145-
if not isinstance(entry, dict): # defensive: skip unexpected
1165+
# Each entry is a FilesObject with path relative to root and type
1166+
if not isinstance(entry, FilesObject): # defensive: skip unexpected
11461167
continue
1147-
rel_path = entry['path']
1148-
if entry['type'] == 'directory':
1168+
rel_path = entry.path
1169+
if entry.type == 'directory':
11491170
# Ensure local directory exists; no remote call needed
11501171
target_dir = os.path.normpath(os.path.join(local_path, rel_path))
11511172
os.makedirs(target_dir, exist_ok=True)

singlestoredb/management/workspace.py

Lines changed: 72 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
import time
1111
from collections.abc import Mapping
1212
from typing import Any
13+
from typing import cast
1314
from typing import Dict
1415
from typing import List
16+
from typing import Literal
1517
from typing import Optional
18+
from typing import overload
1619
from typing import Union
1720

1821
from .. import config
@@ -447,58 +450,117 @@ def is_file(self, stage_path: PathLike) -> bool:
447450
return False
448451
raise
449452

450-
def _listdir(self, stage_path: PathLike, *, recursive: bool = False) -> List[str]:
453+
def _listdir(
454+
self, stage_path: PathLike, *,
455+
recursive: bool = False,
456+
return_objects: bool = False,
457+
) -> List[Union[str, 'FilesObject']]:
451458
"""
452-
Return the names of files in a directory.
459+
Return the names (or FilesObject instances) of files in a directory.
453460
454461
Parameters
455462
----------
456463
stage_path : Path or str
457464
Path to the folder in Stage
458465
recursive : bool, optional
459466
Should folders be listed recursively?
467+
return_objects : bool, optional
468+
If True, return list of FilesObject instances. Otherwise just paths.
460469
461470
"""
471+
from .files import FilesObject
462472
res = self._manager._get(
463473
re.sub(r'/+$', r'/', f'stage/{self._deployment_id}/fs/{stage_path}'),
464474
).json()
465475
if recursive:
466-
out = []
476+
out: List[Union[str, FilesObject]] = []
467477
for item in res['content'] or []:
468-
out.append(item['path'])
478+
if return_objects:
479+
out.append(FilesObject.from_dict(item, self))
480+
else:
481+
out.append(item['path'])
469482
if item['type'] == 'directory':
470-
out.extend(self._listdir(item['path'], recursive=recursive))
483+
out.extend(
484+
self._listdir(
485+
item['path'],
486+
recursive=recursive,
487+
return_objects=return_objects,
488+
),
489+
)
471490
return out
491+
if return_objects:
492+
return [
493+
FilesObject.from_dict(x, self)
494+
for x in res['content'] or []
495+
]
472496
return [x['path'] for x in res['content'] or []]
473497

498+
@overload
499+
def listdir(
500+
self,
501+
stage_path: PathLike = '/',
502+
*,
503+
recursive: bool = False,
504+
return_objects: Literal[True],
505+
) -> List['FilesObject']:
506+
...
507+
508+
@overload
474509
def listdir(
475510
self,
476511
stage_path: PathLike = '/',
477512
*,
478513
recursive: bool = False,
514+
return_objects: Literal[False] = False,
479515
) -> List[str]:
516+
...
517+
518+
def listdir(
519+
self,
520+
stage_path: PathLike = '/',
521+
*,
522+
recursive: bool = False,
523+
return_objects: bool = False,
524+
) -> Union[List[str], List['FilesObject']]:
480525
"""
481526
List the files / folders at the given path.
482527
483528
Parameters
484529
----------
485530
stage_path : Path or str, optional
486531
Path to the stage location
532+
return_objects : bool, optional
533+
If True, return list of FilesObject instances. Otherwise just paths.
487534
488535
Returns
489536
-------
490-
List[str]
537+
List[str] or List[FilesObject]
491538
492539
"""
540+
from .files import FilesObject
493541
stage_path = re.sub(r'^(\./|/)+', r'', str(stage_path))
494542
stage_path = re.sub(r'/+$', r'', stage_path) + '/'
495543

496544
if self.is_dir(stage_path):
497-
out = self._listdir(stage_path, recursive=recursive)
545+
out = self._listdir(
546+
stage_path,
547+
recursive=recursive,
548+
return_objects=return_objects,
549+
)
498550
if stage_path != '/':
499551
stage_path_n = len(stage_path.split('/')) - 1
500-
out = ['/'.join(x.split('/')[stage_path_n:]) for x in out]
501-
return out
552+
if return_objects:
553+
result: List[FilesObject] = []
554+
for item in out:
555+
if isinstance(item, FilesObject):
556+
rel = '/'.join(item.path.split('/')[stage_path_n:])
557+
item.path = rel
558+
result.append(item)
559+
return result
560+
out = ['/'.join(str(x).split('/')[stage_path_n:]) for x in out]
561+
if return_objects:
562+
return cast(List[FilesObject], out)
563+
return cast(List[str], out)
502564

503565
raise NotADirectoryError(f'stage path is not a directory: {stage_path}')
504566

@@ -577,7 +639,7 @@ def download_folder(
577639
if not self.is_dir(stage_path):
578640
raise NotADirectoryError(f'stage path is not a directory: {stage_path}')
579641

580-
for f in self.listdir(stage_path, recursive=True):
642+
for f in self.listdir(stage_path, recursive=True, return_objects=False):
581643
if self.is_dir(f):
582644
continue
583645
target = os.path.normpath(os.path.join(local_path, f))

0 commit comments

Comments
 (0)