Skip to content

Commit

Permalink
Merge branch 'shakim-enhance-exception-information'
Browse files Browse the repository at this point in the history
  • Loading branch information
lavagetto committed Feb 22, 2015
2 parents 2cfb11a + 31d5257 commit ce3e8f0
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 20 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ python:

before_install:
- ./build_etcd.sh v0.3.0
- pip install --upgrade setuptools

# command to install dependencies
install:
Expand Down
58 changes: 51 additions & 7 deletions src/etcd/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,19 +105,56 @@ class EtcdException(Exception):
"""
Generic Etcd Exception.
"""
def __init__(self, message=None, payload=None):
super(Exception, self).__init__(message)
self.payload=payload


class EtcdKeyError(EtcdException):
"""
Etcd Generic KeyError Exception
"""
pass

class EtcdKeyNotFound(EtcdKeyError):
"""
Etcd key not found exception (100)
"""
pass

class EtcdNotFile(EtcdKeyError):
"""
Etcd not a file exception (102)
"""
pass

class EtcdNotDir(EtcdKeyError):
"""
Etcd not a directory exception (104)
"""
pass

class EtcdAlreadyExist(EtcdKeyError):
"""
Etcd already exist exception (105)
"""
pass

class EtcdEventIndexCleared(EtcdException):
"""
Etcd event index is outdated and cleared exception (401)
"""
pass

class EtcdError(object):
# See https://github.com/coreos/etcd/blob/master/Documentation/errorcode.md
error_exceptions = {
100: KeyError,
100: EtcdKeyNotFound,
101: ValueError,
102: KeyError,
102: EtcdNotFile,
103: Exception,
104: KeyError,
105: KeyError,
104: EtcdNotDir,
105: EtcdAlreadyExist,
106: KeyError,
200: ValueError,
201: ValueError,
Expand All @@ -126,20 +163,27 @@ class EtcdError(object):
300: Exception,
301: Exception,
400: Exception,
401: EtcdException,
401: EtcdEventIndexCleared,
500: EtcdException
}

@classmethod
def handle(cls, errorCode=None, message=None, cause=None, **kwdargs):
""" Decodes the error and throws the appropriate error message"""
try:
msg = "{} : {}".format(message, cause)
msg = '{} : {}'.format(message, cause)
payload={'errorCode': errorCode, 'message': message, 'cause': cause}
if len(kwdargs) > 0:
for key in kwdargs:
payload[key]=kwdargs[key]
exc = cls.error_exceptions[errorCode]
except:
msg = "Unable to decode server response"
exc = EtcdException
raise exc(msg)
if exc in [EtcdException, EtcdKeyNotFound, EtcdNotFile, EtcdNotDir, EtcdAlreadyExist, EtcdEventIndexCleared]:
raise exc(msg, payload)
else:
raise exc(msg)


# Attempt to enable urllib3's SNI support, if possible
Expand Down
2 changes: 1 addition & 1 deletion src/etcd/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ def __contains__(self, key):
try:
self.get(key)
return True
except KeyError:
except etcd.EtcdKeyNotFound:
return False

def _sanitize_key(self, key):
Expand Down
2 changes: 1 addition & 1 deletion src/etcd/tests/integration/test_election.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ def test_get_delete_after_ttl_expired_raises(self):
e.set('/mysql', name='foo', ttl=1)
time.sleep(2)
self.assertRaises(etcd.EtcdException, e.get, '/mysql')
self.assertRaises(KeyError, e.delete, '/mysql', name='foo')
self.assertRaises(etcd.EtcdKeyNotFound, e.delete, '/mysql', name='foo')
11 changes: 6 additions & 5 deletions src/etcd/tests/integration/test_simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def test_get_set_delete(self):
try:
get_result = self.client.get('/test_set')
assert False
except KeyError as e:
except etcd.EtcdKeyNotFound as e:
pass

self.assertFalse('/test_set' in self.client)
Expand All @@ -100,7 +100,7 @@ def test_get_set_delete(self):
try:
get_result = self.client.get('/test_set')
assert False
except KeyError as e:
except etcd.EtcdKeyNotFound as e:
pass

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

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

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

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

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


class TestClusterFunctions(EtcdIntegrationTest):
Expand Down
10 changes: 5 additions & 5 deletions src/etcd/tests/unit/test_old_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def test_get_subdirs(self):
def test_not_in(self):
""" Can check if key is not in client """
client = etcd.Client()
client.get = mock.Mock(side_effect=KeyError())
client.get = mock.Mock(side_effect=etcd.EtcdKeyNotFound())
result = '/testkey' not in client
self.assertEquals(True, result)

Expand Down Expand Up @@ -307,8 +307,8 @@ def test_get_error(self):
try:
client.api_execute('/v2/keys/testkey', client._MGET)
assert False
except KeyError as e:
self.assertEquals(str(e), "'message : cause'")
except etcd.EtcdKeyNotFound as e:
self.assertEquals(str(e), 'message : cause')

def test_put(self):
""" http put request """
Expand Down Expand Up @@ -357,8 +357,8 @@ def test_set_error(self):
try:
client.api_execute('/v2/keys/testkey', client._MPUT, payload)
self.fail()
except KeyError as e:
self.assertEquals("'message : cause'", str(e))
except etcd.EtcdNotFile as e:
self.assertEquals('message : cause', str(e))

def test_get_error_unknown(self):
""" http get error request unknown """
Expand Down
2 changes: 1 addition & 1 deletion src/etcd/tests/unit/test_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ def test_get_dir(self):

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

def test_in(self):
Expand Down

0 comments on commit ce3e8f0

Please sign in to comment.