emlaver: @smithsz @alfinkel I'm considering changing the exception class names to module+exception i.e. from `CloudantClientException` to `ClientException`.  Any thoughts?
alfinkel: Have you considered doing something similar to this with the CloudantArgumentError expections?
emlaver: @alfinkel I've considered moving the `CloudantArgumentError` error messages to the `messages` module.  I currently don't see it being worthwhile to have separate classes for argument errors in each module.
alfinkel: I agree that it's not worthwhile splitting the argument errors exceptions into their own classes.  However, my question was whether you considered moving _all_ argument error messages into your `_messages` module and basically handling argument error messages the same way as you handle the individual class exception messages.
diff --git a/src/cloudant/_messages.py b/src/cloudant/_messages.py
new file mode 100644
index 0000000..79485ad
--- /dev/null
+++ b/src/cloudant/_messages.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+# Copyright (c) 2016 IBM. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Module that contains exception messages for the Cloudant Python client
+library.
+"""
+CLIENT = {
+    100: 'A general Cloudant client exception was raised.',
+    101: 'Value must be set to a Database object. Found type: {0}.',
+    102: 'You must provide a url or an account.',
+    404: 'Database {0} does not exist. Verify that the client is valid and try again.',
+    409: 'Database {0} already exists.'
+}
+
+DATABASE = {
+    100: 'A general Cloudant database exception was raised.',
+    101: 'Unexpected index type. Found: {0}.',
+    400: 'Invalid database name during creation. Found: {0}',
+    401: 'Unauthorized to create database {0}.',
+    409: 'Document with id {0} already exists.'
+}
+
+DESIGN_DOCUMENT = {
+    100: 'A general Cloudant design document exception was raised.',
+    101: 'Cannot add a MapReduce view to a design document for query indexes.',
+    102: 'Cannot update a query index view using this method.',
+    103: 'Cannot delete a query index view using this method.',
+    104: 'View {0} must be of type View.',
+    105: 'View {0} must be of type QueryIndexView.',
+    106: 'Function for search index {0} must be of type string.',
+    107: 'Definition for query text index {0} must be of type dict.'
+}
+
+DOCUMENT = {
+    100: 'A general Cloudant document exception was raised.',
+    101: 'A document id is required to fetch document contents. '
+         'Add an _id key and value to the document and re-try.',
+    102: 'The field {0} is not a list.',
+    103: 'Attempting to delete a doc with no _rev. Try running .fetch and re-try.'
+}
+
+FEED = {
+    100: 'A general Cloudant feed exception was raised.',
+    101: 'Infinite _db_updates feed not supported for CouchDB.'
+}
+
+INDEX = {
+    100: 'A general Cloudant index exception was raised.',
+    101: 'Creating the \"special\" index is not allowed.',
+    102: 'Deleting the \"special\" index is not allowed.'
+}
+
+REPLICATOR = {
+    100: 'A general Cloudant replicator exception was raised.',
+    101: 'You must specify either a source_db Database object or a manually composed'
+         ' \'source\' string/dict.',
+    102: 'You must specify either a target_db Database object or a manually composed'
+         ' \'target\' string/dict.',
+    404: 'Replication with id {0} not found.'
+}
+
+RESULT = {
+    100: 'A general result exception was raised.',
+    101: 'Failed to interpret the argument {0} as a valid key value or as a valid slice.',
+    102: 'Cannot use {0} when performing key access or key slicing. Found {1}.',
+    103: 'Cannot use {0} for iteration. Found {1}.',
+    104: 'Invalid page_size: {0}'
+}
diff --git a/src/cloudant/client.py b/src/cloudant/client.py
index ea4fe7f..7d73d5c 100755
--- a/src/cloudant/client.py
+++ b/src/cloudant/client.py
@@ -24,7 +24,7 @@ import requests
 from ._2to3 import bytes_, unicode_
 from .database import CloudantDatabase, CouchDatabase
 from .feed import Feed, InfiniteFeed
-from .error import CloudantException, CloudantArgumentError
+from .error import CloudantArgumentError, CloudantClientException
 from ._common_util import (
     USER_AGENT,
     append_response_error_content,
@@ -198,37 +198,34 @@ class CouchDB(dict):
         Creates a new database on the remote server with the name provided
         and adds the new database object to the client's locally cached
         dictionary before returning it to the caller.  The method will
-        optionally throw a CloudantException if the database exists remotely.
+        optionally throw a CloudantClientException if the database
+        exists remotely.
 
         :param str dbname: Name used to create the database.
         :param bool throw_on_exists: Boolean flag dictating whether or
-            not to throw a CloudantException when attempting to create a
-            database that already exists.
+            not to throw a CloudantClientException when attempting to
+            create a database that already exists.
 
         :returns: The newly created database object
         """
         new_db = self._DATABASE_CLASS(self, dbname)
         if new_db.exists():
             if kwargs.get('throw_on_exists', True):
-                raise CloudantException(
-                    "Database {0} already exists".format(dbname)
-                )
+                raise CloudantClientException(409, dbname)
         new_db.create()
         super(CouchDB, self).__setitem__(dbname, new_db)
         return new_db
 
     def delete_database(self, dbname):
         """
-        Removes the named database remotely and locally. The method will throw a
-        CloudantException if the database does not exist.
+        Removes the named database remotely and locally. The method will throw
+        a CloudantClientException if the database does not exist.
 
         :param str dbname: Name of the database to delete.
         """
         db = self._DATABASE_CLASS(self, dbname)
         if not db.exists():
-            raise CloudantException(
-                "Database {0} does not exist".format(dbname)
-            )
+            raise CloudantClientException(404, dbname)
         db.delete()
         if dbname in list(self.keys()):
             super(CouchDB, self).__delitem__(dbname)
@@ -378,8 +375,7 @@ class CouchDB(dict):
             create the database remotely or not.  Defaults to False.
         """
         if not isinstance(value, self._DATABASE_CLASS):
-            msg = "Value must be set to a Database object"
-            raise CloudantException(msg)
+            raise CloudantClientException(101, type(value).__name__)
         if remote and not value.exists():
             value.create()
         super(CouchDB, self).__setitem__(key, value)
@@ -426,7 +422,7 @@ class Cloudant(CouchDB):
                 self._client_user_header['X-Cloudant-User'] = x_cloudant_user
 
         if self.server_url is None:
-            raise CloudantException('You must provide a url or an account.')
+            raise CloudantClientException(102)
 
         if kwargs.get('connect', False):
             self.connect()
@@ -537,7 +533,7 @@ class Cloudant(CouchDB):
             between 1 and 12. Optional parameter. Defaults to None.
             If used, it must be accompanied by ``year``.
         """
-        err = None
+        err = False
         if year is None and month is None:
             resp = self.r_session.get(endpoint)
         else:
@@ -548,14 +544,14 @@ class Cloudant(CouchDB):
                             endpoint, str(int(year)), str(int(month)))
                     )
                 else:
-                    err = ('Invalid year and/or month supplied.  '
-                           'Found: year - {0}, month - {1}').format(year, month)
+                    err = True
             except (ValueError, TypeError):
-                err = ('Invalid year and/or month supplied.  '
-                       'Found: year - {0}, month - {1}').format(year, month)
+                err = True
 
         if err:
-            raise CloudantArgumentError(err)
+            msg = ('Invalid year and/or month supplied.  '
+                   'Found: year - {0}, month - {1}').format(year, month)
+            raise CloudantArgumentError(msg)
         else:
             resp.raise_for_status()
             return resp.json()
diff --git a/src/cloudant/database.py b/src/cloudant/database.py
index bf0a25c..14a92f2 100644
--- a/src/cloudant/database.py
+++ b/src/cloudant/database.py
@@ -34,7 +34,7 @@ from .security_document import SecurityDocument
 from .view import View
 from .index import Index, TextIndex, SpecialIndex
 from .query import Query
-from .error import CloudantException, CloudantArgumentError
+from .error import CloudantArgumentError, CloudantDatabaseException
 from .result import Result, QueryResult
 from .feed import Feed, InfiniteFeed
 
@@ -158,9 +158,7 @@ class CouchDatabase(dict):
         else:
             doc = Document(self, docid)
         if throw_on_exists and doc.exists():
-            raise CloudantException(
-                'Error - Document with id {0} already exists.'.format(docid)
-            )
+            raise CloudantDatabaseException(409, docid)
         doc.update(data)
         doc.create()
         super(CouchDatabase, self).__setitem__(doc['_id'], doc)
@@ -346,11 +344,8 @@ class CouchDatabase(dict):
         if resp.status_code == 201 or resp.status_code == 202:
             return self
 
-        raise CloudantException(
-            "Unable to create database {0}: Reason: {1}".format(
-                self.database_url, resp.text
-            ),
-            code=resp.status_code
+        raise CloudantDatabaseException(
+            resp.status_code, self.database_url, resp.text
         )
 
     def delete(self):
@@ -747,10 +742,7 @@ class CouchDatabase(dict):
         try:
             ret = int(resp.text)
         except ValueError:
-            resp.status_code = 400
-            raise CloudantException(
-                'Error - Invalid Response Value: {}'.format(resp.json())
-            )
+            raise CloudantDatabaseException(400, resp.json())
 
         return ret
 
@@ -1090,7 +1082,7 @@ class CloudantDatabase(CouchDatabase):
                     **data.get('def', {})
                 ))
             else:
-                raise CloudantException('Unexpected index content: {0} found.')
+                raise CloudantDatabaseException(101, data.get('type'))
         return indexes
 
     def create_query_index(
diff --git a/src/cloudant/design_document.py b/src/cloudant/design_document.py
index 74d430d..cdca554 100644
--- a/src/cloudant/design_document.py
+++ b/src/cloudant/design_document.py
@@ -19,7 +19,7 @@ from ._2to3 import iteritems_, STRTYPE
 from ._common_util import QUERY_LANGUAGE, codify
 from .document import Document
 from .view import View, QueryIndexView
-from .error import CloudantArgumentError, CloudantException
+from .error import CloudantArgumentError, CloudantDesignDocumentException
 
 class DesignDocument(Document):
     """
@@ -285,9 +285,7 @@ class DesignDocument(Document):
             msg = "View {0} already exists in this design doc".format(view_name)
             raise CloudantArgumentError(msg)
         if self.get('language', None) == QUERY_LANGUAGE:
-            msg = ('Cannot add a MapReduce view to a '
-                   'design document for query indexes.')
-            raise CloudantException(msg)
+            raise CloudantDesignDocumentException(101)
 
         view = View(self, view_name, map_func, reduce_func, **kwargs)
         self.views.__setitem__(view_name, view)
@@ -361,8 +359,7 @@ class DesignDocument(Document):
             msg = "View {0} does not exist in this design doc".format(view_name)
             raise CloudantArgumentError(msg)
         if isinstance(view, QueryIndexView):
-            msg = 'Cannot update a query index view using this method.'
-            raise CloudantException(msg)
+            raise CloudantDesignDocumentException(102)
 
         view = View(self, view_name, map_func, reduce_func, **kwargs)
         self.views.__setitem__(view_name, view)
@@ -432,8 +429,7 @@ class DesignDocument(Document):
         if view is None:
             return
         if isinstance(view, QueryIndexView):
-            msg = 'Cannot delete a query index view using this method.'
-            raise CloudantException(msg)
+            raise CloudantDesignDocumentException(103)
 
         self.views.__delitem__(view_name)
 
@@ -517,34 +513,22 @@ class DesignDocument(Document):
             if self.get('language', None) != QUERY_LANGUAGE:
                 for view_name, view in self.iterviews():
                     if isinstance(view, QueryIndexView):
-                        msg = 'View {0} must be of type View.'.format(view_name)
-                        raise CloudantException(msg)
+                        raise CloudantDesignDocumentException(104, view_name)
             else:
                 for view_name, view in self.iterviews():
                     if not isinstance(view, QueryIndexView):
-                        msg = (
-                            'View {0} must be of type QueryIndexView.'
-                        ).format(view_name)
-                        raise CloudantException(msg)
+                        raise CloudantDesignDocumentException(105, view_name)
 
         if self.indexes:
             if self.get('language', None) != QUERY_LANGUAGE:
                 for index_name, search in self.iterindexes():
                     # Check the instance of the javascript search function
                     if not isinstance(search['index'], STRTYPE):
-                        msg = (
-                            'Function for search index {0} must '
-                            'be of type string.'
-                        ).format(index_name)
-                        raise CloudantException(msg)
+                        raise CloudantDesignDocumentException(106, index_name)
             else:
                 for index_name, index in self.iterindexes():
                     if not isinstance(index['index'], dict):
-                        msg = (
-                            'Definition for query text index {0} must '
-                            'be of type dict.'
-                        ).format(index_name)
-                        raise CloudantException(msg)
+                        raise CloudantDesignDocumentException(107, index_name)
 
         for prop in self._nested_object_names:
             if not getattr(self, prop):
diff --git a/src/cloudant/document.py b/src/cloudant/document.py
index 0492777..500acc1 100644
--- a/src/cloudant/document.py
+++ b/src/cloudant/document.py
@@ -21,7 +21,8 @@ import requests
 from requests.exceptions import HTTPError
 
 from ._2to3 import url_quote, url_quote_plus
-from .error import CloudantException
+from .error import CloudantDocumentException
+
 
 class Document(dict):
     """
@@ -161,10 +162,7 @@ class Document(dict):
         the locally cached Document object.
         """
         if self.document_url is None:
-            raise CloudantException(
-                'A document id is required to fetch document contents.  '
-                'Add an _id key and value to the document and re-try.'
-            )
+            raise CloudantDocumentException(101)
         resp = self.r_session.get(self.document_url)
         resp.raise_for_status()
         self.clear()
@@ -210,9 +208,7 @@ class Document(dict):
         if doc.get(field) is None:
             doc[field] = []
         if not isinstance(doc[field], list):
-            raise CloudantException(
-                'The field {0} is not a list.'.format(field)
-            )
+            raise CloudantDocumentException(102, field)
         if value is not None:
             doc[field].append(value)
 
@@ -227,9 +223,7 @@ class Document(dict):
         :param value: Value to remove from the field list.
         """
         if not isinstance(doc[field], list):
-            raise CloudantException(
-                'The field {0} is not a list.'.format(field)
-            )
+            raise CloudantDocumentException(102, field)
         doc[field].remove(value)
 
     @staticmethod
@@ -314,10 +308,7 @@ class Document(dict):
         object.
         """
         if not self.get("_rev"):
-            raise CloudantException(
-                "Attempting to delete a doc with no _rev. Try running "
-                ".fetch first!"
-            )
+            raise CloudantDocumentException(103)
 
         del_resp = self.r_session.delete(
             self.document_url,
diff --git a/src/cloudant/error.py b/src/cloudant/error.py
index 4769062..a09743c 100644
--- a/src/cloudant/error.py
+++ b/src/cloudant/error.py
@@ -1,5 +1,5 @@
 #!/usr/bin/env python
-# Copyright (c) 2015 IBM. All rights reserved.
+# Copyright (c) 2015, 2016 IBM. All rights reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -16,6 +16,16 @@
 Module that contains common exception classes for the Cloudant Python client
 library.
 """
+from cloudant._messages import (
+    CLIENT,
+    DATABASE,
+    DESIGN_DOCUMENT,
+    DOCUMENT,
+    FEED,
+    INDEX,
+    REPLICATOR,
+    RESULT
+)
 
 class CloudantException(Exception):
     """
@@ -61,18 +71,115 @@ class ResultException(CloudantException):
     """
 
     def __init__(self, code=100, *args):
-        messages = {
-            100:'A general result exception was raised.',
-            101:'Failed to interpret the argument {0} as a valid key value or as a valid slice.',
-            102:'Cannot use {0} when performing key access or key slicing.  Found {1}.',
-            103:'Cannot use {0} for iteration.  Found {1}.',
-            104:'Invalid page_size: {0}'
-        }
-
         try:
-            msg = messages[code].format(*args)
-        # pylint: disable=broad-except
-        except Exception:
+            msg = RESULT[code].format(*args)
+        except (KeyError, IndexError):
             code = 100
-            msg = messages[code]
+            msg = RESULT[code]
         super(ResultException, self).__init__(msg, code)
+
+
+class CloudantClientException(CloudantException):
+    """
+    Provides a way to issue Cloudant library client specific exceptions.
+
+    :param int code: A code value used to identify the client exception.
+    :param args: A list of arguments used to format the exception message.
+    """
+    def __init__(self, code=100, *args):
+        try:
+            msg = CLIENT[code].format(*args)
+        except (KeyError, IndexError):
+            code = 100
+            msg = CLIENT[code]
+        super(CloudantClientException, self).__init__(msg, code)
+
+class CloudantDatabaseException(CloudantException):
+    """
+    Provides a way to issue Cloudant library database specific exceptions.
+
+    :param int code: A code value used to identify the database exception.
+    :param args: A list of arguments used to format the exception message.
+    """
+    def __init__(self, code=100, *args):
+        try:
+            msg = DATABASE[code].format(*args)
+        except (KeyError, IndexError):
+            code = 100
+            msg = DATABASE[code]
+        super(CloudantDatabaseException, self).__init__(msg, code)
+
+class CloudantDesignDocumentException(CloudantException):
+    """
+    Provides a way to issue Cloudant library design document exceptions.
+
+    :param int code: A code value used to identify the design doc exception.
+    :param args: A list of arguments used to format the exception message.
+    """
+    def __init__(self, code=100, *args):
+        try:
+            msg = DESIGN_DOCUMENT[code].format(*args)
+        except (KeyError, IndexError):
+            code = 100
+            msg = DESIGN_DOCUMENT[code]
+        super(CloudantDesignDocumentException, self).__init__(msg, code)
+
+class CloudantDocumentException(CloudantException):
+    """
+    Provides a way to issue Cloudant library document specific exceptions.
+
+    :param int code: A code value used to identify the document exception.
+    :param args: A list of arguments used to format the exception message.
+    """
+    def __init__(self, code=100, *args):
+        try:
+            msg = DOCUMENT[code].format(*args)
+        except (KeyError, IndexError):
+            code = 100
+            msg = DOCUMENT[code]
+        super(CloudantDocumentException, self).__init__(msg, code)
+
+class CloudantFeedException(CloudantException):
+    """
+    Provides a way to issue Cloudant library feed specific exceptions.
+
+    :param int code: A code value used to identify the feed exception.
+    :param args: A list of arguments used to format the exception message.
+    """
+    def __init__(self, code=100, *args):
+        try:
+            msg = FEED[code].format(*args)
+        except (KeyError, IndexError):
+            code = 100
+            msg = FEED[code]
+        super(CloudantFeedException, self).__init__(msg, code)
+
+class CloudantIndexException(CloudantException):
+    """
+    Provides a way to issue Cloudant library index specific exceptions.
+
+    :param int code: A code value used to identify the index exception.
+    :param args: A list of arguments used to format the exception message.
+    """
+    def __init__(self, code=100, *args):
+        try:
+            msg = INDEX[code].format(*args)
+        except (KeyError, IndexError):
+            code = 100
+            msg = INDEX[code]
+        super(CloudantIndexException, self).__init__(msg, code)
+
+class CloudantReplicatorException(CloudantException):
+    """
+    Provides a way to issue Cloudant library replicator specific exceptions.
+
+    :param int code: A code value used to identify the replicator exception.
+    :param args: A list of arguments used to format the exception message.
+    """
+    def __init__(self, code=100, *args):
+        try:
+            msg = REPLICATOR[code].format(*args)
+        except (KeyError, IndexError):
+            code = 100
+            msg = REPLICATOR[code]
+        super(CloudantReplicatorException, self).__init__(msg, code)
diff --git a/src/cloudant/feed.py b/src/cloudant/feed.py
index 32b833a..a5b85fc 100644
--- a/src/cloudant/feed.py
+++ b/src/cloudant/feed.py
@@ -20,7 +20,7 @@ continuous and non-continuous feeds like ``_changes`` and ``_db_updates``.
 import json
 
 from ._2to3 import iteritems_, next_, unicode_, STRTYPE, NONETYPE
-from .error import CloudantArgumentError, CloudantException
+from .error import CloudantArgumentError, CloudantFeedException
 from ._common_util import feed_arg_types, TYPE_CONVERTERS
 
 class Feed(object):
@@ -252,8 +252,7 @@ class InfiniteFeed(Feed):
         """
         while True:
             if self._source == 'CouchDB':
-                raise CloudantException(
-                    'Infinite _db_updates feed not supported for CouchDB.')
+                raise CloudantFeedException(101)
             if self._last_seq:
                 self._options.update({'since': self._last_seq})
                 self._resp = None
diff --git a/src/cloudant/index.py b/src/cloudant/index.py
index 87b2fec..3ec3dad 100644
--- a/src/cloudant/index.py
+++ b/src/cloudant/index.py
@@ -24,7 +24,7 @@ from ._common_util import JSON_INDEX_TYPE
 from ._common_util import TEXT_INDEX_TYPE
 from ._common_util import SPECIAL_INDEX_TYPE
 from ._common_util import TEXT_INDEX_ARGS
-from .error import CloudantArgumentError, CloudantException
+from .error import CloudantArgumentError, CloudantIndexException
 
 class Index(object):
     """
@@ -252,13 +252,11 @@ class SpecialIndex(Index):
         A "special" index cannot be created.  This method is disabled for a
         SpecialIndex object.
         """
-        msg = 'Creating the \"special\" index is not allowed.'
-        raise CloudantException(msg)
+        raise CloudantIndexException(101)
 
     def delete(self):
         """
         A "special" index cannot be deleted.  This method is disabled for a
         SpecialIndex object.
         """
-        msg = 'Deleting the \"special\" index is not allowed.'
-        raise CloudantException(msg)
+        raise CloudantIndexException(102)
diff --git a/src/cloudant/replicator.py b/src/cloudant/replicator.py
index c858ec2..06e91b0 100644
--- a/src/cloudant/replicator.py
+++ b/src/cloudant/replicator.py
@@ -18,7 +18,7 @@ API module/class for handling database replications
 
 import uuid
 
-from .error import CloudantException
+from .error import CloudantReplicatorException, CloudantClientException
 from .document import Document
 
 class Replicator(object):
@@ -33,13 +33,11 @@ class Replicator(object):
     """
 
     def __init__(self, client):
+        repl_db = '_replicator'
         try:
-            self.database = client['_replicator']
+            self.database = client[repl_db]
         except Exception:
-            raise CloudantException(
-                'Unable to acquire _replicator database.  '
-                'Verify that the client is valid and try again.'
-            )
+            raise CloudantClientException(404, repl_db)
 
     def create_replication(self, source_db=None, target_db=None,
                            repl_id=None, **kwargs):
@@ -78,10 +76,7 @@ class Replicator(object):
 
         if not data.get('source'):
             if source_db is None:
-                raise CloudantException(
-                    'You must specify either a source_db Database '
-                    'object or a manually composed \'source\' string/dict.'
-                )
+                raise CloudantReplicatorException(101)
             data['source'] = {'url': source_db.database_url}
             if not source_db.admin_party:
                 data['source'].update(
@@ -90,10 +85,7 @@ class Replicator(object):
 
         if not data.get('target'):
             if target_db is None:
-                raise CloudantException(
-                    'You must specify either a target_db Database '
-                    'object or a manually composed \'target\' string/dict.'
-                )
+                raise CloudantReplicatorException(102)
             data['target'] = {'url': target_db.database_url}
             if not target_db.admin_party:
                 data['target'].update(
@@ -136,9 +128,7 @@ class Replicator(object):
         try:
             repl_doc = self.database[repl_id]
         except KeyError:
-            raise CloudantException(
-                "Replication {} not found".format(repl_id)
-            )
+            raise CloudantReplicatorException(404, repl_id)
         repl_doc.fetch()
         return repl_doc.get('_replication_state')
 
@@ -203,8 +193,7 @@ class Replicator(object):
         try:
             repl_doc = self.database[repl_id]
         except KeyError:
-            raise CloudantException(
-                "Could not find replication with id {}".format(repl_id))
+            raise CloudantReplicatorException(404, repl_id)
 
         repl_doc.fetch()
         repl_doc.delete()

Improve CloudantException design
Using the ResultException class as a guide, we can improve the error handling design by 1) have specific class exceptions for many or all of the modules (i.e. `CloudantClientException` and 2) move the error messages from `error.py` and the other modules to an internal `messages.py` module to reduce clutter.

After a discussion with @alfinkel, will follow these guidelines:
(1) try and preserve Python `dict` behavior
(2) only throwing Cloudant exceptions when trying to communicate errors encountered pre-remote database interaction 


diff --git a/tests/unit/client_tests.py b/tests/unit/client_tests.py
index 49a60ac..8ec4286 100644
--- a/tests/unit/client_tests.py
+++ b/tests/unit/client_tests.py
@@ -30,13 +30,51 @@ import datetime
 
 from cloudant import cloudant, couchdb, couchdb_admin_party
 from cloudant.client import Cloudant, CouchDB
-from cloudant.error import CloudantException, CloudantArgumentError
+from cloudant.error import CloudantArgumentError, CloudantClientException
 from cloudant.feed import Feed, InfiniteFeed
 from cloudant._common_util import InfiniteSession
 
 from .unit_t_db_base import UnitTestDbBase
 from .. import bytes_, str_
 
+class CloudantClientExceptionTests(unittest.TestCase):
+    """
+    Ensure CloudantClientException functions as expected.
+    """
+
+    def test_raise_without_code(self):
+        """
+        Ensure that a default exception/code is used if none is provided.
+        """
+        with self.assertRaises(CloudantClientException) as cm:
+            raise CloudantClientException()
+        self.assertEqual(cm.exception.status_code, 100)
+
+    def test_raise_using_invalid_code(self):
+        """
+        Ensure that a default exception/code is used if invalid code is provided.
+        """
+        with self.assertRaises(CloudantClientException) as cm:
+            raise CloudantClientException('foo')
+        self.assertEqual(cm.exception.status_code, 100)
+
+    def test_raise_without_args(self):
+        """
+        Ensure that a default exception/code is used if the message requested
+        by the code provided requires an argument list and none is provided.
+        """
+        with self.assertRaises(CloudantClientException) as cm:
+            raise CloudantClientException(404)
+        self.assertEqual(cm.exception.status_code, 100)
+
+    def test_raise_with_proper_code_and_args(self):
+        """
+        Ensure that the requested exception is raised.
+        """
+        with self.assertRaises(CloudantClientException) as cm:
+            raise CloudantClientException(404, 'foo')
+        self.assertEqual(cm.exception.status_code, 404)
+
 class ClientTests(UnitTestDbBase):
     """
     CouchDB/Cloudant client unit tests
@@ -233,19 +271,14 @@ class ClientTests(UnitTestDbBase):
         Test creation of already existing database
         """
         dbname = self.dbname()
-        try:
-            self.client.connect()
-            self.client.create_database(dbname)
+        self.client.connect()
+        self.client.create_database(dbname)
+        with self.assertRaises(CloudantClientException) as cm:
             self.client.create_database(dbname, throw_on_exists=True)
-            self.fail('Above statement should raise a CloudantException')
-        except CloudantException as err:
-            self.assertEqual(
-                str(err),
-                'Database {0} already exists'.format(dbname)
-                )
-        finally:
-            self.client.delete_database(dbname)
-            self.client.disconnect()
+        self.assertEqual(cm.exception.status_code, 409)
+
+        self.client.delete_database(dbname)
+        self.client.disconnect()
 
     def test_delete_non_existing_database(self):
         """
@@ -255,8 +288,9 @@ class ClientTests(UnitTestDbBase):
             self.client.connect()
             self.client.delete_database('no_such_db')
             self.fail('Above statement should raise a CloudantException')
-        except CloudantException as err:
-            self.assertEqual(str(err), 'Database no_such_db does not exist')
+        except CloudantClientException as err:
+            self.assertEqual(str(err), 'Database no_such_db does not exist. '
+                                       'Verify that the client is valid and try again.')
         finally:
             self.client.disconnect()
 
@@ -396,8 +430,10 @@ class ClientTests(UnitTestDbBase):
             self.client.connect()
             self.client['not-a-db'] = 'This is not a database object'
             self.fail('Above statement should raise a CloudantException')
-        except CloudantException as err:
-            self.assertEqual(str(err), 'Value must be set to a Database object')
+        except CloudantClientException as err:
+            self.assertEqual(
+                str(err),
+                'Value must be set to a Database object. Found type: str.')
         finally:
             self.client.disconnect()
 
diff --git a/tests/unit/database_tests.py b/tests/unit/database_tests.py
index afc6734..a6c0f4e 100644
--- a/tests/unit/database_tests.py
+++ b/tests/unit/database_tests.py
@@ -30,7 +30,7 @@ import os
 import uuid
 
 from cloudant.result import Result, QueryResult
-from cloudant.error import CloudantException, CloudantArgumentError
+from cloudant.error import CloudantArgumentError, CloudantDatabaseException
 from cloudant.document import Document
 from cloudant.design_document import DesignDocument
 from cloudant.security_document import SecurityDocument
@@ -40,6 +40,44 @@ from cloudant.feed import Feed, InfiniteFeed
 from .unit_t_db_base import UnitTestDbBase
 from .. import unicode_
 
+class CloudantDatabaseExceptionTests(unittest.TestCase):
+    """
+    Ensure CloudantDatabaseException functions as expected.
+    """
+
+    def test_raise_without_code(self):
+        """
+        Ensure that a default exception/code is used if none is provided.
+        """
+        with self.assertRaises(CloudantDatabaseException) as cm:
+            raise CloudantDatabaseException()
+        self.assertEqual(cm.exception.status_code, 100)
+
+    def test_raise_using_invalid_code(self):
+        """
+        Ensure that a default exception/code is used if invalid code is provided.
+        """
+        with self.assertRaises(CloudantDatabaseException) as cm:
+            raise CloudantDatabaseException('foo')
+        self.assertEqual(cm.exception.status_code, 100)
+
+    def test_raise_without_args(self):
+        """
+        Ensure that a default exception/code is used if the message requested
+        by the code provided requires an argument list and none is provided.
+        """
+        with self.assertRaises(CloudantDatabaseException) as cm:
+            raise CloudantDatabaseException(400)
+        self.assertEqual(cm.exception.status_code, 100)
+
+    def test_raise_with_proper_code_and_args(self):
+        """
+        Ensure that the requested exception is raised.
+        """
+        with self.assertRaises(CloudantDatabaseException) as cm:
+            raise CloudantDatabaseException(400, 'foo')
+        self.assertEqual(cm.exception.status_code, 400)
+
 class DatabaseTests(UnitTestDbBase):
     """
     CouchDatabase/CloudantDatabase unit tests
@@ -217,10 +255,10 @@ class DatabaseTests(UnitTestDbBase):
         try:
             self.db.create_document(data, throw_on_exists=True)
             self.fail('Above statement should raise a CloudantException')
-        except CloudantException as err:
+        except CloudantDatabaseException as err:
             self.assertEqual(
                 str(err),
-                'Error - Document with id julia06 already exists.'
+                'Document with id julia06 already exists.'
                 )
 
     def test_create_document_without_id(self):
diff --git a/tests/unit/design_document_tests.py b/tests/unit/design_document_tests.py
index c0f25d9..196a11c 100644
--- a/tests/unit/design_document_tests.py
+++ b/tests/unit/design_document_tests.py
@@ -30,10 +30,48 @@ import requests
 from cloudant.document import Document 
 from cloudant.design_document import DesignDocument
 from cloudant.view import View, QueryIndexView
-from cloudant.error import CloudantArgumentError, CloudantException
+from cloudant.error import CloudantArgumentError, CloudantDesignDocumentException
 
 from .unit_t_db_base import UnitTestDbBase
 
+class CloudantDesignDocumentExceptionTests(unittest.TestCase):
+    """
+    Ensure CloudantDesignDocumentException functions as expected.
+    """
+
+    def test_raise_without_code(self):
+        """
+        Ensure that a default exception/code is used if none is provided.
+        """
+        with self.assertRaises(CloudantDesignDocumentException) as cm:
+            raise CloudantDesignDocumentException()
+        self.assertEqual(cm.exception.status_code, 100)
+
+    def test_raise_using_invalid_code(self):
+        """
+        Ensure that a default exception/code is used if invalid code is provided.
+        """
+        with self.assertRaises(CloudantDesignDocumentException) as cm:
+            raise CloudantDesignDocumentException('foo')
+        self.assertEqual(cm.exception.status_code, 100)
+
+    def test_raise_without_args(self):
+        """
+        Ensure that a default exception/code is used if the message requested
+        by the code provided requires an argument list and none is provided.
+        """
+        with self.assertRaises(CloudantDesignDocumentException) as cm:
+            raise CloudantDesignDocumentException(104)
+        self.assertEqual(cm.exception.status_code, 100)
+
+    def test_raise_with_proper_code_and_args(self):
+        """
+        Ensure that the requested exception is raised.
+        """
+        with self.assertRaises(CloudantDesignDocumentException) as cm:
+            raise CloudantDesignDocumentException(104, 'foo')
+        self.assertEqual(cm.exception.status_code, 104)
+
 class DesignDocumentTests(UnitTestDbBase):
     """
     DesignDocument unit tests
@@ -173,7 +211,7 @@ class DesignDocumentTests(UnitTestDbBase):
         """
         ddoc = DesignDocument(self.db, '_design/ddoc001')
         ddoc['language'] = 'query'
-        with self.assertRaises(CloudantException) as cm:
+        with self.assertRaises(CloudantDesignDocumentException) as cm:
             ddoc.add_view('view001', {'foo': 'bar'})
         err = cm.exception
         self.assertEqual(
@@ -239,7 +277,7 @@ class DesignDocumentTests(UnitTestDbBase):
         self.db.create_document(data)
         ddoc = DesignDocument(self.db, '_design/ddoc001')
         ddoc.fetch()
-        with self.assertRaises(CloudantException) as cm:
+        with self.assertRaises(CloudantDesignDocumentException) as cm:
             ddoc.update_view(
                 'view001',
                 'function (doc) {\n  emit(doc._id, 1);\n}'
@@ -283,7 +321,7 @@ class DesignDocumentTests(UnitTestDbBase):
         self.db.create_document(data)
         ddoc = DesignDocument(self.db, '_design/ddoc001')
         ddoc.fetch()
-        with self.assertRaises(CloudantException) as cm:
+        with self.assertRaises(CloudantDesignDocumentException) as cm:
             ddoc.delete_view('view001')
         err = cm.exception
         self.assertEqual(
@@ -504,7 +542,7 @@ class DesignDocumentTests(UnitTestDbBase):
             'analyzer': {'name': 'perfield','default': 'keyword',
                          'fields': {'$default': 'german'}}}
         self.assertIsInstance(ddoc['indexes']['index001']['index'], dict)
-        with self.assertRaises(CloudantException) as cm:
+        with self.assertRaises(CloudantDesignDocumentException) as cm:
             ddoc.save()
         err = cm.exception
         self.assertEqual(
@@ -539,7 +577,7 @@ class DesignDocumentTests(UnitTestDbBase):
             'analyzer': {'name': 'perfield','default': 'keyword',
                          'fields': {'$default': 'german'}}}
         self.assertIsInstance(ddoc_remote['indexes']['index001']['index'], dict)
-        with self.assertRaises(CloudantException) as cm:
+        with self.assertRaises(CloudantDesignDocumentException) as cm:
             ddoc_remote.save()
         err = cm.exception
         self.assertEqual(
@@ -558,7 +596,7 @@ class DesignDocumentTests(UnitTestDbBase):
         db_copy = '{0}-copy'.format(self.db.database_name)
         ddoc.add_view('view001', view_map, view_reduce)
         ddoc['language'] = 'query'
-        with self.assertRaises(CloudantException) as cm:
+        with self.assertRaises(CloudantDesignDocumentException) as cm:
             ddoc.save()
         err = cm.exception
         self.assertEqual(
@@ -600,13 +638,13 @@ class DesignDocumentTests(UnitTestDbBase):
         self.db.create_document(data)
         ddoc = DesignDocument(self.db, '_design/ddoc001')
         ddoc.fetch()
-        with self.assertRaises(CloudantException) as cm:
+        with self.assertRaises(CloudantDesignDocumentException) as cm:
             ddoc['language'] = 'not-query'
             ddoc.save()
         err = cm.exception
         self.assertEqual(str(err), 'View view001 must be of type View.')
 
-        with self.assertRaises(CloudantException) as cm:
+        with self.assertRaises(CloudantDesignDocumentException) as cm:
             del ddoc['language']
             ddoc.save()
         err = cm.exception
@@ -1043,7 +1081,7 @@ class DesignDocumentTests(UnitTestDbBase):
                      '{"store": true}); }\n}',
             'analyzer': 'standard'}
         self.assertIsInstance(ddoc['indexes']['search001']['index'], str)
-        with self.assertRaises(CloudantException) as cm:
+        with self.assertRaises(CloudantDesignDocumentException) as cm:
             ddoc.save()
         err = cm.exception
         self.assertEqual(
@@ -1076,7 +1114,7 @@ class DesignDocumentTests(UnitTestDbBase):
         self.assertIsInstance(
             ddoc['indexes']['search001']['index'], str
         )
-        with self.assertRaises(CloudantException) as cm:
+        with self.assertRaises(CloudantDesignDocumentException) as cm:
             ddoc.save()
         err = cm.exception
         self.assertEqual(
diff --git a/tests/unit/document_tests.py b/tests/unit/document_tests.py
index 4aaa57f..2d76389 100644
--- a/tests/unit/document_tests.py
+++ b/tests/unit/document_tests.py
@@ -32,7 +32,7 @@ import uuid
 import inspect
 
 from cloudant.document import Document
-from cloudant.error import CloudantException
+from cloudant.error import CloudantDocumentException
 
 from .. import StringIO, unicode_
 from .unit_t_db_base import UnitTestDbBase
@@ -43,6 +43,44 @@ def find_fixture(name):
     filename = os.path.join(dirname, name)
     return filename
 
+class CloudantDocumentExceptionTests(unittest.TestCase):
+    """
+    Ensure CloudantDocumentException functions as expected.
+    """
+
+    def test_raise_without_code(self):
+        """
+        Ensure that a default exception/code is used if none is provided.
+        """
+        with self.assertRaises(CloudantDocumentException) as cm:
+            raise CloudantDocumentException()
+        self.assertEqual(cm.exception.status_code, 100)
+
+    def test_raise_using_invalid_code(self):
+        """
+        Ensure that a default exception/code is used if invalid code is provided.
+        """
+        with self.assertRaises(CloudantDocumentException) as cm:
+            raise CloudantDocumentException('foo')
+        self.assertEqual(cm.exception.status_code, 100)
+
+    def test_raise_without_args(self):
+        """
+        Ensure that a default exception/code is used if the message requested
+        by the code provided requires an argument list and none is provided.
+        """
+        with self.assertRaises(CloudantDocumentException) as cm:
+            raise CloudantDocumentException(102)
+        self.assertEqual(cm.exception.status_code, 100)
+
+    def test_raise_with_proper_code_and_args(self):
+        """
+        Ensure that the requested exception is raised.
+        """
+        with self.assertRaises(CloudantDocumentException) as cm:
+            raise CloudantDocumentException(102, 'foo')
+        self.assertEqual(cm.exception.status_code, 102)
+
 class DocumentTests(UnitTestDbBase):
     """
     Document unit tests
@@ -230,10 +268,10 @@ class DocumentTests(UnitTestDbBase):
         try:
             doc.fetch()
             self.fail('Above statement should raise an Exception')
-        except CloudantException as err:
+        except CloudantDocumentException as err:
             self.assertEqual(
                 str(err),
-                'A document id is required to fetch document contents.  '
+                'A document id is required to fetch document contents. '
                 'Add an _id key and value to the document and re-try.'
             )
 
@@ -382,7 +420,7 @@ class DocumentTests(UnitTestDbBase):
         try:
             doc.list_field_append(doc, 'name', 'isabel')
             self.fail('Above statement should raise an Exception')
-        except CloudantException as err:
+        except CloudantDocumentException as err:
             self.assertEqual(str(err), 'The field name is not a list.')
         self.assertEqual(doc, {'name': 'julia'})
 
@@ -408,7 +446,7 @@ class DocumentTests(UnitTestDbBase):
         try:
             doc.list_field_remove(doc, 'name', 'julia')
             self.fail('Above statement should raise an Exception')
-        except CloudantException as err:
+        except CloudantDocumentException as err:
             self.assertEqual(str(err), 'The field name is not a list.')
         self.assertEqual(doc, {'name': 'julia'})
 
@@ -455,11 +493,11 @@ class DocumentTests(UnitTestDbBase):
         try:
             doc.delete()
             self.fail('Above statement should raise an Exception')
-        except CloudantException as err:
+        except CloudantDocumentException as err:
             self.assertEqual(
                 str(err), 
                 'Attempting to delete a doc with no _rev. '
-                'Try running .fetch first!'
+                'Try running .fetch and re-try.'
             )
 
     def test_delete_document_success(self):
diff --git a/tests/unit/index_tests.py b/tests/unit/index_tests.py
index 6f316f4..398e308 100644
--- a/tests/unit/index_tests.py
+++ b/tests/unit/index_tests.py
@@ -33,11 +33,40 @@ from cloudant.query import Query
 from cloudant.view import QueryIndexView
 from cloudant.design_document import DesignDocument
 from cloudant.document import Document
-from cloudant.error import CloudantArgumentError, CloudantException
+from cloudant.error import CloudantArgumentError, CloudantIndexException
 
 from .. import PY2
 from .unit_t_db_base import UnitTestDbBase
 
+class CloudantIndexExceptionTests(unittest.TestCase):
+    """
+    Ensure CloudantIndexException functions as expected.
+    """
+
+    def test_raise_without_code(self):
+        """
+        Ensure that a default exception/code is used if none is provided.
+        """
+        with self.assertRaises(CloudantIndexException) as cm:
+            raise CloudantIndexException()
+        self.assertEqual(cm.exception.status_code, 100)
+
+    def test_raise_using_invalid_code(self):
+        """
+        Ensure that a default exception/code is used if invalid code is provided.
+        """
+        with self.assertRaises(CloudantIndexException) as cm:
+            raise CloudantIndexException('foo')
+        self.assertEqual(cm.exception.status_code, 100)
+
+    def test_raise_with_proper_code_and_args(self):
+        """
+        Ensure that the requested exception is raised.
+        """
+        with self.assertRaises(CloudantIndexException) as cm:
+            raise CloudantIndexException(101)
+        self.assertEqual(cm.exception.status_code, 101)
+
 @unittest.skipUnless(
     os.environ.get('RUN_CLOUDANT_TESTS') is not None,
     'Skipping Cloudant Index tests'
@@ -591,7 +620,7 @@ class SpecialIndexTests(unittest.TestCase):
         Test that the SpecialIndex create method is disabled.
         """
         index = SpecialIndex(self.db, fields=[{'_id': 'asc'}])
-        with self.assertRaises(CloudantException) as cm:
+        with self.assertRaises(CloudantIndexException) as cm:
             index.create()
         err = cm.exception
         self.assertEqual(
@@ -604,7 +633,7 @@ class SpecialIndexTests(unittest.TestCase):
         Test that the SpecialIndex delete method is disabled.
         """
         index = SpecialIndex(self.db, fields=[{'_id': 'asc'}])
-        with self.assertRaises(CloudantException) as cm:
+        with self.assertRaises(CloudantIndexException) as cm:
             index.delete()
         err = cm.exception
         self.assertEqual(
diff --git a/tests/unit/infinite_feed_tests.py b/tests/unit/infinite_feed_tests.py
index 5092f9a..f7b2ee4 100644
--- a/tests/unit/infinite_feed_tests.py
+++ b/tests/unit/infinite_feed_tests.py
@@ -24,7 +24,7 @@ from time import sleep
 
 from cloudant.feed import InfiniteFeed, Feed
 from cloudant.client import CouchDB
-from cloudant.error import CloudantException, CloudantArgumentError
+from cloudant.error import CloudantArgumentError, CloudantFeedException
 
 from .unit_t_db_base import UnitTestDbBase
 
@@ -42,6 +42,35 @@ class MethodCallCount(object):
         self.called_count += 1
         self._ref()
 
+class CloudantFeedExceptionTests(unittest.TestCase):
+    """
+    Ensure CloudantFeedException functions as expected.
+    """
+
+    def test_raise_without_code(self):
+        """
+        Ensure that a default exception/code is used if none is provided.
+        """
+        with self.assertRaises(CloudantFeedException) as cm:
+            raise CloudantFeedException()
+        self.assertEqual(cm.exception.status_code, 100)
+
+    def test_raise_using_invalid_code(self):
+        """
+        Ensure that a default exception/code is used if invalid code is provided.
+        """
+        with self.assertRaises(CloudantFeedException) as cm:
+            raise CloudantFeedException('foo')
+        self.assertEqual(cm.exception.status_code, 100)
+
+    def test_raise_with_proper_code_and_args(self):
+        """
+        Ensure that the requested exception is raised.
+        """
+        with self.assertRaises(CloudantFeedException) as cm:
+            raise CloudantFeedException(101)
+        self.assertEqual(cm.exception.status_code, 101)
+
 class InfiniteFeedTests(UnitTestDbBase):
     """
     Infinite Feed unit tests
@@ -101,7 +130,7 @@ class InfiniteFeedTests(UnitTestDbBase):
         """
         Ensure that a CouchDB client cannot be used with an infinite feed.
         """
-        with self.assertRaises(CloudantException) as cm:
+        with self.assertRaises(CloudantFeedException) as cm:
             invalid_feed = [x for x in InfiniteFeed(self.client)]
         self.assertEqual(str(cm.exception),
             'Infinite _db_updates feed not supported for CouchDB.')
diff --git a/tests/unit/replicator_tests.py b/tests/unit/replicator_tests.py
index 2cbcb4c..d5f041d 100644
--- a/tests/unit/replicator_tests.py
+++ b/tests/unit/replicator_tests.py
@@ -29,11 +29,49 @@ import requests
 
 from cloudant.replicator import Replicator
 from cloudant.document import Document
-from cloudant.error import CloudantException
+from cloudant.error import CloudantReplicatorException, CloudantClientException
 
 from .unit_t_db_base import UnitTestDbBase
 from .. import unicode_
 
+class CloudantReplicatorExceptionTests(unittest.TestCase):
+    """
+    Ensure CloudantReplicatorException functions as expected.
+    """
+
+    def test_raise_without_code(self):
+        """
+        Ensure that a default exception/code is used if none is provided.
+        """
+        with self.assertRaises(CloudantReplicatorException) as cm:
+            raise CloudantReplicatorException()
+        self.assertEqual(cm.exception.status_code, 100)
+
+    def test_raise_using_invalid_code(self):
+        """
+        Ensure that a default exception/code is used if invalid code is provided.
+        """
+        with self.assertRaises(CloudantReplicatorException) as cm:
+            raise CloudantReplicatorException('foo')
+        self.assertEqual(cm.exception.status_code, 100)
+
+    def test_raise_without_args(self):
+        """
+        Ensure that a default exception/code is used if the message requested
+        by the code provided requires an argument list and none is provided.
+        """
+        with self.assertRaises(CloudantReplicatorException) as cm:
+            raise CloudantReplicatorException(404)
+        self.assertEqual(cm.exception.status_code, 100)
+
+    def test_raise_with_proper_code_and_args(self):
+        """
+        Ensure that the requested exception is raised.
+        """
+        with self.assertRaises(CloudantReplicatorException) as cm:
+            raise CloudantReplicatorException(404, 'foo')
+        self.assertEqual(cm.exception.status_code, 404)
+
 class ReplicatorTests(UnitTestDbBase):
     """
     Replicator unit tests
@@ -102,10 +140,10 @@ class ReplicatorTests(UnitTestDbBase):
             self.client.disconnect()
             repl = Replicator(self.client)
             self.fail('Above statement should raise a CloudantException')
-        except CloudantException as err:
+        except CloudantClientException as err:
             self.assertEqual(
                 str(err),
-                'Unable to acquire _replicator database.  '
+                'Database _replicator does not exist. '
                 'Verify that the client is valid and try again.'
             )
         finally:
@@ -177,7 +215,7 @@ class ReplicatorTests(UnitTestDbBase):
         try:
             repl_doc = self.replicator.create_replication()
             self.fail('Above statement should raise a CloudantException')
-        except CloudantException as err:
+        except CloudantReplicatorException as err:
             self.assertEqual(
                 str(err),
                 'You must specify either a source_db Database '
@@ -192,7 +230,7 @@ class ReplicatorTests(UnitTestDbBase):
         try:
             repl_doc = self.replicator.create_replication(self.db)
             self.fail('Above statement should raise a CloudantException')
-        except CloudantException as err:
+        except CloudantReplicatorException as err:
             self.assertEqual(
                 str(err),
                 'You must specify either a target_db Database '
@@ -252,10 +290,10 @@ class ReplicatorTests(UnitTestDbBase):
         try:
             self.replicator.replication_state(repl_id)
             self.fail('Above statement should raise a CloudantException')
-        except CloudantException as err:
+        except CloudantReplicatorException as err:
             self.assertEqual(
                 str(err),
-                'Replication {} not found'.format(repl_id)
+                'Replication with id {} not found.'.format(repl_id)
             )
             self.assertIsNone(repl_state)
 
@@ -288,10 +326,10 @@ class ReplicatorTests(UnitTestDbBase):
         try:
             self.replicator.stop_replication(repl_id)
             self.fail('Above statement should raise a CloudantException')
-        except CloudantException as err:
+        except CloudantReplicatorException as err:
             self.assertEqual(
                 str(err),
-                'Could not find replication with id {}'.format(repl_id)
+                'Replication with id {} not found.'.format(repl_id)
             )
 
     def test_follow_replication(self):

pip install -e .[dev]
parse_log_pytest
requirements.txt
nose pytest
3.5
requirements.txt test-requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.13
astroid==2.11.7
attrs==22.2.0
Babel==2.11.0
certifi==2021.5.30
charset-normalizer==2.0.12
-e git+https://github.com/cloudant/python-cloudant.git@bdd3a694d392e06d35ec381c164d1a155e435cd2#egg=cloudant
dill==0.3.4
docutils==0.18.1
idna==3.10
imagesize==1.4.1
importlib-metadata==4.8.3
iniconfig==1.1.1
isort==5.10.1
Jinja2==3.0.3
lazy-object-proxy==1.7.1
MarkupSafe==2.0.1
mccabe==0.7.0
mock==1.3.0
nose==1.3.7
packaging==21.3
pbr==6.1.1
platformdirs==2.4.0
pluggy==1.0.0
py==1.11.0
Pygments==2.14.0
pylint==2.13.9
pyparsing==3.1.4
pytest==7.0.1
pytz==2025.2
requests==2.27.1
six==1.17.0
snowballstemmer==2.2.0
Sphinx==5.3.0
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.0
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
tomli==1.2.3
typed-ast==1.5.5
typing_extensions==4.1.1
urllib3==1.26.20
wrapt==1.16.0
zipp==3.6.0

name: python-cloudant
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.13
      - astroid==2.11.7
      - attrs==22.2.0
      - babel==2.11.0
      - charset-normalizer==2.0.12
      - dill==0.3.4
      - docutils==0.18.1
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - isort==5.10.1
      - jinja2==3.0.3
      - lazy-object-proxy==1.7.1
      - markupsafe==2.0.1
      - mccabe==0.7.0
      - mock==1.3.0
      - nose==1.3.7
      - packaging==21.3
      - pbr==6.1.1
      - platformdirs==2.4.0
      - pluggy==1.0.0
      - py==1.11.0
      - pygments==2.14.0
      - pylint==2.13.9
      - pyparsing==3.1.4
      - pytest==7.0.1
      - pytz==2025.2
      - requests==2.27.1
      - six==1.17.0
      - snowballstemmer==2.2.0
      - sphinx==5.3.0
      - sphinxcontrib-applehelp==1.0.2
      - sphinxcontrib-devhelp==1.0.2
      - sphinxcontrib-htmlhelp==2.0.0
      - sphinxcontrib-jsmath==1.0.1
      - sphinxcontrib-qthelp==1.0.3
      - sphinxcontrib-serializinghtml==1.1.5
      - tomli==1.2.3
      - typed-ast==1.5.5
      - typing-extensions==4.1.1
      - urllib3==1.26.20
      - wrapt==1.16.0
      - zipp==3.6.0
prefix: /opt/conda/envs/python-cloudant

tests/unit/client_tests.py::CloudantClientExceptionTests::test_raise_using_invalid_code tests/unit/client_tests.py::CloudantClientExceptionTests::test_raise_with_proper_code_and_args tests/unit/client_tests.py::CloudantClientExceptionTests::test_raise_without_args tests/unit/client_tests.py::CloudantClientExceptionTests::test_raise_without_code tests/unit/database_tests.py::CloudantDatabaseExceptionTests::test_raise_using_invalid_code tests/unit/database_tests.py::CloudantDatabaseExceptionTests::test_raise_with_proper_code_and_args tests/unit/database_tests.py::CloudantDatabaseExceptionTests::test_raise_without_args tests/unit/database_tests.py::CloudantDatabaseExceptionTests::test_raise_without_code tests/unit/design_document_tests.py::CloudantDesignDocumentExceptionTests::test_raise_using_invalid_code tests/unit/design_document_tests.py::CloudantDesignDocumentExceptionTests::test_raise_with_proper_code_and_args tests/unit/design_document_tests.py::CloudantDesignDocumentExceptionTests::test_raise_without_args tests/unit/design_document_tests.py::CloudantDesignDocumentExceptionTests::test_raise_without_code tests/unit/document_tests.py::CloudantDocumentExceptionTests::test_raise_using_invalid_code tests/unit/document_tests.py::CloudantDocumentExceptionTests::test_raise_with_proper_code_and_args tests/unit/document_tests.py::CloudantDocumentExceptionTests::test_raise_without_args tests/unit/document_tests.py::CloudantDocumentExceptionTests::test_raise_without_code tests/unit/index_tests.py::CloudantIndexExceptionTests::test_raise_using_invalid_code tests/unit/index_tests.py::CloudantIndexExceptionTests::test_raise_with_proper_code_and_args tests/unit/index_tests.py::CloudantIndexExceptionTests::test_raise_without_code tests/unit/index_tests.py::SpecialIndexTests::test_constructor tests/unit/index_tests.py::SpecialIndexTests::test_create_disabled tests/unit/index_tests.py::SpecialIndexTests::test_delete_disabled tests/unit/infinite_feed_tests.py::CloudantFeedExceptionTests::test_raise_using_invalid_code tests/unit/infinite_feed_tests.py::CloudantFeedExceptionTests::test_raise_with_proper_code_and_args tests/unit/infinite_feed_tests.py::CloudantFeedExceptionTests::test_raise_without_code tests/unit/replicator_tests.py::CloudantReplicatorExceptionTests::test_raise_using_invalid_code tests/unit/replicator_tests.py::CloudantReplicatorExceptionTests::test_raise_with_proper_code_and_args tests/unit/replicator_tests.py::CloudantReplicatorExceptionTests::test_raise_without_args tests/unit/replicator_tests.py::CloudantReplicatorExceptionTests::test_raise_without_code
tests/unit/database_tests.py::DatabaseTests::test_view_clean_up tests/unit/design_document_tests.py::DesignDocumentTests::test_update_validator tests/unit/document_tests.py::DocumentTests::test_update_field tests/unit/infinite_feed_tests.py::InfiniteFeedTests::test_invalid_source_couchdb tests/unit/replicator_tests.py::ReplicatorTests::test_stop_replication_using_invalid_id tests/unit/database_tests.py::DatabaseTests::test_all_docs_get tests/unit/database_tests.py::DatabaseTests::test_all_docs_post tests/unit/database_tests.py::DatabaseTests::test_all_docs_post_multiple_params tests/unit/database_tests.py::DatabaseTests::test_bulk_docs_creation tests/unit/database_tests.py::DatabaseTests::test_bulk_docs_update tests/unit/database_tests.py::DatabaseTests::test_bulk_docs_uses_custom_encoder tests/unit/database_tests.py::DatabaseTests::test_changes_feed_call tests/unit/database_tests.py::DatabaseTests::test_changes_inifinite_feed_call tests/unit/database_tests.py::DatabaseTests::test_constructor tests/unit/database_tests.py::DatabaseTests::test_create_db_delete_db tests/unit/database_tests.py::DatabaseTests::test_create_design_document tests/unit/database_tests.py::DatabaseTests::test_create_doc_with_update_handler tests/unit/database_tests.py::DatabaseTests::test_create_document_with_id tests/unit/database_tests.py::DatabaseTests::test_create_document_without_id tests/unit/database_tests.py::DatabaseTests::test_create_empty_document tests/unit/database_tests.py::DatabaseTests::test_custom_result_context_manager tests/unit/database_tests.py::DatabaseTests::test_database_request_fails_after_client_disconnects tests/unit/database_tests.py::DatabaseTests::test_delete_exception tests/unit/database_tests.py::DatabaseTests::test_document_iteration_over_fetch_limit tests/unit/database_tests.py::DatabaseTests::test_document_iteration_returns_valid_documents tests/unit/database_tests.py::DatabaseTests::test_document_iteration_under_fetch_limit tests/unit/database_tests.py::DatabaseTests::test_exists tests/unit/database_tests.py::DatabaseTests::test_exists_raises_httperror tests/unit/database_tests.py::DatabaseTests::test_get_db_via_getitem tests/unit/database_tests.py::DatabaseTests::test_get_list_function_result tests/unit/database_tests.py::DatabaseTests::test_get_list_function_result_with_invalid_argument tests/unit/database_tests.py::DatabaseTests::test_get_non_existing_doc_via_getitem tests/unit/database_tests.py::DatabaseTests::test_get_security_document tests/unit/database_tests.py::DatabaseTests::test_get_set_revision_limit tests/unit/database_tests.py::DatabaseTests::test_get_show_result tests/unit/database_tests.py::DatabaseTests::test_keys tests/unit/database_tests.py::DatabaseTests::test_missing_revisions tests/unit/database_tests.py::DatabaseTests::test_missing_revisions_uses_custom_encoder tests/unit/database_tests.py::DatabaseTests::test_retrieve_creds tests/unit/database_tests.py::DatabaseTests::test_retrieve_db_metadata tests/unit/database_tests.py::DatabaseTests::test_retrieve_db_url tests/unit/database_tests.py::DatabaseTests::test_retrieve_design_document tests/unit/database_tests.py::DatabaseTests::test_retrieve_design_document_list tests/unit/database_tests.py::DatabaseTests::test_retrieve_design_documents tests/unit/database_tests.py::DatabaseTests::test_retrieve_document_count tests/unit/database_tests.py::DatabaseTests::test_retrieve_raw_view_results tests/unit/database_tests.py::DatabaseTests::test_retrieve_view_results tests/unit/database_tests.py::DatabaseTests::test_revisions_diff tests/unit/database_tests.py::DatabaseTests::test_revs_diff_uses_custom_encoder tests/unit/database_tests.py::DatabaseTests::test_update_doc_with_update_handler tests/unit/database_tests.py::DatabaseTests::test_update_handler_raises_httperror tests/unit/design_document_tests.py::DesignDocumentTests::test_add_a_list_function tests/unit/design_document_tests.py::DesignDocumentTests::test_add_a_search_index tests/unit/design_document_tests.py::DesignDocumentTests::test_add_a_search_index_with_analyzer tests/unit/design_document_tests.py::DesignDocumentTests::test_add_a_show_function tests/unit/design_document_tests.py::DesignDocumentTests::test_add_a_view tests/unit/design_document_tests.py::DesignDocumentTests::test_adding_existing_list_function tests/unit/design_document_tests.py::DesignDocumentTests::test_adding_existing_search_index tests/unit/design_document_tests.py::DesignDocumentTests::test_adding_existing_show_functions tests/unit/design_document_tests.py::DesignDocumentTests::test_adding_existing_view tests/unit/design_document_tests.py::DesignDocumentTests::test_adding_query_index_view tests/unit/design_document_tests.py::DesignDocumentTests::test_constructor_with_design_docid tests/unit/design_document_tests.py::DesignDocumentTests::test_constructor_with_docid tests/unit/design_document_tests.py::DesignDocumentTests::test_constructor_without_docid tests/unit/design_document_tests.py::DesignDocumentTests::test_create_design_document_with_docid_encoded_url tests/unit/design_document_tests.py::DesignDocumentTests::test_delete_a_list_function tests/unit/design_document_tests.py::DesignDocumentTests::test_delete_a_query_index_view tests/unit/design_document_tests.py::DesignDocumentTests::test_delete_a_search_index tests/unit/design_document_tests.py::DesignDocumentTests::test_delete_a_show_function tests/unit/design_document_tests.py::DesignDocumentTests::test_delete_a_view tests/unit/design_document_tests.py::DesignDocumentTests::test_delete_design_document_success_with_encoded_url tests/unit/design_document_tests.py::DesignDocumentTests::test_fetch_existing_design_document_with_docid_encoded_url tests/unit/design_document_tests.py::DesignDocumentTests::test_fetch_list_functions tests/unit/design_document_tests.py::DesignDocumentTests::test_fetch_map_reduce tests/unit/design_document_tests.py::DesignDocumentTests::test_fetch_no_list_functions tests/unit/design_document_tests.py::DesignDocumentTests::test_fetch_no_search_index tests/unit/design_document_tests.py::DesignDocumentTests::test_fetch_no_show_functions tests/unit/design_document_tests.py::DesignDocumentTests::test_fetch_no_views tests/unit/design_document_tests.py::DesignDocumentTests::test_fetch_query_views tests/unit/design_document_tests.py::DesignDocumentTests::test_fetch_search_index tests/unit/design_document_tests.py::DesignDocumentTests::test_fetch_show_functions tests/unit/design_document_tests.py::DesignDocumentTests::test_fetch_text_indexes tests/unit/design_document_tests.py::DesignDocumentTests::test_fetch_text_indexes_and_query_views tests/unit/design_document_tests.py::DesignDocumentTests::test_get_info tests/unit/design_document_tests.py::DesignDocumentTests::test_get_info_raises_httperror tests/unit/design_document_tests.py::DesignDocumentTests::test_get_list_function tests/unit/design_document_tests.py::DesignDocumentTests::test_get_search_index tests/unit/design_document_tests.py::DesignDocumentTests::test_get_show_function tests/unit/design_document_tests.py::DesignDocumentTests::test_get_view tests/unit/design_document_tests.py::DesignDocumentTests::test_iterating_over_list_functions tests/unit/design_document_tests.py::DesignDocumentTests::test_iterating_over_search_indexes tests/unit/design_document_tests.py::DesignDocumentTests::test_iterating_over_show_functions tests/unit/design_document_tests.py::DesignDocumentTests::test_iterating_over_views tests/unit/design_document_tests.py::DesignDocumentTests::test_list_search_indexes tests/unit/design_document_tests.py::DesignDocumentTests::test_list_views tests/unit/design_document_tests.py::DesignDocumentTests::test_listing_list_functions tests/unit/design_document_tests.py::DesignDocumentTests::test_listing_show_functions tests/unit/design_document_tests.py::DesignDocumentTests::test_mr_view_save_fails_when_lang_is_query tests/unit/design_document_tests.py::DesignDocumentTests::test_mr_view_save_succeeds tests/unit/design_document_tests.py::DesignDocumentTests::test_query_view_save_fails_when_lang_is_not_query tests/unit/design_document_tests.py::DesignDocumentTests::test_query_view_save_succeeds tests/unit/design_document_tests.py::DesignDocumentTests::test_rewrite_rule tests/unit/design_document_tests.py::DesignDocumentTests::test_save_with_no_list_functions tests/unit/design_document_tests.py::DesignDocumentTests::test_save_with_no_search_indexes tests/unit/design_document_tests.py::DesignDocumentTests::test_save_with_no_show_functions tests/unit/design_document_tests.py::DesignDocumentTests::test_save_with_no_views tests/unit/design_document_tests.py::DesignDocumentTests::test_search_index_save_fails_when_lang_is_query tests/unit/design_document_tests.py::DesignDocumentTests::test_search_index_save_fails_with_existing_text_index tests/unit/design_document_tests.py::DesignDocumentTests::test_search_index_save_succeeds tests/unit/design_document_tests.py::DesignDocumentTests::test_setting_id tests/unit/design_document_tests.py::DesignDocumentTests::test_text_index_save_fails_when_lang_is_not_query tests/unit/design_document_tests.py::DesignDocumentTests::test_text_index_save_fails_with_existing_search_index tests/unit/design_document_tests.py::DesignDocumentTests::test_update_a_list_function tests/unit/design_document_tests.py::DesignDocumentTests::test_update_a_search_index tests/unit/design_document_tests.py::DesignDocumentTests::test_update_a_search_index_with_analyzer tests/unit/design_document_tests.py::DesignDocumentTests::test_update_a_show_function tests/unit/design_document_tests.py::DesignDocumentTests::test_update_a_view tests/unit/design_document_tests.py::DesignDocumentTests::test_update_design_document_with_encoded_url tests/unit/design_document_tests.py::DesignDocumentTests::test_update_non_existing_list_function tests/unit/design_document_tests.py::DesignDocumentTests::test_update_non_existing_search_index tests/unit/design_document_tests.py::DesignDocumentTests::test_update_non_existing_show_function tests/unit/design_document_tests.py::DesignDocumentTests::test_update_non_existing_view tests/unit/design_document_tests.py::DesignDocumentTests::test_update_query_index_view tests/unit/document_tests.py::DocumentTests::test_appended_error_message_using_save_with_invalid_key tests/unit/document_tests.py::DocumentTests::test_attachment_management tests/unit/document_tests.py::DocumentTests::test_constructor_with_docid tests/unit/document_tests.py::DocumentTests::test_constructor_without_docid tests/unit/document_tests.py::DocumentTests::test_create_document_using_save tests/unit/document_tests.py::DocumentTests::test_create_document_with_docid tests/unit/document_tests.py::DocumentTests::test_create_document_with_docid_encoded_url tests/unit/document_tests.py::DocumentTests::test_create_document_without_docid tests/unit/document_tests.py::DocumentTests::test_create_existing_document tests/unit/document_tests.py::DocumentTests::test_delete_document_failure tests/unit/document_tests.py::DocumentTests::test_delete_document_success tests/unit/document_tests.py::DocumentTests::test_delete_document_success_with_encoded_url tests/unit/document_tests.py::DocumentTests::test_design_document_url tests/unit/document_tests.py::DocumentTests::test_design_document_url_encodes_correctly tests/unit/document_tests.py::DocumentTests::test_document_context_manager tests/unit/document_tests.py::DocumentTests::test_document_exists tests/unit/document_tests.py::DocumentTests::test_document_exists_raises_httperror tests/unit/document_tests.py::DocumentTests::test_document_request_fails_after_client_disconnects tests/unit/document_tests.py::DocumentTests::test_document_url tests/unit/document_tests.py::DocumentTests::test_document_url_encodes_correctly tests/unit/document_tests.py::DocumentTests::test_fetch_document_without_docid tests/unit/document_tests.py::DocumentTests::test_fetch_existing_document_with_docid tests/unit/document_tests.py::DocumentTests::test_fetch_existing_document_with_docid_encoded_url tests/unit/document_tests.py::DocumentTests::test_fetch_non_existing_document tests/unit/document_tests.py::DocumentTests::test_field_set_and_replace tests/unit/document_tests.py::DocumentTests::test_get_binary_attachment tests/unit/document_tests.py::DocumentTests::test_get_json_attachment tests/unit/document_tests.py::DocumentTests::test_get_text_attachment tests/unit/document_tests.py::DocumentTests::test_list_field_append_failure tests/unit/document_tests.py::DocumentTests::test_list_field_append_successfully tests/unit/document_tests.py::DocumentTests::test_list_field_remove_failure tests/unit/document_tests.py::DocumentTests::test_list_field_remove_successfully tests/unit/document_tests.py::DocumentTests::test_removing_id tests/unit/document_tests.py::DocumentTests::test_retrieve_document_json tests/unit/document_tests.py::DocumentTests::test_setting_id tests/unit/document_tests.py::DocumentTests::test_update_document_using_save tests/unit/document_tests.py::DocumentTests::test_update_document_with_encoded_url tests/unit/infinite_feed_tests.py::InfiniteFeedTests::test_constructor_no_feed_option tests/unit/infinite_feed_tests.py::InfiniteFeedTests::test_constructor_with_feed_option tests/unit/infinite_feed_tests.py::InfiniteFeedTests::test_constructor_with_invalid_feed_option tests/unit/infinite_feed_tests.py::InfiniteFeedTests::test_infinite_feed tests/unit/replicator_tests.py::ReplicatorTests::test_constructor tests/unit/replicator_tests.py::ReplicatorTests::test_constructor_failure tests/unit/replicator_tests.py::ReplicatorTests::test_create_replication tests/unit/replicator_tests.py::ReplicatorTests::test_create_replication_without_a_source tests/unit/replicator_tests.py::ReplicatorTests::test_create_replication_without_a_target tests/unit/replicator_tests.py::ReplicatorTests::test_follow_replication tests/unit/replicator_tests.py::ReplicatorTests::test_list_replications tests/unit/replicator_tests.py::ReplicatorTests::test_replication_with_generated_id tests/unit/replicator_tests.py::ReplicatorTests::test_retrieve_replication_state tests/unit/replicator_tests.py::ReplicatorTests::test_retrieve_replication_state_using_invalid_id tests/unit/replicator_tests.py::ReplicatorTests::test_stop_replication
src/cloudant/error.py src/cloudant/document.py src/cloudant/design_document.py src/cloudant/client.py src/cloudant/index.py src/cloudant/replicator.py src/cloudant/database.py src/cloudant/_messages.py src/cloudant/feed.py
src/cloudant/error.py src/cloudant/document.py src/cloudant/design_document.py src/cloudant/client.py src/cloudant/index.py src/cloudant/replicator.py src/cloudant/database.py src/cloudant/_messages.py src/cloudant/feed.py