Skip to content

Commit 932ed19

Browse files
authored
consolidate handle handle_many and download download_many functions (#348)
1 parent 66830c6 commit 932ed19

7 files changed

Lines changed: 90 additions & 168 deletions

File tree

cterasdk/asynchronous/core/files/browser.py

Lines changed: 23 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from .. import query
2-
from ....cio.core.commands import Open, OpenMany, Upload, Download, EnsureDirectory, \
3-
DownloadMany, UnShare, CreateDirectory, GetMetadata, GetProperties, ListVersions, RecursiveIterator, \
2+
from ....cio.core.commands import Open, Upload, Download, EnsureDirectory, \
3+
UnShare, CreateDirectory, GetMetadata, GetProperties, ListVersions, RecursiveIterator, \
44
Delete, Recover, Rename, GetShareMetadata, Link, Copy, Move, ResourceIterator, GetPermalink, GetExternalShareInfo
55
from ....cio.core.types import InvitationPath
66
from ....lib.storage import commonfs
@@ -11,60 +11,39 @@
1111
class FileBrowser(BaseCommand):
1212
"""Async File Browser API."""
1313

14-
async def handle(self, path):
14+
async def handle(self, path, objects):
1515
"""
1616
Get a file handle.
1717
1818
:param str path: Path to a file.
19+
:param list[str],optional objects: Files and folders to include.
1920
:returns: File handle.
2021
:rtype: object
21-
:raises cterasdk.exceptions.io.core.OpenError: Raised on error to obtain file handle.
22+
:raises cterasdk.exceptions.io.core.OpenError: Raised on error to obtain a file handle.
23+
:raises cterasdk.exceptions.io.core.GetMetadataError: If the directory was not found.
24+
:raises cterasdk.exceptions.io.core.NotADirectoryException: If the target path is not a directory.
2225
"""
23-
return await Open(io.handle, self._core, path).a_execute()
26+
async with GetProperties(io.listdir, self._core, path) as properties:
27+
return await Open(io.handle_many if properties.is_dir else io.handle, self._core,
28+
properties.path, properties, objects).a_execute()
2429

25-
async def handle_many(self, directory, *objects):
26-
"""
27-
Get a ZIP archive file handle.
28-
29-
:param str directory: Path to a folder.
30-
:param args objects: List of files and folders to include.
31-
:returns: File handle.
32-
:rtype: object
33-
:raises cterasdk.exceptions.io.core.GetMetadataError: If directory not found.
34-
:raises cterasdk.exceptions.io.core.NotADirectoryException: If target path is not a directory.
35-
"""
36-
async with EnsureDirectory(io.listdir, self._core, directory) as (_, resource):
37-
return await OpenMany(io.handle_many, self._core, resource, directory, *objects).a_execute()
38-
39-
async def download(self, path, destination=None):
30+
async def download(self, path, objects=None, destination=None):
4031
"""
4132
Download a file.
4233
4334
:param str path: Path.
44-
:param str, optional destination: File destination. If directory, original filename preserved. Defaults to default directory.
45-
:returns: Path to local file.
35+
:param list[str],optional objects: List of files and / or directory names to download.
36+
:param str, optional destination: File destination. If a directory is provided, the original filename is preserved.
37+
Defaults to the default download directory.
38+
:returns: Path to the local file.
4639
:rtype: str
47-
:raises cterasdk.exceptions.io.core.OpenError: Raised on error to obtain file handle.
40+
:raises cterasdk.exceptions.io.core.OpenError: Raised on error to obtain a file handle.
41+
:raises cterasdk.exceptions.io.core.GetMetadataError: If the directory was not found.
42+
:raises cterasdk.exceptions.io.core.NotADirectoryException: If the target path is not a directory.
4843
"""
49-
return await Download(io.handle, self._core, path, destination).a_execute()
50-
51-
async def download_many(self, directory, objects, destination=None):
52-
"""
53-
Download selected files and/or directories as a ZIP archive.
54-
55-
.. warning::
56-
Only existing files and directories will be included in the resulting ZIP file.
57-
58-
:param str directory: Path to a folder.
59-
:param list[str] objects: List of files and / or directory names to download.
60-
:param str destination: Optional path to destination file or directory. Defaults to default download directory.
61-
:returns: Path to local file.
62-
:rtype: str
63-
:raises cterasdk.exceptions.io.core.GetMetadataError: If directory not found.
64-
:raises cterasdk.exceptions.io.core.NotADirectoryException: If target path is not a directory.
65-
"""
66-
async with EnsureDirectory(io.listdir, self._core, directory) as (_, resource):
67-
return await DownloadMany(io.handle_many, self._core, resource, directory, objects, destination).a_execute()
44+
async with GetProperties(io.listdir, self._core, path) as properties:
45+
return await Download(io.handle_many if properties.is_dir else io.handle, self._core,
46+
properties.path, properties, objects, destination).a_execute()
6847

6948
async def listdir(self, path=None, include_deleted=False):
7049
"""
@@ -383,11 +362,8 @@ async def mkdir(self, path):
383362
async def makedirs(self, path):
384363
return await self._file_browser.makedirs(self._invitation.join(path))
385364

386-
async def download(self, path, destination=None):
387-
return await self._file_browser.download(self._invitation.join(path), destination)
388-
389-
async def download_many(self, directory, objects, destination=None):
390-
return await self._file_browser.download_many(self._invitation.join(directory), objects, destination)
365+
async def download(self, path, objects=None, destination=None):
366+
return await self._file_browser.download(self._invitation.join(path), objects, destination)
391367

392368
async def upload(self, destination, handle, name=None, size=None):
393369
return await self._file_browser.upload(self._invitation.join(destination), handle, name, size)

cterasdk/cio/core/commands.py

Lines changed: 34 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -356,24 +356,42 @@ def obtain_current_accounts(collaborators):
356356
class Open(PortalCommand):
357357
"""Open file"""
358358

359-
def __init__(self, function, receiver, path):
359+
def __init__(self, function, receiver, path, properties, objects):
360360
super().__init__(function, receiver)
361361
self.path = automatic_resolution(path, receiver.context)
362+
self.properties = properties
363+
self.objects = objects or []
362364

363365
def get_parameter(self):
364-
return self.path.relative_encode
366+
if self.properties and self.properties.is_dir:
367+
param = Object()
368+
param.paths = [self.path.join(filename).absolute_encode for filename in self.objects] if self.objects else [self.path.absolute]
369+
param.snapshot = None
370+
param.password = None
371+
param.portalName = None
372+
param.showDeleted = False
373+
uid = (
374+
str(self.properties.volume.id)
375+
if self._receiver.context != Context.Invitations
376+
else f'share/{self._receiver.invite}'
377+
)
378+
return uid, encode_request_parameter(param)
379+
return self.path.relative_encode, # pylint: disable=trailing-comma-tuple
365380

366381
def _before_command(self):
367382
raise_or_suppress_access_error(self._receiver, self.path)
368-
logger.info('Getting handle: %s', self.path)
383+
if self.properties and self.properties.is_dir and self.objects:
384+
logger.info('Getting handle: %s', [self.path.join(o).relative for o in self.objects])
385+
else:
386+
logger.info('Getting handle: %s', self.path)
369387

370388
def _execute(self):
371389
with self.trace_execution():
372-
return self._function(self._receiver, self.get_parameter())
390+
return self._function(self._receiver, *self.get_parameter())
373391

374392
async def _a_execute(self):
375393
with self.trace_execution():
376-
return await self._function(self._receiver, self.get_parameter())
394+
return await self._function(self._receiver, *self.get_parameter())
377395

378396
def _handle_exception(self, e):
379397
path = self.path.relative
@@ -385,86 +403,34 @@ def _handle_exception(self, e):
385403

386404
class Download(PortalCommand):
387405

388-
def __init__(self, function, receiver, path, destination):
406+
def __init__(self, function, receiver, path, properties=None, objects=None, destination=None):
389407
super().__init__(function, receiver)
390408
self.path = automatic_resolution(path, receiver.context)
391-
self.destination = destination
392-
393-
def get_parameter(self):
394-
return commonfs.determine_directory_and_filename(self.path.reference, destination=self.destination)
395-
396-
def _before_command(self):
397-
logger.info('Downloading: %s', self.path)
398-
399-
def _execute(self):
400-
directory, name = self.get_parameter()
401-
with self.trace_execution():
402-
with Open(self._function, self._receiver, self.path) as handle:
403-
return synfs.write(directory, name, handle)
404-
405-
async def _a_execute(self):
406-
directory, name = self.get_parameter()
407-
with self.trace_execution():
408-
async with Open(self._function, self._receiver, self.path) as handle:
409-
return await asynfs.write(directory, name, handle)
410-
411-
412-
class OpenMany(PortalCommand):
413-
414-
def __init__(self, function, receiver, resource, directory, *objects):
415-
super().__init__(function, receiver)
416-
self.uid = str(resource.cloudFolderInfo.uid) if receiver.context != Context.Invitations else f'share/{receiver.invite}'
417-
self.directory = automatic_resolution(directory, receiver.context)
418-
self.objects = objects
419-
420-
def _before_command(self):
421-
raise_or_suppress_access_error(self._receiver, self.directory)
422-
logger.info('Getting handle: %s', [self.directory.join(o).relative for o in self.objects])
423-
424-
def get_parameter(self):
425-
param = Object()
426-
param.paths = [self.directory.join(filename).absolute_encode for filename in self.objects]
427-
param.snapshot = None
428-
param.password = None
429-
param.portalName = None
430-
param.showDeleted = False
431-
return encode_request_parameter(param)
432-
433-
def _execute(self):
434-
with self.trace_execution():
435-
return self._function(self._receiver, self.uid, self.get_parameter())
436-
437-
async def _a_execute(self):
438-
with self.trace_execution():
439-
return await self._function(self._receiver, self.uid, self.get_parameter())
440-
441-
442-
class DownloadMany(PortalCommand):
443-
444-
def __init__(self, function, receiver, resource, directory, objects, destination):
445-
super().__init__(function, receiver)
446-
self.resource = resource
447-
self.directory = automatic_resolution(directory, receiver.context)
409+
self.properties = properties
448410
self.objects = objects
449411
self.destination = destination
450412

451413
def get_parameter(self):
452-
return commonfs.determine_directory_and_filename(self.directory.reference, self.objects, destination=self.destination, archive=True)
414+
archive = self.properties.is_dir if self.properties else False
415+
return commonfs.determine_directory_and_filename(self.path.reference, self.objects,
416+
self.destination, archive)
453417

454418
def _before_command(self):
455-
for o in self.objects:
456-
logger.info('Downloading: %s', self.directory.join(o).relative)
419+
if self.properties and self.properties.is_dir and self.objects:
420+
logger.info('Downloading: %s', [self.path.join(o).relative for o in self.objects])
421+
else:
422+
logger.info('Downloading: %s', self.path)
457423

458424
def _execute(self):
459425
directory, name = self.get_parameter()
460426
with self.trace_execution():
461-
with OpenMany(self._function, self._receiver, self.resource, self.directory, *self.objects) as handle:
427+
with Open(self._function, self._receiver, self.path, self.properties, self.objects) as handle:
462428
return synfs.write(directory, name, handle)
463429

464430
async def _a_execute(self):
465431
directory, name = self.get_parameter()
466432
with self.trace_execution():
467-
async with OpenMany(self._function, self._receiver, self.resource, self.directory, *self.objects) as handle:
433+
async with Open(self._function, self._receiver, self.path, self.properties, self.objects) as handle:
468434
return await asynfs.write(directory, name, handle)
469435

470436

cterasdk/cli/dav.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ async def handle_download(args): # pylint: disable=too-many-branches
5353

5454
async def download(invitation, resource, objects=None, archive=False, destination=None):
5555
if objects is not None or archive:
56-
return await invitation.files.download_many(resource.path.relative, [o.name for o in objects], f'{destination}.zip')
57-
return await invitation.files.download(resource.path.relative, destination.joinpath(resource.path.relative))
56+
return await invitation.files.download(resource.path.relative, [o.name for o in objects], f'{destination}.zip')
57+
return await invitation.files.download(resource.path.relative, destination=destination.joinpath(resource.path.relative))
5858

5959
async with AsyncInvitation.from_uri(args.endpoint) as invitation:
6060
jobs = []

cterasdk/core/files/browser.py

Lines changed: 16 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from .. import query
2-
from ...cio.core.commands import Open, OpenMany, Upload, Download, EnsureDirectory, \
3-
DownloadMany, UnShare, CreateDirectory, GetMetadata, GetProperties, ListVersions, RecursiveIterator, \
2+
from ...cio.core.commands import Open, Upload, Download, EnsureDirectory, \
3+
UnShare, CreateDirectory, GetMetadata, GetProperties, ListVersions, RecursiveIterator, \
44
Delete, Recover, Rename, GetShareMetadata, Link, Copy, Move, ResourceIterator, GetPermalink, GetExternalShareInfo
55
from ...cio.core.types import InvitationPath
66
from ...lib.storage import commonfs
@@ -11,61 +11,39 @@
1111
class FileBrowser(BaseCommand):
1212
"""CTERA Portal File Browser API."""
1313

14-
def handle(self, path):
14+
def handle(self, path, objects=None):
1515
"""
1616
Get a file handle.
1717
1818
:param str path: Path to a file.
19+
:param list[str],optional objects: Files and folders to include.
1920
:returns: File handle.
2021
:rtype: object
2122
:raises cterasdk.exceptions.io.core.OpenError: Raised on error to obtain a file handle.
22-
"""
23-
return Open(io.handle, self._core, path).execute()
24-
25-
def handle_many(self, directory, *objects):
26-
"""
27-
Get a ZIP archive file handle.
28-
29-
:param str directory: Path to a folder.
30-
:param args objects: Files and folders to include.
31-
:returns: File handle.
32-
:rtype: object
3323
:raises cterasdk.exceptions.io.core.GetMetadataError: If the directory was not found.
3424
:raises cterasdk.exceptions.io.core.NotADirectoryException: If the target path is not a directory.
3525
"""
36-
with EnsureDirectory(io.listdir, self._core, directory) as (_, resource):
37-
return OpenMany(io.handle_many, self._core, resource, directory, *objects).execute()
26+
with GetProperties(io.listdir, self._core, path) as properties:
27+
return Open(io.handle_many if properties.is_dir else io.handle, self._core,
28+
properties.path, properties, objects).execute()
3829

39-
def download(self, path, destination=None):
30+
def download(self, path, objects=None, destination=None):
4031
"""
4132
Download a file.
4233
4334
:param str path: Path.
35+
:param list[str],optional objects: List of files and / or directory names to download.
4436
:param str, optional destination: File destination. If a directory is provided, the original filename is preserved.
4537
Defaults to the default download directory.
4638
:returns: Path to the local file.
4739
:rtype: str
4840
:raises cterasdk.exceptions.io.core.OpenError: Raised on error to obtain a file handle.
49-
"""
50-
return Download(io.handle, self._core, path, destination).execute()
51-
52-
def download_many(self, directory, objects, destination=None):
53-
"""
54-
Download selected files and/or directories as a ZIP archive.
55-
56-
.. warning::
57-
Only existing files and directories will be included in the resulting ZIP file.
58-
59-
:param str directory: Path to a folder.
60-
:param list[str] objects: List of files and / or directory names to download.
61-
:param str destination: Optional path to destination file or directory. Defaults to the default download directory.
62-
:returns: Path to the local file.
63-
:rtype: str
6441
:raises cterasdk.exceptions.io.core.GetMetadataError: If the directory was not found.
6542
:raises cterasdk.exceptions.io.core.NotADirectoryException: If the target path is not a directory.
6643
"""
67-
with EnsureDirectory(io.listdir, self._core, directory) as (_, resource):
68-
return DownloadMany(io.handle_many, self._core, resource, directory, objects, destination).execute()
44+
with GetProperties(io.listdir, self._core, path) as properties:
45+
return Download(io.handle_many if properties.is_dir else io.handle, self._core,
46+
properties.path, properties, objects, destination).execute()
6947

7048
def listdir(self, path=None, include_deleted=False):
7149
"""
@@ -352,7 +330,8 @@ def device_config(self, device, destination=None):
352330
:raises cterasdk.exceptions.io.core.OpenError: Raised on error obtaining file handle.
353331
"""
354332
destination = destination if destination is not None else f'{commonfs.downloads()}/{device}.xml'
355-
return Download(io.handle, self._core, f'backups/{device}/Device Configuration/db.xml', destination).execute()
333+
return Download(io.handle, self._core,
334+
f'backups/{device}/Device Configuration/db.xml', destination=destination).execute()
356335

357336

358337
class InvitationBrowser:
@@ -380,11 +359,8 @@ def mkdir(self, path):
380359
def makedirs(self, path):
381360
return self._file_browser.makedirs(self._invitation.join(path))
382361

383-
def download(self, path, destination=None):
384-
return self._file_browser.download(self._invitation.join(path), destination)
385-
386-
def download_many(self, directory, objects, destination=None):
387-
return self._file_browser.download_many(self._invitation.join(directory), objects, destination)
362+
def download(self, path, objects=None, destination=None):
363+
return self._file_browser.download(self._invitation.join(path), objects, destination)
388364

389365
def upload(self, destination, handle, name=None, size=None):
390366
return self._file_browser.upload(self._invitation.join(destination), handle, name, size)

cterasdk/lib/storage/commonfs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def determine_zip_archive_name(directory, objects):
9696
9797
:rtype: str
9898
"""
99-
if len(objects) > 1:
99+
if not objects or len(objects) > 1:
100100
path = Path(directory)
101101
else:
102102
path = Path(objects[0])

0 commit comments

Comments
 (0)