Skip to content

Commit ce3e8f0

Browse files
committedFeb 22, 2015
Merge branch 'shakim-enhance-exception-information'
2 parents 2cfb11a + 31d5257 commit ce3e8f0

File tree

7 files changed

+66
-20
lines changed

7 files changed

+66
-20
lines changed
 

‎.travis.yml

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ python:
55

66
before_install:
77
- ./build_etcd.sh v0.3.0
8+
- pip install --upgrade setuptools
89

910
# command to install dependencies
1011
install:

‎src/etcd/__init__.py

+51-7
Original file line numberDiff line numberDiff line change
@@ -105,19 +105,56 @@ class EtcdException(Exception):
105105
"""
106106
Generic Etcd Exception.
107107
"""
108+
def __init__(self, message=None, payload=None):
109+
super(Exception, self).__init__(message)
110+
self.payload=payload
108111

112+
113+
class EtcdKeyError(EtcdException):
114+
"""
115+
Etcd Generic KeyError Exception
116+
"""
117+
pass
118+
119+
class EtcdKeyNotFound(EtcdKeyError):
120+
"""
121+
Etcd key not found exception (100)
122+
"""
123+
pass
124+
125+
class EtcdNotFile(EtcdKeyError):
126+
"""
127+
Etcd not a file exception (102)
128+
"""
109129
pass
110130

131+
class EtcdNotDir(EtcdKeyError):
132+
"""
133+
Etcd not a directory exception (104)
134+
"""
135+
pass
136+
137+
class EtcdAlreadyExist(EtcdKeyError):
138+
"""
139+
Etcd already exist exception (105)
140+
"""
141+
pass
142+
143+
class EtcdEventIndexCleared(EtcdException):
144+
"""
145+
Etcd event index is outdated and cleared exception (401)
146+
"""
147+
pass
111148

112149
class EtcdError(object):
113150
# See https://github.com/coreos/etcd/blob/master/Documentation/errorcode.md
114151
error_exceptions = {
115-
100: KeyError,
152+
100: EtcdKeyNotFound,
116153
101: ValueError,
117-
102: KeyError,
154+
102: EtcdNotFile,
118155
103: Exception,
119-
104: KeyError,
120-
105: KeyError,
156+
104: EtcdNotDir,
157+
105: EtcdAlreadyExist,
121158
106: KeyError,
122159
200: ValueError,
123160
201: ValueError,
@@ -126,20 +163,27 @@ class EtcdError(object):
126163
300: Exception,
127164
301: Exception,
128165
400: Exception,
129-
401: EtcdException,
166+
401: EtcdEventIndexCleared,
130167
500: EtcdException
131168
}
132169

133170
@classmethod
134171
def handle(cls, errorCode=None, message=None, cause=None, **kwdargs):
135172
""" Decodes the error and throws the appropriate error message"""
136173
try:
137-
msg = "{} : {}".format(message, cause)
174+
msg = '{} : {}'.format(message, cause)
175+
payload={'errorCode': errorCode, 'message': message, 'cause': cause}
176+
if len(kwdargs) > 0:
177+
for key in kwdargs:
178+
payload[key]=kwdargs[key]
138179
exc = cls.error_exceptions[errorCode]
139180
except:
140181
msg = "Unable to decode server response"
141182
exc = EtcdException
142-
raise exc(msg)
183+
if exc in [EtcdException, EtcdKeyNotFound, EtcdNotFile, EtcdNotDir, EtcdAlreadyExist, EtcdEventIndexCleared]:
184+
raise exc(msg, payload)
185+
else:
186+
raise exc(msg)
143187

144188

145189
# Attempt to enable urllib3's SNI support, if possible

‎src/etcd/client.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ def __contains__(self, key):
202202
try:
203203
self.get(key)
204204
return True
205-
except KeyError:
205+
except etcd.EtcdKeyNotFound:
206206
return False
207207

208208
def _sanitize_key(self, key):

‎src/etcd/tests/integration/test_election.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,4 @@ def test_get_delete_after_ttl_expired_raises(self):
3131
e.set('/mysql', name='foo', ttl=1)
3232
time.sleep(2)
3333
self.assertRaises(etcd.EtcdException, e.get, '/mysql')
34-
self.assertRaises(KeyError, e.delete, '/mysql', name='foo')
34+
self.assertRaises(etcd.EtcdKeyNotFound, e.delete, '/mysql', name='foo')

‎src/etcd/tests/integration/test_simple.py

+6-5
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def test_get_set_delete(self):
7474
try:
7575
get_result = self.client.get('/test_set')
7676
assert False
77-
except KeyError as e:
77+
except etcd.EtcdKeyNotFound as e:
7878
pass
7979

8080
self.assertFalse('/test_set' in self.client)
@@ -100,7 +100,7 @@ def test_get_set_delete(self):
100100
try:
101101
get_result = self.client.get('/test_set')
102102
assert False
103-
except KeyError as e:
103+
except etcd.EtcdKeyNotFound as e:
104104
pass
105105

106106
def test_update(self):
@@ -120,7 +120,7 @@ def test_retrieve_subkeys(self):
120120
set_result = self.client.write('/subtree/test_set2', 'test-key3')
121121
get_result = self.client.read('/subtree', recursive=True)
122122
result = [subkey.value for subkey in get_result.leaves]
123-
self.assertEquals(['test-key1', 'test-key2', 'test-key3'], result)
123+
self.assertEquals(['test-key1', 'test-key2', 'test-key3'].sort(), result.sort())
124124

125125
def test_directory_ttl_update(self):
126126
""" INTEGRATION: should be able to update a dir TTL """
@@ -140,7 +140,7 @@ def test_is_not_a_file(self):
140140
""" INTEGRATION: try to write value to an existing directory """
141141

142142
self.client.set('/directory/test-key', 'test-value')
143-
self.assertRaises(KeyError, self.client.set, '/directory', 'test-value')
143+
self.assertRaises(etcd.EtcdNotFile, self.client.set, '/directory', 'test-value')
144144

145145
def test_test_and_set(self):
146146
""" INTEGRATION: try test_and_set operation """
@@ -159,7 +159,8 @@ def test_creating_already_existing_directory(self):
159159
`prevExist=True` should fail """
160160
self.client.write('/mydir', None, dir=True)
161161

162-
self.assertRaises(KeyError, self.client.write, '/mydir', None, dir=True)
162+
self.assertRaises(etcd.EtcdNotFile, self.client.write, '/mydir', None, dir=True)
163+
self.assertRaises(etcd.EtcdAlreadyExist, self.client.write, '/mydir', None, dir=True, prevExist=False)
163164

164165

165166
class TestClusterFunctions(EtcdIntegrationTest):

‎src/etcd/tests/unit/test_old_request.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ def test_get_subdirs(self):
171171
def test_not_in(self):
172172
""" Can check if key is not in client """
173173
client = etcd.Client()
174-
client.get = mock.Mock(side_effect=KeyError())
174+
client.get = mock.Mock(side_effect=etcd.EtcdKeyNotFound())
175175
result = '/testkey' not in client
176176
self.assertEquals(True, result)
177177

@@ -307,8 +307,8 @@ def test_get_error(self):
307307
try:
308308
client.api_execute('/v2/keys/testkey', client._MGET)
309309
assert False
310-
except KeyError as e:
311-
self.assertEquals(str(e), "'message : cause'")
310+
except etcd.EtcdKeyNotFound as e:
311+
self.assertEquals(str(e), 'message : cause')
312312

313313
def test_put(self):
314314
""" http put request """
@@ -357,8 +357,8 @@ def test_set_error(self):
357357
try:
358358
client.api_execute('/v2/keys/testkey', client._MPUT, payload)
359359
self.fail()
360-
except KeyError as e:
361-
self.assertEquals("'message : cause'", str(e))
360+
except etcd.EtcdNotFile as e:
361+
self.assertEquals('message : cause', str(e))
362362

363363
def test_get_error_unknown(self):
364364
""" http get error request unknown """

‎src/etcd/tests/unit/test_request.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ def test_get_dir(self):
282282

283283
def test_not_in(self):
284284
""" Can check if key is not in client """
285-
self._mock_exception(KeyError, 'Key not Found : /testKey')
285+
self._mock_exception(etcd.EtcdKeyNotFound, 'Key not Found : /testKey')
286286
self.assertTrue('/testey' not in self.client)
287287

288288
def test_in(self):

0 commit comments

Comments
 (0)
Please sign in to comment.