PY2 = sys.version_info[0] < 3
ENCODING = 'utf-8'
NONETYPE = type(None)
STRTYPE = basestring if PY2 else str
UNITYPE = unicode if PY2 else str
LONGTYPE = long if PY2 else int
UNICHR = unichr if PY2 else chr
    def iteritems_(adict):
    def next_(itr):
    def iteritems_(adict):
    def next_(itr):
def bytes_(astr):
def unicode_(astr): QUERY_LANGUAGE = 'query' def codify(code_or_str):
    """
    Provides a helper to rationalize code content.
    """
    if code_or_str is None:
        return None
    if not isinstance(code_or_str, _Code):
        return _Code(code_or_str)
    return code_or_str def response_to_json_dict(response, **kwargs):
    """
    Standard place to convert responses to JSON.

    :param response: requests response object
    :param **kwargs: arguments accepted by json.loads

    :returns: dict of JSON response
    """
    if response.encoding is None:
        response.encoding = 'utf-8'
    return json.loads(response.text, **kwargs) def assert_document_type_id(docid):
    """
    Validate the document ID.  Raises an error if the ID is an `_` prefixed name
    that isn't either `_design` or `_local`.
    :return:
    """
    invalid = False
    if docid.startswith('_'):
        if docid.startswith(DESIGN_PREFIX) and DESIGN_PREFIX != docid:
            invalid = False
        elif docid.startswith(LOCAL_PREFIX) and LOCAL_PREFIX != docid:
            invalid = False
        else:
            invalid = True
    if invalid:
        raise CloudantArgumentError(137, docid) DESIGN_PREFIX = '_design/' class Document(dict):
    """
    Encapsulates a JSON document.  A Document object is instantiated with a
    reference to a database and used to manipulate document content
    in a CouchDB or Cloudant database instance.

    In addition to basic CRUD style operations, a Document object also provides
    a convenient context manager.  This context manager removes having to
    explicitly :func:`~cloudant.document.Document.fetch` the document from the
    remote database before commencing work on it as well as explicitly having
    to :func:`~cloudant.document.Document.save` the document once work is
    complete.

    For example:

    .. code-block:: python

        # Upon entry into the document context, fetches the document from the
        # remote database, if it exists. Upon exit from the context, saves the
        # document to the remote database with changes made within the context.
        with Document(database, 'julia006') as document:
            # The document is fetched from the remote database
            # Changes are made locally
            document['name'] = 'Julia'
            document['age'] = 6
            # The document is saved to the remote database

    :param database: A database instance used by the Document.  Can be
        either a ``CouchDatabase`` or ``CloudantDatabase`` instance.
    :param str document_id: Optional document id used to identify the document.
    :param str encoder: Optional JSON encoder object (extending json.JSONEncoder).
    :param str decoder: Optional JSON decoder object (extending json.JSONDecoder).
    """
    def __init__(self, database, document_id=None, **kwargs):
        super(Document, self).__init__()
        self._client = database.client
        self._database = database
        self._database_host = self._client.server_url
        self._database_name = database.database_name
        if document_id:
            self['_id'] = document_id
        self.encoder = kwargs.get('encoder') or self._client.encoder
        self.decoder = kwargs.get('decoder') or json.JSONDecoder

    @property
    def r_session(self):
        """
        Returns the database instance ``r_session`` used by the document.

        :returns: Client ``r_session``
        """
        return self._client.r_session

    @property
    def document_url(self):
        """
        Constructs and returns the document URL.

        :returns: Document URL
        """
        if '_id' not in self or self['_id'] is None:
            return None

        # handle design document url
        if self['_id'].startswith('_design/'):
            return '/'.join((
                self._database_host,
                url_quote_plus(self._database_name),
                '_design',
                url_quote(self['_id'][8:], safe='')
            ))

        # handle _local document url
        if self['_id'].startswith('_local/'):
            return '/'.join((
                self._database_host,
                url_quote_plus(self._database_name),
                '_local',
                url_quote(self['_id'][7:], safe='')
            ))

        # handle document url
        return '/'.join((
            self._database_host,
            url_quote_plus(self._database_name),
            url_quote(self['_id'], safe='')
        ))

    def exists(self):
        """
        Retrieves whether the document exists in the remote database or not.

        :returns: True if the document exists in the remote database,
            otherwise False
        """
        if '_id' not in self or self['_id'] is None:
            return False

        assert_document_type_id(self['_id'])

        resp = self.r_session.head(self.document_url)
        if resp.status_code not in [200, 404]:
            resp.raise_for_status()

        return resp.status_code == 200

    def json(self):
        """
        Retrieves the JSON string representation of the current locally cached
        document object, encoded by the encoder specified in the associated
        client object.

        :returns: Encoded JSON string containing the document data
        """
        return json.dumps(dict(self), cls=self.encoder)

    def create(self):
        """
        Creates the current document in the remote database and if successful,
        updates the locally cached Document object with the ``_id``
        and ``_rev`` returned as part of the successful response.
        """

        # Ensure that an existing document will not be "updated"
        doc = dict(self)
        if doc.get('_rev') is not None:
            doc.__delitem__('_rev')

        headers = {'Content-Type': 'application/json'}
        resp = self.r_session.post(
            self._database.database_url,
            headers=headers,
            data=json.dumps(doc, cls=self.encoder)
        )
        resp.raise_for_status()
        data = response_to_json_dict(resp)
        super(Document, self).__setitem__('_id', data['id'])
        super(Document, self).__setitem__('_rev', data['rev'])

    def fetch(self):
        """
        Retrieves the content of the current document from the remote database
        and populates the locally cached Document object with that content.
        A call to fetch will overwrite any dictionary content currently in
        the locally cached Document object.
        """
        if self.document_url is None:
            raise CloudantDocumentException(101)
        if '_id' in self:
            assert_document_type_id(self['_id'])
        resp = self.r_session.get(self.document_url)
        resp.raise_for_status()
        self.clear()
        self.update(response_to_json_dict(resp, cls=self.decoder))

    def save(self):
        """
        Saves changes made to the locally cached Document object's data
        structures to the remote database.  If the document does not exist
        remotely then it is created in the remote database.  If the object
        does exist remotely then the document is updated remotely.  In either
        case the locally cached Document object is also updated accordingly
        based on the successful response of the operation.
        """
        headers = {}
        headers.setdefault('Content-Type', 'application/json')
        if not self.exists():
            self.create()
            return
        put_resp = self.r_session.put(
            self.document_url,
            data=self.json(),
            headers=headers
        )
        put_resp.raise_for_status()
        data = response_to_json_dict(put_resp)
        super(Document, self).__setitem__('_rev', data['rev'])
        return

    # Update Actions
    # These are handy functions to use with update_field below.
    @staticmethod
    def list_field_append(doc, field, value):
        """
        Appends a value to a list field in a locally cached Document object.
        If a field does not exist it will be created first.

        :param Document doc: Locally cached Document object that can be a
            Document, DesignDocument or dict.
        :param str field: Name of the field list to append to.
        :param value: Value to append to the field list.
        """
        if doc.get(field) is None:
            doc[field] = []
        if not isinstance(doc[field], list):
            raise CloudantDocumentException(102, field)
        if value is not None:
            doc[field].append(value)

    @staticmethod
    def list_field_remove(doc, field, value):
        """
        Removes a value from a list field in a locally cached Document object.

        :param Document doc: Locally cached Document object that can be a
            Document, DesignDocument or dict.
        :param str field: Name of the field list to remove from.
        :param value: Value to remove from the field list.
        """
        if not isinstance(doc[field], list):
            raise CloudantDocumentException(102, field)
        doc[field].remove(value)

    @staticmethod
    def field_set(doc, field, value):
        """
        Sets or replaces a value for a field in a locally cached Document
        object.  To remove the field set the ``value`` to None.

        :param Document doc: Locally cached Document object that can be a
            Document, DesignDocument or dict.
        :param str field: Name of the field to set.
        :param value: Value to set the field to.
        """
        if value is None:
            doc.__delitem__(field)
        else:
            doc[field] = value

    def _update_field(self, action, field, value, max_tries, tries=0):
        """
        Private update_field method. Wrapped by Document.update_field.
        Tracks a "tries" var to help limit recursion.
        """
        # Refresh our view of the document.
        self.fetch()

        # Update the field.
        action(self, field, value)

        # Attempt to save, retrying conflicts up to max_tries.
        try:
            self.save()
        except requests.HTTPError as ex:
            if tries < max_tries and ex.response.status_code == 409:
                self._update_field(
                    action, field, value, max_tries, tries=tries+1)
            else:
                raise

    def update_field(self, action, field, value, max_tries=10):
        """
        Updates a field in the remote document. If a conflict exists,
        the document is re-fetched from the remote database and the update
        is retried.  This is performed up to ``max_tries`` number of times.

        Use this method when you want to update a single field in a document,
        and don't want to risk clobbering other people's changes to
        the document in other fields, but also don't want the caller
        to implement logic to deal with conflicts.

        For example:

        .. code-block:: python

            # Append the string 'foo' to the 'words' list of Document doc.
            doc.update_field(
                action=doc.list_field_append,
                field='words',
                value='foo'
            )

        :param callable action: A routine that takes a Document object,
            a field name, and a value. The routine should attempt to
            update a field in the locally cached Document object with the
            given value, using whatever logic is appropriate.
            Valid actions are
            :func:`~cloudant.document.Document.list_field_append`,
            :func:`~cloudant.document.Document.list_field_remove`,
            :func:`~cloudant.document.Document.field_set`
        :param str field: Name of the field to update
        :param value: Value to update the field with
        :param int max_tries: In the case of a conflict, the number of retries
            to attempt
        """
        self._update_field(action, field, value, max_tries)

    def delete(self):
        """
        Removes the document from the remote database and clears the content of
        the locally cached Document object with the exception of the ``_id``
        field.  In order to successfully remove a document from the remote
        database, a ``_rev`` value must exist in the locally cached Document
        object.
        """
        if not self.get("_rev"):
            raise CloudantDocumentException(103)

        assert_document_type_id(self['_id'])

        del_resp = self.r_session.delete(
            self.document_url,
            params={"rev": self["_rev"]},
        )
        del_resp.raise_for_status()
        _id = self['_id']
        self.clear()
        self['_id'] = _id

    def __enter__(self):
        """
        Supports context like editing of document fields.  Handles context
        entry logic.  Executes a Document.fetch() upon entry.
        """

        # We don't want to raise an exception if the document is not found
        # because upon __exit__ the save() call will create the document
        # if necessary.
        try:
            self.fetch()
        except HTTPError as error:
            if error.response.status_code != 404:
                raise
        except CloudantDocumentException as error:
            if error.status_code != 101:
                raise

        return self

    def __exit__(self, exc_type, exc_value, traceback):
        """
        Support context like editing of document fields. Handles context exit
        logic. Executes a `Document.save()` upon exit if no exception occurred.
        """
        if exc_type is None:
            self.save()

    def get_attachment(
            self,
            attachment,
            headers=None,
            write_to=None,
            attachment_type=None):
        """
        Retrieves a document's attachment and optionally writes it to a file.
        If the content_type of the attachment is 'application/json' then the
        data returned will be in JSON format otherwise the response content will
        be returned as text or binary.

        :param str attachment: Attachment file name used to identify the
            attachment.
        :param dict headers: Optional, additional headers to be sent
            with request.
        :param file write_to: Optional file handler to write the attachment to.
            The write_to file must be opened for writing prior to including it
            as an argument for this method.
        :param str attachment_type: Optional setting to define how to handle the
            attachment when returning its contents from this method.  Valid
            values are ``'text'``, ``'json'``, and ``'binary'``  If
            omitted then the returned content will be based on the
            response Content-Type.

        :returns: The attachment content
        """
        # need latest rev
        self.fetch()
        assert_attachment_name(attachment)
        attachment_url = '/'.join((self.document_url, url_quote(attachment, safe='')))
        if headers is None:
            headers = {'If-Match': self['_rev']}
        else:
            headers['If-Match'] = self['_rev']

        resp = self.r_session.get(attachment_url, headers=headers)
        resp.raise_for_status()

        if attachment_type is None:
            if resp.headers['Content-Type'].startswith('text/'):
                attachment_type = 'text'
            elif resp.headers['Content-Type'] == 'application/json':
                attachment_type = 'json'
            else:
                attachment_type = 'binary'

        if write_to is not None:
            if attachment_type in ('text', 'json'):
                write_to.write(resp.text)
            else:
                write_to.write(resp.content)
        if attachment_type == 'text':
            return resp.text
        if attachment_type == 'json':
            return response_to_json_dict(resp)

        return resp.content

    def delete_attachment(self, attachment, headers=None):
        """
        Removes an attachment from a remote document and refreshes the locally
        cached document object.

        :param str attachment: Attachment file name used to identify the
            attachment.
        :param dict headers: Optional, additional headers to be sent
            with request.

        :returns: Attachment deletion status in JSON format
        """
        # need latest rev
        self.fetch()
        assert_attachment_name(attachment)
        attachment_url = '/'.join((self.document_url, attachment))
        if headers is None:
            headers = {'If-Match': self['_rev']}
        else:
            headers['If-Match'] = self['_rev']

        resp = self.r_session.delete(
            attachment_url,
            headers=headers
        )
        resp.raise_for_status()
        super(Document, self).__setitem__('_rev', response_to_json_dict(resp)['rev'])
        # Execute logic only if attachment metadata exists locally
        if self.get('_attachments'):
            # Remove the attachment metadata for the specified attachment
            if self['_attachments'].get(attachment):
                self['_attachments'].__delitem__(attachment)
            # Remove empty attachment metadata from the local dictionary
            if not self['_attachments']:
                super(Document, self).__delitem__('_attachments')

        return response_to_json_dict(resp)

    def put_attachment(self, attachment, content_type, data, headers=None):
        """
        Adds a new attachment, or updates an existing attachment, to
        the remote document and refreshes the locally cached
        Document object accordingly.

        :param attachment: Attachment file name used to identify the
            attachment.
        :param content_type: The http ``Content-Type`` of the attachment used
            as an additional header.
        :param data: Attachment data defining the attachment content.
        :param headers: Optional, additional headers to be sent
            with request.

        :returns: Attachment addition/update status in JSON format
        """
        # need latest rev
        self.fetch()
        assert_attachment_name(attachment)
        attachment_url = '/'.join((self.document_url, attachment))
        if headers is None:
            headers = {
                'If-Match': self['_rev'],
                'Content-Type': content_type
            }
        else:
            headers['If-Match'] = self['_rev']
            headers['Content-Type'] = content_type

        resp = self.r_session.put(
            attachment_url,
            data=data,
            headers=headers
        )
        resp.raise_for_status()
        self.fetch()
        return response_to_json_dict(resp) class View(dict):
    """
    Encapsulates a view as a dictionary based object, exposing the map and
    reduce functions as attributes and supporting query/data access through
    the view.  A View object is instantiated with a reference to a
    DesignDocument and is typically used as part of the
    :class:`~cloudant.design_document.DesignDocument` view management API.

    A View object provides a key accessible, sliceable, and iterable default
    result collection that can be used to query the view data through the
    ``result`` attribute.

    For example:

    .. code-block:: python

        # Access result collection through individual keys
        view.result[100]
        view.result['foo']

        # Access result collection through index slicing:
        view.result[100: 200]
        view.result[: 200]
        view.result[100: ]
        view.result[: ]

        # Access result collection through key slicing:
        view.result['bar': 'foo']
        view.result['bar': ]
        view.result[: 'foo']

        # Iterate over the result collection:
        for doc in view.result:
            print doc

    The default result collection provides basic functionality,
    which can be customized with other arguments using the
    :func:`~cloudant.view.View.custom_result` context manager.

    For example:

    .. code-block:: python

        # Including documents as part of a custom result
        with view.custom_result(include_docs=True) as rslt:
            rslt[100: 200]                       # slice by result
            rslt[['2013', '10']: ['2013', '11']] # slice by startkey/endkey

            # Iteration
            for doc in rslt:
                print doc

        # Iteration over a view within startkey/endkey range:
        with view.custom_result(startkey='2013', endkey='2014') as rslt:
            for doc in rslt:
                print doc

    Note:  A view must exist as part of a design document remotely in order to
    access result content as depicted in the above examples.

    :param DesignDocument ddoc: DesignDocument instance used in part to
        identify the view.
    :param str view_name: Name used in part to identify the view.
    :param str map_func: Optional Javascript map function.
    :param str reduce_func: Optional Javascript reduce function.
    :param str partition_key: Optional. Specify a view partition key. Defaults
        to ``None`` resulting in global queries.
    """
    def __init__(
            self,
            ddoc,
            view_name,
            map_func=None,
            reduce_func=None,
            partition_key=None,
            **kwargs
    ):
        super(View, self).__init__()
        self.design_doc = ddoc
        self._r_session = self.design_doc.r_session
        self.view_name = view_name
        if map_func is not None:
            self['map'] = codify(map_func)
        if reduce_func is not None:
            self['reduce'] = codify(reduce_func)
        self._partition_key = partition_key
        self.update(kwargs)
        self.result = Result(self)

    @property
    def map(self):
        """
        Provides an map property accessor and setter.

        For example:

        .. code-block:: python

            # Set the View map property
            view.map = 'function (doc) {\\n  emit(doc._id, 1);\\n}'
            print view.map

        :param str js_func: Javascript function.

        :returns: Codified map function
        """
        return self.get('map')

    @map.setter
    def map(self, js_func):
        """
        Provides a map property setter.
        """
        self['map'] = codify(js_func)

    @property
    def reduce(self):
        """
        Provides an reduce property accessor and setter.

        For example:

        .. code-block:: python

            # Set the View reduce property
            view.reduce = '_count'
            # Get and print the View reduce property
            print view.reduce

        :param str js_func: Javascript function.

        :returns: Codified reduce function
        """
        return self.get('reduce')

    @reduce.setter
    def reduce(self, js_func):
        """
        Provides a reduce property setter.
        """
        self['reduce'] = codify(js_func)

    @property
    def url(self):
        """
        Constructs and returns the View URL.

        :returns: View URL
        """
        if self._partition_key:
            base_url = self.design_doc.document_partition_url(
                self._partition_key)
        else:
            base_url = self.design_doc.document_url

        return '/'.join((
            base_url,
            '_view',
            self.view_name
        ))

    def __call__(self, **kwargs):
        """
        Makes the View object callable and retrieves the raw JSON content
        from the remote database based on the View definition on the server,
        using the kwargs provided as query parameters.

        For example:

        .. code-block:: python

            # Construct a View
            view = View(ddoc, 'view001')
            # Assuming that 'view001' exists as part of the
            # design document ddoc in the remote database...
            # Use view as a callable
            for row in view(include_docs=True, limit=100, skip=100)['rows']:
                # Process view data (in JSON format).

        Note:  Rather than using the View callable directly, if you wish to
        retrieve view results in raw JSON format use ``raw_result=True`` with
        the provided database API of
        :func:`~cloudant.database.CouchDatabase.get_view_result` instead.

        :param bool descending: Return documents in descending key order.
        :param endkey: Stop returning records at this specified key.
        :param str endkey_docid: Stop returning records when the specified
            document id is reached.
        :param bool group: Using the reduce function, group the results to a
            group or single row.
        :param group_level: Only applicable if the view uses complex keys: keys
            that are JSON arrays. Groups reduce results for the specified number
            of array fields.
        :param bool include_docs: Include the full content of the documents.
        :param bool inclusive_end: Include rows with the specified endkey.
        :param str key: Return only documents that match the specified key.
        :param list keys: Return only documents that match the specified keys.
        :param int limit: Limit the number of returned documents to the
            specified count.
        :param bool reduce: True to use the reduce function, false otherwise.
        :param int skip: Skip this number of rows from the start.
        :param str stale: Allow the results from a stale view to be used. This
            makes the request return immediately, even if the view has not been
            completely built yet. If this parameter is not given, a response is
            returned only after the view has been built.
        :param startkey: Return records starting with the specified key.
        :param str startkey_docid: Return records starting with the specified
            document ID.

        :returns: View result data in JSON format
        """
        resp = get_docs(self._r_session,
                        self.url,
                        self.design_doc.encoder,
                        **kwargs)
        return response_to_json_dict(resp)

    @contextlib.contextmanager
    def custom_result(self, **options):
        """
        Customizes the :class:`~cloudant.result.Result` behavior and provides
        a convenient context manager for the Result.  Result customizations
        can be made by providing extra options to the result call using this
        context manager.  Depending on how you are accessing, slicing or
        iterating through your result collection certain query parameters are
        not permitted.  See :class:`~cloudant.result.Result` for additional
        details.

        For example:

        .. code-block:: python

            with view.custom_result(include_docs=True, reduce=False) as rslt:
                data = rslt[100: 200]

        :param bool descending: Return documents in descending key order.
        :param endkey: Stop returning records at this specified key.
            Not valid when used with :class:`~cloudant.result.Result` key
            access and key slicing.
        :param str endkey_docid: Stop returning records when the specified
            document id is reached.
        :param bool group: Using the reduce function, group the results to a
            group or single row.
        :param group_level: Only applicable if the view uses complex keys: keys
            that are JSON arrays. Groups reduce results for the specified number
            of array fields.
        :param bool include_docs: Include the full content of the documents.
        :param bool inclusive_end: Include rows with the specified endkey.
        :param key: Return only documents that match the specified key.
            Not valid when used with :class:`~cloudant.result.Result` key
            access and key slicing.
        :param list keys: Return only documents that match the specified keys.
            Not valid when used with :class:`~cloudant.result.Result` key
            access and key slicing.
        :param int limit: Limit the number of returned documents to the
            specified count.  Not valid when used with
            :class:`~cloudant.result.Result` iteration.
        :param int page_size: Sets the page size for result iteration.
        :param bool reduce: True to use the reduce function, false otherwise.
        :param int skip: Skip this number of rows from the start.
            Not valid when used with :class:`~cloudant.result.Result` iteration.
        :param str stale: Allow the results from a stale view to be used. This
            makes the request return immediately, even if the view has not been
            completely built yet. If this parameter is not given, a response is
            returned only after the view has been built.
        :param startkey: Return records starting with the specified key.
            Not valid when used with :class:`~cloudant.result.Result` key
            access and key slicing.
        :param str startkey_docid: Return records starting with the specified
            document ID.

        :returns: View result data wrapped in a Result instance
        """
        rslt = Result(self, **options)
        yield rslt
        del rslt class QueryIndexView(View):
    """
    A view that defines a JSON query index in a design document.

    If you wish to manage a view that represents a JSON query index it
    is strongly recommended that
    :func:`~cloudant.database.CloudantDatabase.create_query_index`
    and :func:`~cloudant.database.CloudantDatabase.delete_query_index` are used.
    """
    def __init__(self, ddoc, view_name, map_fields, reduce_func, **kwargs):
        if not isinstance(map_fields, dict):
            raise CloudantArgumentError(132)
        if not isinstance(reduce_func, STRTYPE):
            raise CloudantArgumentError(133)
        super(QueryIndexView, self).__init__(
            ddoc,
            view_name,
            map_fields,
            reduce_func,
            **kwargs
        )
        self['map'] = map_fields
        self['reduce'] = reduce_func
        self.result = None

    @property
    def map(self):
        """
        Provides a map property accessor and setter.

        :param dict map_func: A dictionary of fields defining the index.

        :returns: Fields defining the index
        """
        return self.get('map')

    @map.setter
    def map(self, map_func):
        """
        Provides a map property setter.
        """
        if isinstance(map_func, dict):
            self['map'] = map_func
        else:
            raise CloudantArgumentError(132)

    @property
    def reduce(self):
        """
        Provides a reduce property accessor and setter.

        :param str reduce_func: A string representation of the reduce function
            used in part to define the index.

        :returns: Reduce function as a string
        """
        return self.get('reduce')

    @reduce.setter
    def reduce(self, reduce_func):
        """
        Provides a reduce property setter.
        """
        if isinstance(reduce_func, STRTYPE):
            self['reduce'] = reduce_func
        else:
            raise CloudantArgumentError(133)

    def __call__(self, **kwargs):
        """
        QueryIndexView objects are not callable.  If you wish to execute a query
        using a query index, use
        :func:`~cloudant.database.CloudantDatabase.get_query_result` instead.
        """
        raise CloudantViewException(101)

    def custom_result(self, **options):
        """
        This method overrides the View base class
        :func:`~cloudant.view.View.custom_result` method with the sole purpose of
        disabling it.  Since QueryIndexView objects are not callable, there is
        no reason to wrap their output in a Result.  If you wish to execute a
        query using a query index, use
        :func:`~cloudant.database.CloudantDatabase.get_query_result` instead.
        """
        raise CloudantViewException(102) class CloudantArgumentError(CloudantException):
    """
    Provides a way to issue Cloudant Python client library specific exceptions
    that pertain to invalid argument errors.

    Note:  The intended use for this class is internal to the Cloudant Python
    client library.

    :param int code: An optional code value used to identify the exception.
        Defaults to 100.
    :param args: A list of arguments used to format the exception message.
    """
    def __init__(self, code=100, *args):
        try:
            msg = ARGUMENT_ERROR[code].format(*args)
        except (KeyError, IndexError):
            code = 100
            msg = ARGUMENT_ERROR[code]
        super(CloudantArgumentError, 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)
from ._2to3 import iteritems_, url_quote_plus, STRTYPE
from ._common_util import QUERY_LANGUAGE, codify, response_to_json_dict, \
    assert_document_type_id, DESIGN_PREFIX
from .document import Document
from .view import View, QueryIndexView
from .error import CloudantArgumentError, CloudantDesignDocumentException
        Once the Cloudant Geo index is saved to the remote database, you can
        query the index with a GET request.  To issue a request against the
        ``_geo`` endpoint, see the steps outlined in the `endpoint access
        <getting_started.html#endpoint-access>`_ section.

        For more details, see the `Cloudant Geospatial
        documentation <https://console.bluemix.net/docs/services/Cloudant/api/cloudant-geo.html>`_.

        :return: Dictionary containing Cloudant Geo names and index objects
            as key/value
        """
        return self.get('st_indexes')

    @property
    def lists(self):
        """
        Provides an accessor property to the lists dictionary in the locally
        cached DesignDocument.

        :returns: Dictionary containing list names and objects as key/value
        """
        return self.get('lists')

    @property
    def shows(self):
        """
        Provides an accessor property to the shows dictionary in the
        locally cached DesignDocument.

        :returns: Dictionary containing show names and functions
            as key/value
        """
        return self.get('shows')

    @property
    def rewrites(self):
        """
        Provides an accessor property to a list of dictionaries with rewrite
        rules in the locally cached DesignDocument.  Each rule for URL rewriting
        is a JSON object with four fields: ``from``, ``to``, ``method``,
        and ``query``.

        Note: Requests that match the rewrite rules must have a URL path that
        starts with ``/$DATABASE/_design/doc/_rewrite``.

        Rewrite rule example:

        .. code-block:: python

            # Add the rule to ``rewrites`` and save the design document
            ddoc = DesignDocument(self.db, '_design/ddoc001')
            ddoc['rewrites'] = [
                {"from": "/old/topic",
                 "to": "/new/",
                 "method": "GET",
                 "query": {}
                 }
            ]
            ddoc.save()

        Once the rewrite rule is saved to the remote database, the GET
        request URL ``/$DATABASE/_design/doc/_rewrite/old/topic?k=v`` would be
        rewritten as ``/$DATABASE/_design/doc/_rewrite/new?k=v``.

        For more details on URL rewriting, see the `rewrite rules
        documentation <https://console.bluemix.net/docs/services/Cloudant/api/design_documents.html
        #rewrite-rules>`_.

        :returns: List of dictionaries containing rewrite rules as key/value
        """
        return self.get('rewrites')

    @property
    def views(self):
        """
        Provides an accessor property to the View dictionary in the locally
        cached DesignDocument.

        :returns: Dictionary containing view names and View objects as key/value
        """
        return self.get('views')

    @property
    def indexes(self):
        """
        Provides an accessor property to the indexes dictionary in the
        locally cached DesignDocument.

        :returns: Dictionary containing index names and index objects
            as key/value
        """
        return self.get('indexes')

    def document_partition_url(self, partition_key):
        """
        Retrieve the design document partition URL.

        :param str partition_key: Partition key.
        :return: Design document partition URL.
        :rtype: str
        """
        return '/'.join((
            self._database.database_partition_url(partition_key),
            '_design',
            url_quote_plus(self['_id'][8:], safe='')
        ))

    def add_view(self, view_name, map_func, reduce_func=None, **kwargs):
        """
        Appends a MapReduce view to the locally cached DesignDocument View
        dictionary.  To create a JSON query index use
        :func:`~cloudant.database.CloudantDatabase.create_query_index` instead.
        A CloudantException is raised if an attempt to add a QueryIndexView
        (JSON query index) using this method is made.

        :param str view_name: Name used to identify the View.
        :param str map_func: Javascript map function.
        :param str reduce_func: Optional Javascript reduce function.
        """
        if self.get_view(view_name) is not None:

            raise CloudantArgumentError(107, view_name)