Merge lp:~nataliabidart/ubuntuone-storage-protocol/map-those-errors into lp:ubuntuone-storage-protocol

Proposed by Natalia Bidart
Status: Merged
Approved by: John O'Brien
Approved revision: 98
Merged at revision: not available
Proposed branch: lp:~nataliabidart/ubuntuone-storage-protocol/map-those-errors
Merge into: lp:ubuntuone-storage-protocol
Diff against target: 632 lines (+309/-74)
9 files modified
pylintrc (+3/-1)
tests/test_client.py (+1/-1)
tests/test_errors.py (+51/-0)
tests/test_request.py (+59/-6)
tests/test_volumes.py (+1/-1)
ubuntuone/storageprotocol/client.py (+22/-30)
ubuntuone/storageprotocol/errors.py (+156/-0)
ubuntuone/storageprotocol/request.py (+14/-35)
ubuntuone/storageprotocol/volumes.py (+2/-0)
To merge this branch: bzr merge lp:~nataliabidart/ubuntuone-storage-protocol/map-those-errors
Reviewer Review Type Date Requested Status
John O'Brien (community) Approve
dobey (community) Approve
Review via email: mp+22640@code.launchpad.net

Commit message

Added an internal mapping to correctly raise specific exceptions when an ERROR was received in a Request.

Description of the change

Added an internal mapping to correctly raise specific exceptions when an ERROR was received in a Request.

To post a comment you must log in.
Revision history for this message
dobey (dobey) wrote :

Can you clarify the pylintrc change by also adding comments as per the other values for disable-msg= above the disable-msg= line, or revert the change, using localized disable-msg comments instead if necessary? I'd like to avoid globally disabling messages when possible, as doing so may mask warnings that we should fix.

And can you use the following coding annotation instead, so that it's handled better by different editors?
# -*- coding: utf-8 -*-

review: Needs Fixing
Revision history for this message
Natalia Bidart (nataliabidart) wrote :

> Can you clarify the pylintrc change by also adding comments as per the other
> values for disable-msg= above the disable-msg= line, or revert the change,
> using localized disable-msg comments instead if necessary? I'd like to avoid
> globally disabling messages when possible, as doing so may mask warnings that
> we should fix.
>
> And can you use the following coding annotation instead, so that it's handled
> better by different editors?
> # -*- coding: utf-8 -*-

All changes added in revision 98.

98. By Natalia Bidart

Adding details to pylint disable-msg. Unified coding defs.

Revision history for this message
dobey (dobey) :
review: Approve
Revision history for this message
John O'Brien (jdobrien) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'pylintrc'
2--- pylintrc 2009-03-26 15:47:10 +0000
3+++ pylintrc 2010-04-01 18:11:31 +0000
4@@ -24,7 +24,9 @@
5 # :W0613: *Unused argument %r* (We get lots of these from interfaces)
6 # :W0621: *Redefining name %r from outer scope (line %s)* (pylint does a poor evaluation)
7 # :W0622: *Redefining built-in '%r'
8-disable-msg=E0101,W0142,W0221,W0613,W0621,W0622
9+# :W0201: *Attribute '%r' defined outside __init__*
10+# :W0212: *Access to a protected member %r of a client class*
11+disable-msg=E0101,W0142,W0221,W0613,W0621,W0622,W0201,W0212
12
13
14 [REPORTS]
15
16=== modified file 'tests/test_client.py'
17--- tests/test_client.py 2010-01-04 18:21:24 +0000
18+++ tests/test_client.py 2010-04-01 18:11:31 +0000
19@@ -1,4 +1,4 @@
20-# coding=utf-8
21+# -*- coding: utf-8 -*-
22 #
23 # Author: Natalia B. Bidart <natalia.bidart@canonical.com>
24 #
25
26=== added file 'tests/test_errors.py'
27--- tests/test_errors.py 1970-01-01 00:00:00 +0000
28+++ tests/test_errors.py 2010-04-01 18:11:31 +0000
29@@ -0,0 +1,51 @@
30+# -*- coding: utf-8 -*-
31+#
32+# Author: Natalia B. Bidart <natalia.bidart@canonical.com>
33+#
34+# Copyright (C) 2010 Canonical Ltd.
35+#
36+# This program is free software: you can redistribute it and/or modify it
37+# under the terms of the GNU Affero General Public License version 3,
38+# as published by the Free Software Foundation.
39+#
40+# This program is distributed in the hope that it will be useful, but
41+# WITHOUT ANY WARRANTY; without even the implied warranties of
42+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
43+# PURPOSE. See the GNU Affero General Public License for more details.
44+#
45+# You should have received a copy of the GNU Affero General Public License
46+# along with this program. If not, see <http://www.gnu.org/licenses/>.
47+"""Tests for errors module."""
48+
49+import unittest
50+
51+from ubuntuone.storageprotocol import errors, protocol_pb2
52+
53+REQ_ARGS = dict(request=None, message=protocol_pb2.Message())
54+
55+HIGH_LEVEL_ERRORS = {errors.StorageProtocolErrorSizeTooBig: dict(),
56+ errors.StorageProtocolProtocolError: dict(),
57+ errors.StorageRequestError: REQ_ARGS,
58+ errors.RequestCancelledError: dict()}
59+
60+
61+class ErrorsTestCase(unittest.TestCase):
62+ """Basic testing of errors mapping."""
63+
64+ def setUp(self):
65+ """Init."""
66+
67+ def test_exceptions_are_storage_protocol_error(self):
68+ """High level exceptions inherit from StorageProtocolError."""
69+ for e, args in HIGH_LEVEL_ERRORS.iteritems():
70+ self.assertTrue(isinstance(e(**args), errors.StorageProtocolError),
71+ "%r must inherit from StorageProtocolError" % e)
72+
73+ def test_mapping(self):
74+ """Protocol's specific exceptions are correct."""
75+ for code_error, proto_error in errors._error_mapping.iteritems():
76+ self.assertTrue(isinstance(proto_error(**REQ_ARGS),
77+ errors.StorageRequestError),
78+ "%r must inherit from StorageRequestError" %
79+ proto_error)
80+ self.assertEqual(proto_error, errors.error_to_exception(code_error))
81
82=== modified file 'tests/test_request.py'
83--- tests/test_request.py 2009-06-26 16:23:19 +0000
84+++ tests/test_request.py 2010-04-01 18:11:31 +0000
85@@ -2,16 +2,17 @@
86 # request class tests
87 #
88 # Author: Tim Cole <tim.cole@canonical.com>
89+# Author: Natalia Bidart <natalia.bidart@canonical.com>
90 #
91 # Copyright (C) 2009 Canonical
92 #
93-# This program is free software: you can redistribute it and/or modify it
94+# This program is free software: you can redistribute it and/or modify it
95 # under the terms of the GNU Affero General Public License version 3,
96 # as published by the Free Software Foundation.
97 #
98-# This program is distributed in the hope that it will be useful, but
99-# WITHOUT ANY WARRANTY; without even the implied warranties of
100-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
101+# This program is distributed in the hope that it will be useful, but
102+# WITHOUT ANY WARRANTY; without even the implied warranties of
103+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
104 # PURPOSE. See the GNU Affero General Public License for more details.
105 #
106 # You should have received a copy of the GNU Affero General Public License
107@@ -20,9 +21,13 @@
108
109 from __future__ import with_statement
110
111-from twisted.trial.unittest import TestCase
112+import unittest
113+
114+from twisted.trial.unittest import TestCase as TwistedTestCase
115 from twisted.internet.defer import inlineCallbacks
116 from twisted.python.failure import Failure
117+
118+from ubuntuone.storageprotocol import errors, protocol_pb2
119 from ubuntuone.storageprotocol.request import (
120 RequestHandler, Request)
121
122@@ -50,7 +55,7 @@
123 self.producer = None
124
125
126-class TestRequests(TestCase):
127+class TestRequests(TwistedTestCase):
128 """Tests for request handling."""
129
130 @inlineCallbacks
131@@ -74,3 +79,51 @@
132 pass # passed
133 else:
134 self.fail("Expected to fail with the correct reason.")
135+
136+
137+class TestRequest(unittest.TestCase):
138+ """Tests for request object."""
139+
140+ def setUp(self):
141+ """Init."""
142+ self.request = MindlessRequest(protocol=None)
143+ self.error = None
144+ self.request.error = lambda error: setattr(self, 'error', error)
145+
146+ def tearDown(self):
147+ """Clean up."""
148+ self.error = None
149+ self.request = None
150+
151+ def test_default_process_message_basic(self):
152+ """_default_process_message maps errors to exceptions."""
153+ message = protocol_pb2.Message()
154+ self.request._default_process_message(message)
155+
156+ self.assertTrue(isinstance(self.error, errors.StorageRequestError))
157+ self.assertEqual(self.request, self.error.request)
158+ self.assertEqual(message, self.error.error_message)
159+
160+ def test_default_process_message_no_error_message(self):
161+ """_default_process_message maps errors to exceptions."""
162+ message = protocol_pb2.Message()
163+ self.request._default_process_message(message)
164+
165+ self.assertTrue(self.error.__class__ is errors.StorageRequestError)
166+ self.assertEqual(self.request, self.error.request)
167+ self.assertEqual(message, self.error.error_message)
168+
169+ def test_default_process_message(self):
170+ """_default_process_message maps errors to exceptions."""
171+ for code_error, proto_error in errors._error_mapping.iteritems():
172+ message = protocol_pb2.Message()
173+ message.type = protocol_pb2.Message.ERROR
174+ message.error.type = code_error
175+ self.request._default_process_message(message)
176+
177+ self.assertTrue(isinstance(self.error, proto_error),
178+ "must be an instance of %r" % proto_error)
179+ self.assertEqual(self.request, self.error.request)
180+ self.assertEqual(message, self.error.error_message)
181+
182+ self.error = None
183
184=== modified file 'tests/test_volumes.py'
185--- tests/test_volumes.py 2009-12-23 16:29:02 +0000
186+++ tests/test_volumes.py 2010-04-01 18:11:31 +0000
187@@ -1,4 +1,4 @@
188-# coding=utf-8
189+# -*- coding: utf-8 -*-
190 #
191 # Author: Natalia B. Bidart <natalia.bidart@canonical.com>
192 #
193
194=== modified file 'ubuntuone/storageprotocol/client.py'
195--- ubuntuone/storageprotocol/client.py 2010-03-12 20:32:23 +0000
196+++ ubuntuone/storageprotocol/client.py 2010-04-01 18:11:31 +0000
197@@ -569,14 +569,12 @@
198 if self.callback is None:
199 self.data = "".join(self.parts)
200 self.done()
201- elif message.type == protocol_pb2.Message.ERROR:
202- self.error(request.StorageRequestError(self, message))
203 elif message.type == protocol_pb2.Message.OK:
204 self.done()
205 elif message.type == protocol_pb2.Message.CANCELLED:
206 self.error(request.RequestCancelledError("CANCELLED"))
207 else:
208- self.error(request.StorageProtocolProtocolError(message))
209+ self._default_process_message(message)
210
211 def _cancel(self):
212 """Cancel the current download."""
213@@ -609,7 +607,7 @@
214 elif message.type == protocol_pb2.Message.SHARES_END:
215 self.done()
216 else:
217- self.error(request.StorageRequestError(self, message))
218+ self._default_process_message(message)
219
220
221 class CreateShare(request.Request):
222@@ -666,7 +664,7 @@
223 # XXX: this is for PROTOCOL_VERSION=1 backward compatibility
224 self.done()
225 else:
226- self.error(request.StorageRequestError(self, message))
227+ self._default_process_message(message)
228
229
230 class AcceptShare(request.Request):
231@@ -711,7 +709,7 @@
232 if message.type == protocol_pb2.Message.OK:
233 self.done()
234 else:
235- self.error(request.StorageRequestError(self, message))
236+ self._default_process_message(message)
237
238
239 class DeleteShare(request.Request):
240@@ -739,7 +737,7 @@
241 if message.type == protocol_pb2.Message.OK:
242 self.done()
243 else:
244- self.error(request.StorageRequestError(self, message))
245+ self._default_process_message(message)
246
247
248 class CreateUDF(request.Request):
249@@ -774,7 +772,7 @@
250 self.node_id = message.volume_created.udf.node
251 self.done()
252 else:
253- self.error(request.StorageRequestError(self, message))
254+ self._default_process_message(message)
255
256
257 class ListVolumes(request.Request):
258@@ -816,7 +814,7 @@
259 elif message.type == protocol_pb2.Message.VOLUMES_END:
260 self.done()
261 else:
262- self.error(request.StorageRequestError(self, message))
263+ self._default_process_message(message)
264
265
266 class DeleteVolume(request.Request):
267@@ -844,7 +842,7 @@
268 if message.type == protocol_pb2.Message.OK:
269 self.done()
270 else:
271- self.error(request.StorageRequestError(self, message))
272+ self._default_process_message(message)
273
274
275 class Unlink(request.Request):
276@@ -875,7 +873,7 @@
277 if message.type == protocol_pb2.Message.OK:
278 self.done()
279 else:
280- self.error(request.StorageRequestError(self, message))
281+ self._default_process_message(message)
282
283
284 class Move(request.Request):
285@@ -914,10 +912,8 @@
286 if message.type == protocol_pb2.Message.OK:
287 self.done()
288 else:
289- # XXX:
290- # lucio.torre
291- # handle more error messages
292- self.error(request.StorageRequestError(self, message))
293+ self._default_process_message(message)
294+
295
296 class MultiQuery(object):
297 """Create a Request-like object that encapsulates many Query requests
298@@ -954,6 +950,7 @@
299 for q in self.queries:
300 q.start()
301
302+
303 class Query(request.Request):
304 """Query about the hash of a node_id.
305
306@@ -1011,10 +1008,7 @@
307 elif message.type == protocol_pb2.Message.QUERY_END:
308 self.done()
309 else:
310- # XXX:
311- # lucio.torre
312- # handle more error messages
313- self.error(message)
314+ self._default_process_message(message)
315
316
317 class BytesMessageProducer(object):
318@@ -1122,7 +1116,7 @@
319 elif message.type == protocol_pb2.Message.CANCELLED:
320 self.error(request.RequestCancelledError("CANCELLED"))
321 else:
322- self.error(request.StorageRequestError(self, message))
323+ self._default_process_message(message)
324
325 def _cancel(self):
326 """Cancel the current upload."""
327@@ -1183,7 +1177,8 @@
328 self.new_name = message.new.name
329 self.done()
330 else:
331- self.error(request.StorageRequestError(self, message))
332+ self._default_process_message(message)
333+
334
335 class MakeDir(MakeObject):
336 """Extend MakeObject to make directories."""
337@@ -1224,7 +1219,8 @@
338 self.other_protocol_version = message.protocol.version
339 self.done()
340 else:
341- self.error(request.StorageRequestError(self, message))
342+ self._default_process_message(message)
343+
344
345 class Authenticate(request.Request):
346 """Request to authenticate the user."""
347@@ -1253,12 +1249,8 @@
348 """Handle messages."""
349 if message.type == protocol_pb2.Message.AUTH_AUTHENTICATED:
350 self.done()
351- elif message.type == protocol_pb2.Message.ERROR:
352- # as the error travels with the exception, we send all here
353- self.error(request.StorageRequestError(self, message))
354 else:
355- self.error(request.StorageProtocolError(
356- "Authentication Error:"+str(message)))
357+ self._default_process_message(message)
358
359
360 class QuerySetCaps(request.Request):
361@@ -1304,7 +1296,7 @@
362 self.redirect_srvrecord = message.accept_caps.redirect_srvrecord
363 self.done()
364 else:
365- self.error(request.StorageRequestError(self, message))
366+ self._default_process_message(message)
367
368
369 class FreeSpaceInquiry(request.Request):
370@@ -1330,7 +1322,7 @@
371 self.free_bytes = message.free_space_info.free_bytes
372 self.done()
373 else:
374- self.error(request.StorageRequestError(self, message))
375+ self._default_process_message(message)
376
377
378 class AccountInquiry(request.Request):
379@@ -1350,7 +1342,7 @@
380 self.purchased_bytes = message.account_info.purchased_bytes
381 self.done()
382 else:
383- self.error(request.StorageRequestError(self, message))
384+ self._default_process_message(message)
385
386
387 class ThrottlingStorageClient(StorageClient):
388
389=== added file 'ubuntuone/storageprotocol/errors.py'
390--- ubuntuone/storageprotocol/errors.py 1970-01-01 00:00:00 +0000
391+++ ubuntuone/storageprotocol/errors.py 2010-04-01 18:11:31 +0000
392@@ -0,0 +1,156 @@
393+# -*- coding: utf-8 -*-
394+#
395+# Author: Natalia B. Bidart <natalia.bidart@canonical.com>
396+#
397+# Copyright 2010 Canonical Ltd.
398+#
399+# This program is free software: you can redistribute it and/or modify it
400+# under the terms of the GNU Affero General Public License version 3,
401+# as published by the Free Software Foundation.
402+#
403+# This program is distributed in the hope that it will be useful, but
404+# WITHOUT ANY WARRANTY; without even the implied warranties of
405+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
406+# PURPOSE. See the GNU Affero General Public License for more details.
407+#
408+# You should have received a copy of the GNU Affero General Public License
409+# along with this program. If not, see <http://www.gnu.org/licenses/>.
410+"""The errors abstraction."""
411+
412+from ubuntuone.storageprotocol import protocol_pb2
413+
414+
415+class StorageProtocolError(Exception):
416+ """Base class for all client/server exceptions."""
417+
418+
419+class StorageProtocolErrorSizeTooBig(StorageProtocolError):
420+ """The size we received was too big."""
421+
422+
423+class StorageProtocolProtocolError(StorageProtocolError):
424+ """A protocol error on the storage protocol."""
425+
426+
427+class StorageRequestError(StorageProtocolError):
428+ """An exception that keeps the request that generated it around."""
429+
430+ def __init__(self, request, message):
431+ """Create a StorageRequestError.
432+
433+ @param request: the request that generated this error.
434+ @param message: the message received that generated the error.
435+ """
436+ error_name = protocol_pb2.Error.DESCRIPTOR \
437+ .enum_types_by_name['ErrorType'] \
438+ .values_by_number[message.error.type].name
439+ super(StorageRequestError, self).__init__(error_name)
440+ #: the request that generated the error
441+ self.request = request
442+ #: the message received that generated the error
443+ self.error_message = message
444+
445+
446+class RequestCancelledError(StorageProtocolError):
447+ """The request was cancelled."""
448+
449+
450+# Request specific errors
451+
452+
453+class UnsupportedVersionError(StorageRequestError):
454+ """The version is not supported."""
455+
456+
457+class AuthenticationFailedError(StorageRequestError):
458+ """The authencation failed."""
459+
460+
461+class InternalError(StorageRequestError):
462+ """There was an internal error on the other side."""
463+
464+
465+class AuthenticationRequiredError(StorageRequestError):
466+ """The authentication is required and hasn't been established yet."""
467+
468+
469+class NoPermissionError(StorageRequestError):
470+ """Current permissions are not the required ones."""
471+
472+
473+class AlreadyExistsError(StorageRequestError):
474+ """The node already exists."""
475+
476+
477+class DoesNotExistError(StorageRequestError):
478+ """The node does not exists."""
479+
480+
481+class NotADirectoryError(StorageRequestError):
482+ """The node is not a directory."""
483+
484+
485+class NotEmptyError(StorageRequestError):
486+ """The node is not empty."""
487+
488+
489+class NotAvailableError(StorageRequestError):
490+ """The node is not available."""
491+
492+
493+class UploadInProgressError(StorageRequestError):
494+ """There is already an upload in progress."""
495+
496+
497+class UploadCorruptError(StorageRequestError):
498+ """The upload is corrupted."""
499+
500+
501+class UploadCanceledError(StorageRequestError):
502+ """There upload was canceled."""
503+
504+
505+class ConflictError(StorageRequestError):
506+ """The was a conflict."""
507+
508+
509+class TryAgainError(StorageRequestError):
510+ """Server answered to try again."""
511+
512+
513+class ProtocolError(StorageRequestError):
514+ """There was a protocol error."""
515+
516+
517+class QuotaExceededError(StorageRequestError):
518+ """The quota was exceeded."""
519+
520+
521+class InvalidFilenameError(StorageRequestError):
522+ """The filename is invalid."""
523+
524+
525+_error_mapping = {
526+ protocol_pb2.Error.UNSUPPORTED_VERSION: UnsupportedVersionError,
527+ protocol_pb2.Error.AUTHENTICATION_FAILED: AuthenticationFailedError,
528+ protocol_pb2.Error.INTERNAL_ERROR: InternalError,
529+ protocol_pb2.Error.AUTHENTICATION_REQUIRED: AuthenticationRequiredError,
530+ protocol_pb2.Error.NO_PERMISSION: NoPermissionError,
531+ protocol_pb2.Error.ALREADY_EXISTS: AlreadyExistsError,
532+ protocol_pb2.Error.DOES_NOT_EXIST: DoesNotExistError,
533+ protocol_pb2.Error.NOT_A_DIRECTORY: NotADirectoryError,
534+ protocol_pb2.Error.NOT_EMPTY: NotEmptyError,
535+ protocol_pb2.Error.NOT_AVAILABLE: NotAvailableError,
536+ protocol_pb2.Error.UPLOAD_IN_PROGRESS: UploadInProgressError,
537+ protocol_pb2.Error.UPLOAD_CORRUPT: UploadCorruptError,
538+ protocol_pb2.Error.UPLOAD_CANCELED: UploadCanceledError,
539+ protocol_pb2.Error.CONFLICT: ConflictError,
540+ protocol_pb2.Error.TRY_AGAIN: TryAgainError,
541+ protocol_pb2.Error.PROTOCOL_ERROR: ProtocolError,
542+ protocol_pb2.Error.QUOTA_EXCEEDED: QuotaExceededError,
543+ protocol_pb2.Error.INVALID_FILENAME: InvalidFilenameError,
544+}
545+
546+def error_to_exception(error_code):
547+ """Map protocol errors to specific exceptions."""
548+ return _error_mapping[error_code]
549
550=== modified file 'ubuntuone/storageprotocol/request.py'
551--- ubuntuone/storageprotocol/request.py 2010-01-04 18:21:24 +0000
552+++ ubuntuone/storageprotocol/request.py 2010-04-01 18:11:31 +0000
553@@ -36,6 +36,12 @@
554 from zope.interface import implements
555
556 from ubuntuone.storageprotocol import protocol_pb2, validators
557+from ubuntuone.storageprotocol.errors import (
558+ StorageProtocolError, StorageProtocolErrorSizeTooBig,
559+ StorageProtocolProtocolError, StorageRequestError,
560+ RequestCancelledError, error_to_exception
561+)
562+
563
564 # the max possible packet size is 2**32 (32 bits for size)
565 # although we will not allow packets that big for now
566@@ -53,41 +59,6 @@
567 # the referred is the own root node, and not any of the shares
568 ROOT = ''
569
570-class StorageProtocolError(Exception):
571- """base class for all client/server exceptions."""
572-
573-
574-class StorageProtocolErrorSizeTooBig(StorageProtocolError):
575- """the size we received was too big."""
576-
577-
578-class StorageProtocolProtocolError(StorageProtocolError):
579- """A protocol error on the storage protocol."""
580-
581-
582-class StorageRequestError(StorageProtocolError):
583- """an exception that keeps the request that generated it around."""
584-
585- def __init__(self, request, message):
586- """create a StorageRequestError.
587-
588- @param request: the request that generated this error.
589- @param message: the message received that generated the error.
590- """
591- error_name = protocol_pb2.Error.DESCRIPTOR \
592- .enum_types_by_name['ErrorType'] \
593- .values_by_number[message.error.type].name
594- super(StorageRequestError, self).__init__(error_name)
595- #: the request that generated the error
596- self.request = request
597- #: the message received that generated the error
598- self.error_message = message
599-
600-
601-class RequestCancelledError(Exception):
602- """The request is cancelled."""
603-
604-
605 class RequestHandler(Protocol):
606 """the base class for a network peer.
607
608@@ -419,6 +390,14 @@
609 """override this method to start the request."""
610 raise NotImplementedError("request needs to do something")
611
612+ def _default_process_message(self, message):
613+ """Map ERROR message to a specific exception."""
614+ if message.type == protocol_pb2.Message.ERROR:
615+ error_class = error_to_exception(message.error.type)
616+ else:
617+ error_class = StorageRequestError
618+ self.error(error_class(self, message))
619+
620 def processMessage(self, message):
621 """handle an incoming message for this request. override this.
622
623
624=== modified file 'ubuntuone/storageprotocol/volumes.py'
625--- ubuntuone/storageprotocol/volumes.py 2009-12-22 19:25:50 +0000
626+++ ubuntuone/storageprotocol/volumes.py 2010-04-01 18:11:31 +0000
627@@ -1,3 +1,5 @@
628+# -*- coding: utf-8 -*-
629+#
630 # Author: Natalia B. Bidart <natalia.bidart@canonical.com>
631 #
632 # Copyright 2009 Canonical Ltd.

Subscribers

People subscribed via source and target branches