Skip to content

Commit bdfd2f2

Browse files
authored
Saimon/improve exception mechanism (#300)
1 parent df48d1d commit bdfd2f2

File tree

168 files changed

+1776
-1164
lines changed

Some content is hidden

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

168 files changed

+1776
-1164
lines changed

cterasdk/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# pylint: disable=wrong-import-position
22
import cterasdk.settings # noqa: E402, F401
3+
import cterasdk.exceptions # noqa: E402, F401
34

45
from .common import Object, PolicyRule # noqa: E402, F401
56
from .convert import fromjsonstr, tojsonstr, fromxmlstr, toxmlstr # noqa: E402, F401
6-
from .exceptions import CTERAException # noqa: E402, F401
77
from .core import query # noqa: E402, F401
88
from .edge import types as edge_types # noqa: E402, F401
99
from .edge import enum as edge_enum # noqa: E402, F401

cterasdk/asynchronous/core/files/browser.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,11 @@ async def versions(self, path):
8686
"""
8787
return await io.versions(self._core, self.normalize(path))
8888

89-
async def walk(self, path, include_deleted=False):
89+
async def walk(self, path=None, include_deleted=False):
9090
"""
9191
Walk Directory Contents
9292
93-
:param str path: Path to walk
93+
:param str,optional path: Path to walk, defaults to the root directory
9494
:param bool,optional include_deleted: Include deleted files, defaults to False
9595
"""
9696
return io.walk(self._core, self._scope, path, include_deleted=include_deleted)
@@ -134,14 +134,14 @@ def normalize(self, entries):
134134

135135
class CloudDrive(FileBrowser):
136136

137-
async def upload(self, name, size, destination, handle):
137+
async def upload(self, name, destination, handle, size=None):
138138
"""
139139
Upload from file handle.
140140
141141
:param str name: File name.
142-
:param str size: File size.
143142
:param str destination: Path to remote directory.
144143
:param object handle: Handle.
144+
:param str,optional size: File size, defaults to content length
145145
"""
146146
upload_function = io.upload(name, size, self.normalize(destination), handle)
147147
return await upload_function(self._core)
@@ -155,7 +155,7 @@ async def upload_file(self, path, destination):
155155
"""
156156
with open(path, 'rb') as handle:
157157
metadata = commonfs.properties(path)
158-
response = await self.upload(metadata['name'], metadata['size'], destination, handle)
158+
response = await self.upload(metadata['name'], destination, handle, metadata['size'])
159159
return response
160160

161161
async def mkdir(self, path):

cterasdk/asynchronous/core/files/io.py

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import logging
22
from ....cio.common import encode_request_parameter
33
from ....cio import core as fs
4-
from ....cio import exceptions
4+
from ....exceptions.io import ResourceNotFoundError, NotADirectory, ResourceExistsError
55
from .. import query
66
from ....lib import FetchResourcesResponse
77

@@ -17,18 +17,22 @@ async def listdir(core, path, depth=None, include_deleted=False, search_criteria
1717

1818

1919
async def exists(core, path):
20-
try:
21-
await metadata(core, path)
22-
return True
23-
except exceptions.ResourceNotFoundError:
24-
return False
20+
present, *_ = await metadata(core, path, suppress_error=True)
21+
return present
2522

2623

27-
async def metadata(core, path):
24+
async def metadata(core, path, suppress_error=False):
25+
"""
26+
Get item metadata.
27+
28+
:returns: A tuple indicating if a file exists, and its metadata
29+
"""
2830
response = await listdir(core, path, 0)
2931
if response.root is None:
30-
raise exceptions.ResourceNotFoundError(path.absolute)
31-
return response.root
32+
if not suppress_error:
33+
raise ResourceNotFoundError(path.absolute)
34+
return False, None
35+
return True, response.root
3236

3337

3438
async def versions(core, path):
@@ -59,7 +63,7 @@ async def makedirs(core, path):
5963
path = fs.CorePath.instance(path.scope, '/'.join(directories[:i]))
6064
try:
6165
await mkdir(core, path)
62-
except exceptions.ResourceExistsError:
66+
except ResourceExistsError:
6367
logger.debug('Resource already exists: %s', path.reference.as_posix())
6468

6569

@@ -88,11 +92,11 @@ async def move(core, *paths, destination=None):
8892
return await core.v1.api.execute('', 'moveResources', param)
8993

9094

91-
async def retrieve_remote_dir(core, directory):
92-
resource = await metadata(core, directory)
93-
if not resource.isFolder:
94-
raise exceptions.RemoteStorageException('The destination path is not a directory', None, path=directory.absolute)
95-
return str(resource.cloudFolderInfo.uid)
95+
async def ensure_directory(core, directory, suppress_error=False):
96+
present, resource = await metadata(core, directory, suppress_error=True)
97+
if (not present or not resource.isFolder) and not suppress_error:
98+
raise NotADirectory(directory.absolute)
99+
return resource.isFolder if present else False, resource
96100

97101

98102
def handle(path):
@@ -132,10 +136,19 @@ async def wrapper(core):
132136
:param object handle: File handle.
133137
"""
134138
with fs.handle_many(directory, objects) as param:
135-
return await core.io.download_zip(await retrieve_remote_dir(core, directory), encode_request_parameter(param))
139+
_, resource = await ensure_directory(core, directory)
140+
return await core.io.download_zip(str(resource.cloudFolderInfo.uid), encode_request_parameter(param))
136141
return wrapper
137142

138143

144+
async def _validate_destination(core, name, destination):
145+
is_dir, resource = await ensure_directory(core, destination, suppress_error=True)
146+
if not is_dir:
147+
is_dir, resource = await ensure_directory(core, destination.parent)
148+
return resource.cloudFolderInfo.uid, destination.name, destination.parent
149+
return resource.cloudFolderInfo.uid, name, destination
150+
151+
139152
def upload(name, size, destination, fd):
140153
"""
141154
Create upload function
@@ -150,11 +163,11 @@ async def wrapper(core):
150163
"""
151164
Upload file from metadata and file handle.
152165
153-
:param cterasdk.objects.synchronous.core.Portal core: POrtal object.
166+
:param cterasdk.objects.synchronous.core.Portal core: Portal object.
154167
"""
155-
target = await retrieve_remote_dir(core, destination)
156-
with fs.upload(core, name, destination, size, fd) as param:
157-
return await core.io.upload(target, param)
168+
uid, filename, directory = await _validate_destination(core, name, destination)
169+
with fs.upload(core, filename, directory, size, fd) as param:
170+
return await core.io.upload(str(uid), param)
158171
return wrapper
159172

160173

cterasdk/asynchronous/core/iterator.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
from abc import abstractmethod
44

55

6+
logger = logging.getLogger('cterasdk.common')
7+
8+
69
class BaseAsyncIterator:
710
"""Abstract Asynchronous Iterator"""
811

@@ -23,7 +26,7 @@ async def __anext__(self):
2326
self._objects.extend(page)
2427
if self._objects:
2528
return self.object
26-
logging.getLogger('cterasdk.common').debug('Stopping iteration.')
29+
logger.debug('Stopping iteration.')
2730
raise StopAsyncIteration
2831

2932
@property

cterasdk/asynchronous/core/notifications.py

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
from .base_command import BaseCommand
77
from ...common import Object
88
from ...lib import CursorResponse
9-
from ...exceptions import HTTPError, NotificationsError
9+
from ...exceptions.transport import HTTPError
10+
from ...exceptions.notifications import NotificationsError
11+
12+
13+
logger = logging.getLogger('cterasdk.notifications')
1014

1115

1216
class Notifications(BaseCommand):
@@ -30,11 +34,11 @@ async def get(self, cloudfolders=None, cursor=None, max_results=None):
3034
"""
3135
param = await self._create_parameter(cloudfolders, cursor)
3236
param.max_results = max_results if max_results is not None else 2000
33-
logging.getLogger('cterasdk.metadata.connector').debug('Listing updates.')
37+
logger.debug('Listing updates.')
3438
response = await self._core.v2.api.post('/metadata/list', param)
3539
if response is not None:
3640
return CursorResponse(response)
37-
logging.getLogger('cterasdk.metadata.connector').error('An error occurred while trying to retrieve notifications.')
41+
logger.error('An error occurred while trying to retrieve notifications.')
3842
raise NotificationsError(cloudfolders, cursor)
3943

4044
async def _create_parameter(self, cloudfolders, cursor):
@@ -64,7 +68,7 @@ async def changes(self, cursor, cloudfolders=None, timeout=None):
6468
param = Object()
6569
param = await self._create_parameter(cloudfolders, cursor)
6670
param.timeout = timeout if timeout else 10000
67-
logging.getLogger('cterasdk.metadata.connector').debug('Checking for updates. %s', {'timeout': param.timeout})
71+
logger.debug('Checking for updates. %s', {'timeout': param.timeout})
6872
return (await self._core.v2.api.post('/metadata/longpoll', param)).changes
6973

7074
async def ancestors(self, descendant):
@@ -78,12 +82,11 @@ async def ancestors(self, descendant):
7882
param = Object()
7983
param.folder_id = descendant.folder_id
8084
param.guid = descendant.guid
81-
logging.getLogger('cterasdk.metadata.connector').debug('Getting ancestors. %s', {'guid': param.guid, 'folder_id': param.folder_id})
85+
logger.debug('Getting ancestors. %s', {'guid': param.guid, 'folder_id': param.folder_id})
8286
try:
8387
return await self._core.v2.api.post('/metadata/ancestors', param)
8488
except HTTPError:
85-
logging.getLogger('cterasdk.metadata.connector').error('Could not retrieve ancestors. %s',
86-
{'folder_id': param.folder_id, 'guid': param.guid})
89+
logger.error('Could not retrieve ancestors. %s', {'folder_id': param.folder_id, 'guid': param.guid})
8790
raise
8891

8992

@@ -128,7 +131,7 @@ async def retrieve_events(server_queue, core, cloudfolders, cursor):
128131
:param list[CloudFSFolderFindingHelper] cloudfolders: List of Cloud Drive folders.
129132
:param str cursor: Cursor
130133
"""
131-
logging.getLogger('cterasdk.metadata.connector').debug('Event Retrieval Service.')
134+
logger.debug('Event Retrieval Service.')
132135
last_response = LastResponse(cursor)
133136
try:
134137
while True:
@@ -142,9 +145,9 @@ async def retrieve_events(server_queue, core, cloudfolders, cursor):
142145
except ConnectionError as error:
143146
await on_connection_error(error)
144147
except TimeoutError:
145-
logging.getLogger('cterasdk.metadata.connector').debug('Request timed out. Retrying.')
148+
logger.debug('Request timed out. Retrying.')
146149
except asyncio.CancelledError:
147-
logging.getLogger('cterasdk.metadata.connector').debug('Cancelling Event Retrieval.')
150+
logger.debug('Cancelling Event Retrieval.')
148151

149152

150153
async def forward_events(server_queue, client_queue, save_cursor):
@@ -155,15 +158,15 @@ async def forward_events(server_queue, client_queue, save_cursor):
155158
:param asyncio.Queue client_queue: Client queue.
156159
:param callback save_cursor: Callback function to persist the cursor.
157160
"""
158-
logging.getLogger('cterasdk.metadata.connector').debug('Event Forwarder Service.')
161+
logger.debug('Event Forwarder Service.')
159162
try:
160163
while True:
161164
batch = await server_queue.get()
162165
await enqueue_events(batch.objects, client_queue)
163166
await process_events(client_queue)
164167
await persist_cursor(save_cursor, batch.cursor)
165168
except asyncio.CancelledError:
166-
logging.getLogger('cterasdk.metadata.connector').debug('Cancelling Event Forwarding.')
169+
logger.debug('Cancelling Event Forwarding.')
167170

168171

169172
async def enqueue_events(events, queue):
@@ -174,9 +177,9 @@ async def enqueue_events(events, queue):
174177
:param cterasdk.asynchronous.core.iterator.CursorAsyncIterator events: Event Iterator.
175178
"""
176179
for event in events:
177-
logging.getLogger('cterasdk.metadata.connector').debug('Enqueuing Event.')
180+
logger.debug('Enqueuing Event.')
178181
await queue.put(Event.from_server_object(event))
179-
logging.getLogger('cterasdk.metadata.connector').debug('Enqueued Event.')
182+
logger.debug('Enqueued Event.')
180183

181184

182185
async def process_events(queue):
@@ -185,9 +188,9 @@ async def process_events(queue):
185188
186189
:param asyncio.Queue queue: Queue.
187190
"""
188-
logging.getLogger('cterasdk.metadata.connector').debug('Joining Queue.')
191+
logger.debug('Joining Queue.')
189192
await queue.join()
190-
logging.getLogger('cterasdk.metadata.connector').debug('Completed Processing.')
193+
logger.debug('Completed Processing.')
191194

192195

193196
async def persist_cursor(save_cursor, cursor):
@@ -197,19 +200,18 @@ async def persist_cursor(save_cursor, cursor):
197200
:param callback save_cursor: Asynchronous callback function to persist the cursor.
198201
:param str cursor: Cursor
199202
"""
200-
logging.getLogger('cterasdk.metadata.connector').debug("Persisting Cursor. Calling function: '%s'", save_cursor)
203+
logger.debug("Persisting Cursor. Calling function: '%s'", save_cursor)
201204
try:
202205
await save_cursor(cursor)
203-
logging.getLogger('cterasdk.metadata.connector').debug("Called Persist Cursor Function.")
206+
logger.debug("Called Persist Cursor Function.")
204207
except Exception: # pylint: disable=broad-exception-caught
205-
logging.getLogger('cterasdk.metadata.connector').error("An error occurred while trying to persist cursor. Function: '%s'",
206-
save_cursor)
208+
logger.error("An error occurred while trying to persist cursor. Function: '%s'", save_cursor)
207209

208210

209211
async def on_connection_error(error):
210212
seconds = 5
211-
logging.getLogger('cterasdk.metadata.connector').error('Connection error. Reason: %s.', str(error))
212-
logging.getLogger('cterasdk.metadata.connector').debug("Retrying in %s seconds.", seconds)
213+
logger.error('Connection error. Reason: %s.', str(error))
214+
logger.debug("Retrying in %s seconds.", seconds)
213215
await asyncio.sleep(seconds)
214216

215217

cterasdk/asynchronous/core/users.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from .base_command import BaseCommand
22
from ...common import union, Object
3-
from ...exceptions import ObjectNotFoundException, ContextError
3+
from ...exceptions import ObjectNotFoundException
4+
from ...exceptions.session import ContextError
45

56

67
class Users(BaseCommand):
@@ -25,7 +26,7 @@ async def get(self, user_account, include=None):
2526
include = ['/' + attr for attr in include]
2627
user_object = await self._core.v1.api.get_multi(baseurl, include)
2728
if user_object.name is None:
28-
raise ObjectNotFoundException('Could not find user', baseurl, user_directory=user_account.directory, username=user_account.name)
29+
raise ObjectNotFoundException(baseurl)
2930
return user_object
3031

3132
async def generate_ticket(self, username, tenant):
@@ -36,7 +37,7 @@ async def generate_ticket(self, username, tenant):
3637
:param str portal: Tenant
3738
"""
3839
if self.session().in_tenant_context():
39-
raise ContextError('Context error: Browse the Global Administration to invoke this API.')
40+
raise ContextError('Browse the Global Administration to invoke this API')
4041
param = Object()
4142
param.username = username
4243
param.portal = tenant

cterasdk/asynchronous/edge/files/browser.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ async def listdir(self, path):
1515
"""
1616
return await io.listdir(self._edge, self.normalize(path))
1717

18-
async def walk(self, path):
18+
async def walk(self, path=None):
1919
"""
2020
Walk Directory Contents
2121
22-
:param str path: Path to walk
22+
:param str, defaults to the root directory path: Path to walk
2323
"""
2424
return io.walk(self._edge, path)
2525

0 commit comments

Comments
 (0)