docs: 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)