Skip to content

Commit 70cc7b9

Browse files
authored
Saimon/ctera direct refactor (#315)
1 parent ed15a3e commit 70cc7b9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+1156
-727
lines changed

cterasdk/asynchronous/core/files/browser.py

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,18 @@ async def public_link(self, path, access='RO', expire_in=30):
105105
"""
106106
return await io.public_link(self._core, self.normalize(path), access, expire_in)
107107

108-
async def copy(self, *paths, destination=None):
108+
async def copy(self, *paths, destination=None, wait=False):
109109
"""
110110
Copy one or more files or folders
111111
112112
:param list[str] paths: List of paths
113113
:param str destination: Destination
114+
:param bool,optional wait: ``True`` Wait for task to complete, or ``False`` to return an awaitable task object.
115+
:returns: Task status object, or an awaitable task object
116+
:rtype: cterasdk.common.object.Object or :class:`cterasdk.lib.tasks.AwaitablePortalTask`
114117
"""
115118
try:
116-
return await io.copy(self._core, *[self.normalize(path) for path in paths], destination=self.normalize(destination))
119+
return await io.copy(self._core, *[self.normalize(path) for path in paths], destination=self.normalize(destination), wait=wait)
117120
except ValueError:
118121
raise ValueError('Copy destination was not specified.')
119122

@@ -175,39 +178,51 @@ async def makedirs(self, path):
175178
"""
176179
return await io.makedirs(self._core, self.normalize(path))
177180

178-
async def rename(self, path, name):
181+
async def rename(self, path, name, *, wait=False):
179182
"""
180183
Rename a file
181184
182185
:param str path: Path of the file or directory to rename
183186
:param str name: The name to rename to
187+
:param bool,optional wait: ``True`` Wait for task to complete, or ``False`` to return an awaitable task object.
188+
:returns: Task status object, or an awaitable task object
189+
:rtype: cterasdk.common.object.Object or :class:`cterasdk.lib.tasks.AwaitablePortalTask`
184190
"""
185-
return await io.rename(self._core, self.normalize(path), name)
191+
return await io.rename(self._core, self.normalize(path), name, wait=wait)
186192

187-
async def delete(self, *paths):
193+
async def delete(self, *paths, wait=False):
188194
"""
189195
Delete one or more files or folders
190196
191197
:param str path: Path
198+
:param bool,optional wait: ``True`` Wait for task to complete, or ``False`` to return an awaitable task object.
199+
:returns: Task status object, or an awaitable task object
200+
:rtype: cterasdk.common.object.Object or :class:`cterasdk.lib.tasks.AwaitablePortalTask`
192201
"""
193-
return await io.remove(self._core, *[self.normalize(path) for path in paths])
202+
return await io.remove(self._core, *[self.normalize(path) for path in paths], wait=wait)
194203

195-
async def undelete(self, *paths):
204+
async def undelete(self, *paths, wait=False):
196205
"""
197206
Recover one or more files or folders
198207
199208
:param str path: Path
209+
:param bool,optional wait: ``True`` Wait for task to complete, or ``False`` to return an awaitable task object.
210+
:returns: Task status object, or an awaitable task object
211+
:rtype: cterasdk.common.object.Object or :class:`cterasdk.lib.tasks.AwaitablePortalTask`
200212
"""
201-
return await io.recover(self._core, *[self.normalize(path) for path in paths])
213+
return await io.recover(self._core, *[self.normalize(path) for path in paths], wait=wait)
202214

203-
async def move(self, *paths, destination=None):
215+
async def move(self, *paths, destination=None, wait=False):
204216
"""
205217
Move one or more files or folders
206218
207219
:param list[str] paths: List of paths
208220
:param str destination: Destination
221+
:param bool,optional wait: ``True`` Wait for task to complete, or ``False`` to return an awaitable task object.
222+
:returns: Task status object, or an awaitable task object
223+
:rtype: cterasdk.common.object.Object or :class:`cterasdk.lib.tasks.AwaitablePortalTask`
209224
"""
210225
try:
211-
return await io.move(self._core, *[self.normalize(path) for path in paths], destination=self.normalize(destination))
226+
return await io.move(self._core, *[self.normalize(path) for path in paths], destination=self.normalize(destination), wait=wait)
212227
except ValueError:
213228
raise ValueError('Move destination was not specified.')

cterasdk/asynchronous/core/files/io.py

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import logging
2-
from ....cio.common import encode_request_parameter
2+
from ....cio.common import encode_request_parameter, a_await_or_future
33
from ....cio import core as fs
44
from ....exceptions.io import ResourceNotFoundError, NotADirectory, ResourceExistsError
55
from .. import query
6-
from ....lib import FetchResourcesResponse
76

87

98
logger = logging.getLogger('cterasdk.core')
@@ -12,7 +11,7 @@
1211
async def listdir(core, path, depth=None, include_deleted=False, search_criteria=None, limit=None):
1312
with fs.fetch_resources(path, depth, include_deleted, search_criteria, limit) as param:
1413
if param.depth > 0:
15-
return query.iterator(core, '', param, 'fetchResources', callback_response=FetchResourcesResponse)
14+
return query.iterator(core, '', param, 'fetchResources', callback_response=fs.FetchResourcesResponse)
1615
return await core.v1.api.execute('', 'fetchResources', param)
1716

1817

@@ -54,7 +53,7 @@ async def walk(core, scope, path, include_deleted=False):
5453
async def mkdir(core, path):
5554
with fs.makedir(path) as param:
5655
response = await core.v1.api.execute('', 'makeCollection', param)
57-
fs.accept_response(response, path.reference.as_posix())
56+
fs.accept_response(response)
5857

5958

6059
async def makedirs(core, path):
@@ -67,29 +66,34 @@ async def makedirs(core, path):
6766
logger.debug('Resource already exists: %s', path.reference.as_posix())
6867

6968

70-
async def rename(core, path, name):
69+
async def rename(core, path, name, *, wait=False):
7170
with fs.rename(path, name) as param:
72-
return await core.v1.api.execute('', 'moveResources', param)
71+
ref = await core.v1.api.execute('', 'moveResources', param)
72+
return await a_await_or_future(core, ref, wait)
7373

7474

75-
async def remove(core, *paths):
75+
async def remove(core, *paths, wait=False):
7676
with fs.delete(*paths) as param:
77-
return await core.v1.api.execute('', 'deleteResources', param)
77+
ref = await core.v1.api.execute('', 'deleteResources', param)
78+
return await a_await_or_future(core, ref, wait)
7879

7980

80-
async def recover(core, *paths):
81+
async def recover(core, *paths, wait=False):
8182
with fs.recover(*paths) as param:
82-
return await core.v1.api.execute('', 'restoreResources', param)
83+
ref = await core.v1.api.execute('', 'restoreResources', param)
84+
return await a_await_or_future(core, ref, wait)
8385

8486

85-
async def copy(core, *paths, destination=None):
87+
async def copy(core, *paths, destination=None, wait=False):
8688
with fs.copy(*paths, destination=destination) as param:
87-
return await core.v1.api.execute('', 'copyResources', param)
89+
ref = await core.v1.api.execute('', 'copyResources', param)
90+
return await a_await_or_future(core, ref, wait)
8891

8992

90-
async def move(core, *paths, destination=None):
93+
async def move(core, *paths, destination=None, wait=False):
9194
with fs.move(*paths, destination=destination) as param:
92-
return await core.v1.api.execute('', 'moveResources', param)
95+
ref = await core.v1.api.execute('', 'moveResources', param)
96+
return await a_await_or_future(core, ref, wait)
9397

9498

9599
async def ensure_directory(core, directory, suppress_error=False):
@@ -159,6 +163,8 @@ def upload(name, size, destination, fd):
159163
:returns: Callable function to start the upload.
160164
:rtype: callable
161165
"""
166+
fs.destination_prerequisite_conditions(destination, name)
167+
162168
async def wrapper(core):
163169
"""
164170
Upload file from metadata and file handle.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import logging
2+
from ...lib.tasks import AwaitablePortalTask
3+
from .base_command import BaseCommand
4+
5+
6+
logger = logging.getLogger('cterasdk.core')
7+
8+
9+
class Tasks(BaseCommand):
10+
""" Portal Background Task APIs """
11+
12+
def awaitable_task(self, ref):
13+
return AwaitablePortalTask(self._core, ref)
14+
15+
async def wait(self, ref, timeout=None, poll_interval=None):
16+
awaitable_task = AwaitablePortalTask(self._core, ref)
17+
return await awaitable_task.a_wait(timeout, poll_interval)

cterasdk/cio/common.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,27 @@ def encode_request_parameter(param):
7777
return dict(
7878
inputXML=utf8_decode(toxmlstr(param))
7979
)
80+
81+
82+
def await_or_future(ctera, ref, wait):
83+
"""
84+
Wait for task completion, or return an awaitable task object.
85+
86+
:param str ref: Task reference
87+
:param bool wait: ``True`` to wait for task completion, ``False`` to return an awaitable task object
88+
"""
89+
if wait:
90+
return ctera.tasks.wait(ref)
91+
return ctera.tasks.awaitable_task(ref)
92+
93+
94+
async def a_await_or_future(ctera, ref, wait):
95+
"""
96+
Wait for task completion, or return an awaitable task object.
97+
98+
:param str ref: Task reference
99+
:param bool wait: ``True`` to wait for task completion, ``False`` to return an awaitable task object
100+
"""
101+
if wait:
102+
return await ctera.tasks.wait(ref)
103+
return ctera.tasks.awaitable_task(ref)

cterasdk/cio/core.py

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
from contextlib import contextmanager
44
from ..objects.uri import quote, unquote
55
from ..common import Object, DateTimeUtils
6-
from ..core.enum import ProtectionLevel, CollaboratorType, SearchType, PortalAccountType, FileAccessMode
6+
from ..core.enum import ProtectionLevel, CollaboratorType, SearchType, PortalAccountType, FileAccessMode, FileAccessError
77
from ..core.types import PortalAccount, UserAccount, GroupAccount
8-
from ..exceptions.io import ResourceExistsError, PathValidationError, NameSyntaxError, ReservedNameError
8+
from ..exceptions.io import ResourceExistsError, PathValidationError, NameSyntaxError, ReservedNameError, RestrictedRoot
9+
from ..lib.iterator import DefaultResponse
910
from . import common
1011

1112

@@ -177,6 +178,17 @@ def build(self):
177178
return self.param
178179

179180

181+
class FetchResourcesResponse(DefaultResponse):
182+
183+
def __init__(self, response):
184+
accept_response(response.errorType)
185+
super().__init__(response)
186+
187+
@property
188+
def objects(self):
189+
return self._response.items
190+
191+
180192
@contextmanager
181193
def fetch_resources(path, depth, include_deleted, search_criteria, limit):
182194
"""
@@ -278,6 +290,13 @@ def handle(path):
278290
yield path.reference
279291

280292

293+
def destination_prerequisite_conditions(destination, name):
294+
if not destination.reference.root:
295+
raise RestrictedRoot()
296+
if any(c in name for c in ['\\', '/', ':', '?', '&', '<', '>', '"', '|']):
297+
raise NameSyntaxError()
298+
299+
281300
@contextmanager
282301
def upload(core, name, destination, size, fd):
283302
fd, size = common.encode_stream(fd, size)
@@ -489,28 +508,28 @@ def obtain_current_accounts(param):
489508
return current_accounts
490509

491510

492-
def accept_response(response, reference):
511+
def accept_response(error_type):
493512
"""
494513
Check if response contains an error.
495514
"""
496515
error = {
497-
"FileWithTheSameNameExist": ResourceExistsError(),
498-
"DestinationNotExists": PathValidationError(),
499-
"InvalidName": NameSyntaxError(),
500-
"ReservedName": ReservedNameError()
501-
}.get(response, None)
516+
FileAccessError.FileWithTheSameNameExist: ResourceExistsError(),
517+
FileAccessError.DestinationNotExists: PathValidationError(),
518+
FileAccessError.InvalidName: NameSyntaxError(),
519+
FileAccessError.ReservedName: ReservedNameError()
520+
}.get(error_type, None)
502521
try:
503522
if error:
504523
raise error
505524
except ResourceExistsError as error:
506-
logger.info('Resource already exists: a file or folder with this name already exists. %s', {'path': reference})
525+
logger.info('Resource already exists: a file or folder with this name already exists.')
507526
raise error
508527
except PathValidationError as error:
509-
logger.error('Path validation failed: the specified destination path does not exist. %s', {'path': reference})
528+
logger.error('Path validation failed: the specified destination path does not exist.')
510529
raise error
511530
except NameSyntaxError as error:
512-
logger.error('Invalid name: the name contains characters that are not allowed. %s', {'name': reference})
531+
logger.error('Invalid name: the name contains characters that are not allowed.')
513532
raise error
514533
except ReservedNameError as error:
515-
logger.error('Reserved name error: the name is reserved and cannot be used. %s', {'name': reference})
534+
logger.error('Reserved name error: the name is reserved and cannot be used.')
516535
raise error

cterasdk/common/enum.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,28 @@ class ScheduleType:
9696
Monthly = 'monthly'
9797
Interval = 'interval'
9898
Window = 'window'
99+
100+
101+
class TaskRunningStatus:
102+
"""
103+
Task Status
104+
105+
:ivar str Disabled: Task is disabled.
106+
:ivar str Running: Task is currently running.
107+
:ivar str Completed: Task completed successfully.
108+
:ivar str Failed: Task failed.
109+
:ivar str Retrying: Task is retrying after a failure.
110+
:ivar str Waiting: Task is waiting to be run.
111+
:ivar str Stopping: Task is in the process of stopping.
112+
:ivar str Stopped: Task has been stopped.
113+
:ivar str Warnings: Task completed with warnings.
114+
"""
115+
Disabled = 'disabled'
116+
Running = 'running'
117+
Completed = 'completed'
118+
Failed = 'failed'
119+
Retrying = 'retrying'
120+
Waiting = 'waiting'
121+
Stopping = 'stopping'
122+
Stopped = 'stopped'
123+
Warnings = 'completed with warnings'

0 commit comments

Comments
 (0)