def report_exception(self, msg, notify_user=True, level="error", error_frame=None):
        """
        Report details of an exception to the user.
        This should only be called within an except: block Details of the
        exception are reported eg filename, line number and exception type.

        Because stack trace information outside of py3status or it's modules is
        not helpful in actually finding and fixing the error, we try to locate
        the first place that the exception affected our code.

        Alternatively if the error occurs in a module via a Py3 call that
        catches and reports the error then we receive an error_frame and use
        that as the source of the error.

        NOTE: msg should not end in a '.' for consistency.
        """
        # Get list of paths that our stack trace should be found in.
        py3_paths = [Path(__file__).resolve()] + self.config["include_paths"]
        traceback = None

        try:
            # We need to make sure to delete tb even if things go wrong.
            exc_type, exc_obj, tb = sys.exc_info()
            stack = extract_tb(tb)
            error_str = f"{exc_type.__name__}: {exc_obj}\n"
            traceback = [error_str]

            if error_frame:
                # The error occurred in a py3status module so the traceback
                # should be made to appear correct.  We caught the exception
                # but make it look as though we did not.
                traceback += format_stack(error_frame, 1) + format_tb(tb)
                filename = Path(error_frame.f_code.co_filename).name
                line_no = error_frame.f_lineno
            else:
                # This is a none module based error
                traceback += format_tb(tb)
                # Find first relevant trace in the stack.
                # it should be in py3status or one of it's modules.
                found = False
                for item in reversed(stack):
                    filename = item[0]
                    for path in py3_paths:
                        if filename.startswith(path):
                            # Found a good trace
                            filename = item[0].name
                            line_no = item[1]
                            found = True
                            break
                    if found:
                        break
            # all done!  create our message.
            msg = "{} ({}) {} line {}.".format(msg, exc_type.__name__, filename, line_no)
        except:  # noqa e722
            # something went wrong report what we can.
            msg = f"{msg}."
        finally:
            # delete tb!
            del tb
        # log the exception and notify user
        self.py3_wrapper.log(msg, "warning")
        if traceback:
            # if debug is not in the config  then we are at an early stage of
            # running py3status and logging is not yet available so output the
            # error to STDERR so it can be seen
            if "debug" not in self.config:
                print_stderr("\n".join(traceback))
            elif self.config.get("log_file"):
                self.py3_wrapper.log("".join(["Traceback\n"] + traceback))
        if notify_user:
            self.py3_wrapper.notify_user(msg, level=level)

        Report details of an exception to the user.
        This should only be called within an except: block Details of the
        exception are reported eg filename, line number and exception type.

        Because stack trace information outside of py3status or it's modules is
        not helpful in actually finding and fixing the error, we try to locate
        the first place that the exception affected our code.

        Alternatively if the error occurs in a module via a Py3 call that
        catches and reports the error then we receive an error_frame and use
        that as the source of the error.

        NOTE: msg should not end in a '.' for consistency.
        
py3status/core.py