r"""
        Add an exception to the handler.

        :param exname: Exception name; has to be unique within the namespace,
                       duplicates are eliminated
        :type  exname: non-numeric string

        :param extype: Exception type; *must* be derived from the `Exception
                       <https://docs.python.org/2/library/exceptions.html#
                       exceptions.Exception>`_ class
        :type  extype: Exception type object, i.e. RuntimeError, TypeError,
                       etc.

        :param exmsg: Exception message; it can contain fields to be replaced
                      when the exception is raised via
                      :py:meth:`pexdoc.ExHandle.raise_exception_if`.
                      A field starts with the characters :code:`'\*['` and
                      ends with the characters :code:`']\*'`, the field name
                      follows the same rules as variable names and is between
                      these two sets of characters. For example,
                      :code:`'\*[fname]\*'` defines the fname field
        :type  exmsg: string

        :rtype: tuple

        The returned tuple has the following items:

          * **callable id** (string) first returned item, identification (as
            reported by the `id
            <https://docs.python.org/2/library/functions.html#id>`_ built-in
            function) of the callable where the exception was added

          * **exception definition** (tuple), second returned item, first item
            is the exception type and the second item is the exception message

          * **callable name** (string), third returned item, callable full
            name (encoded with the :py:meth:`ExHandle.encode_call` method

        :raises:
         * RuntimeError (Argument \`exmsg\` is not valid)

         * RuntimeError (Argument \`exname\` is not valid)

         * RuntimeError (Argument \`extype\` is not valid)

def add_exception(self, exname, extype, exmsg):
        r"""
        Add an exception to the handler.

        :param exname: Exception name; has to be unique within the namespace,
                       duplicates are eliminated
        :type  exname: non-numeric string

        :param extype: Exception type; *must* be derived from the `Exception
                       <https://docs.python.org/2/library/exceptions.html#
                       exceptions.Exception>`_ class
        :type  extype: Exception type object, i.e. RuntimeError, TypeError,
                       etc.

        :param exmsg: Exception message; it can contain fields to be replaced
                      when the exception is raised via
                      :py:meth:`pexdoc.ExHandle.raise_exception_if`.
                      A field starts with the characters :code:`'\*['` and
                      ends with the characters :code:`']\*'`, the field name
                      follows the same rules as variable names and is between
                      these two sets of characters. For example,
                      :code:`'\*[fname]\*'` defines the fname field
        :type  exmsg: string

        :rtype: tuple

        The returned tuple has the following items:

          * **callable id** (string) first returned item, identification (as
            reported by the `id
            <https://docs.python.org/2/library/functions.html#id>`_ built-in
            function) of the callable where the exception was added

          * **exception definition** (tuple), second returned item, first item
            is the exception type and the second item is the exception message

          * **callable name** (string), third returned item, callable full
            name (encoded with the :py:meth:`ExHandle.encode_call` method

        :raises:
         * RuntimeError (Argument \`exmsg\` is not valid)

         * RuntimeError (Argument \`exname\` is not valid)

         * RuntimeError (Argument \`extype\` is not valid)
        """
        if not isinstance(exname, str):
            raise RuntimeError("Argument `exname` is not valid")
        number = True
        try:
            int(exname)
        except ValueError:
            number = False
        if number:
            raise RuntimeError("Argument `exname` is not valid")
        if not isinstance(exmsg, str):
            raise RuntimeError("Argument `exmsg` is not valid")
        msg = ""
        try:
            raise extype(exmsg)
        except Exception as eobj:
            msg = _get_ex_msg(eobj)
        if msg != exmsg:
            raise RuntimeError("Argument `extype` is not valid")
        # A callable that defines an exception can be accessed by
        # multiple functions or paths, therefore the callable
        # dictionary key 'function' is a list
        func_id, func_name = self._get_ex_data()
        if func_id not in self._ex_dict:
            self._ex_dict[func_id] = {}
        key = (extype, exmsg)
        exname = "{0}{1}{2}".format(func_id, self._callables_separator, exname)
        entry = self._ex_dict[func_id].get(
            key, {"function": [], "name": exname, "raised": []}
        )
        if func_name not in entry["function"]:
            entry["function"].append(func_name)
            entry["raised"].append(False)
        self._ex_dict[func_id][key] = entry
        return (func_id, key, func_name)