return a new cloned error

        when do:

        ```
        try:
            do_sth()
        except BaseException as e:
            handle(e)

        def handle(error):
            # do sth with error
            raise e  # <- won't work!

        This can generate a new cloned error of the same class

        Parameters
        ----------
        error: the caught error
        args: the new args to init the cloned error

        Returns
        -------
        new error of the same class

def clone_exception(error, args):
        """
        return a new cloned error

        when do:

        ```
        try:
            do_sth()
        except BaseException as e:
            handle(e)

        def handle(error):
            # do sth with error
            raise e  # <- won't work!

        This can generate a new cloned error of the same class

        Parameters
        ----------
        error: the caught error
        args: the new args to init the cloned error

        Returns
        -------
        new error of the same class
        """
        new_error = error.__class__(*args)
        new_error.__dict__ = error.__dict__
        return new_error