# Buggy code
def caller_attributes(cls, module_name: str = "") -> Tuple[str, str, str]:
    """
    Determine extra or changed fields for the caller.

    1. qualname finds the relevant object and its __qualname__
    2. caller_class_names is just the full class names of the calling
     class if relevant.
    3. module is munged if we detect the caller is an EDMC plugin,
     whether internal or found.
    """
    frame = cls.find_caller_frame()

    caller_qualname = caller_class_names = ""
    if frame:
        # <https://stackoverflow.com/questions/2203424/python-how-to-retrieve-class-information-from-a-frame-object#2220759>
        try:
            frame_info = inspect.getframeinfo(frame)
            # raise(IndexError)  # TODO: Remove, only for testing

        except Exception:
            # Separate from the print below to guarantee we see at least this much.
            print(
                "EDMCLogging:EDMCContextFilter:caller_attributes(): Failed in `inspect.getframinfo(frame)`"
            )

            # We want to *attempt* to show something about the nature of 'frame',
            # but at this point we can't trust it will work.
            try:
                print(f"frame: {frame}")

            except Exception:
                pass

            # We've given up, so just return all '??' to signal we couldn't get the info
            return "??", "??", "??"

        args, _, _, value_dict = inspect.getargvalues(frame)
        if len(args) and args[0] in ("self", "cls"):
            frame_class: "object" = value_dict[args[0]]

            if frame_class:
                # See https://en.wikipedia.org/wiki/Name_mangling#Python for how name mangling works.
                name = frame_info.function
                if name.startswith("__") and not name.endswith("__"):
                    name = f"_{frame_class.__class__.__name__}{frame_info.function}"

                # Find __qualname__ of the caller
                fn = getattr(frame_class, name, None)
                if fn and fn.__qualname__:
                    caller_qualname = fn.__qualname__

                # Find containing class name(s) of caller, if any
                if frame_class.__class__ and frame_class.__class__.__qualname__:
                    caller_class_names = frame_class.__class__.__qualname__

        # It's a call from the top level module file
        elif frame_info.function == "<module>":
            caller_class_names = "<none>"
            caller_qualname = value_dict["__name__"]

        elif frame_info.function != "":
            caller_class_names = "<none>"
            caller_qualname = frame_info.function

        module_name = cls.munge_module_name(frame_info, module_name)

        # https://docs.python.org/3.7/library/inspect.html#the-interpreter-stack
        del frame

    if caller_qualname == "":
        print("ALERT!  Something went wrong with finding caller qualname for logging!")
        caller_qualname = (
            '<ERROR in EDMCLogging.caller_class_and_qualname() for "qualname">'
        )

    if caller_class_names == "":
        print(
            "ALERT!  Something went wrong with finding caller class name(s) for logging!"
        )
        caller_class_names = (
            '<ERROR in EDMCLogging.caller_class_and_qualname() for "class">'
        )

    return caller_class_names, caller_qualname, module_name


# Traceback (RecursionError)
File "/home/ash/.local/share/EDMarketConnector/plugins/edrodent/load.py", line 38, in ready_to_rat
logger.debug("ready_to_rat: %r %r %r %r %r", self.open, self.low_fuel, self.hud_in_analysis_mode, self.has_fuel_transfer_limpet_controller, self.limpets)
File "/usr/local/lib/python3.7/logging/__init__.py", line 1366, in debug
self._log(DEBUG, msg, args, **kwargs)
File "/usr/local/lib/python3.7/logging/__init__.py", line 1514, in _log
self.handle(record)
File "/usr/local/lib/python3.7/logging/__init__.py", line 1523, in handle
if (not self.disabled) and self.filter(record):
File "/usr/local/lib/python3.7/logging/__init__.py", line 751, in filter
result = f.filter(record)
File "/home/ash/src/EDMarketConnector/EDMCLogging.py", line 255, in filter
(class_name, qualname, module_name) = self.caller_attributes(module_name=getattr(record, 'module'))
File "/home/ash/src/EDMarketConnector/EDMCLogging.py", line 317, in caller_attributes
fn = getattr(frame_class, name, None)
File "/home/ash/.local/share/EDMarketConnector/plugins/edrodent/load.py", line 38, in ready_to_rat
logger.debug("ready_to_rat: %r %r %r %r %r", self.open, self.low_fuel, self.hud_in_analysis_mode, self.has_fuel_transfer_limpet_controller, self.limpets)
File "/usr/local/lib/python3.7/logging/__init__.py", line 1366, in debug
self._log(DEBUG, msg, args, **kwargs)
File "/usr/local/lib/python3.7/logging/__init__.py", line 1513, in _log
exc_info, func, extra, sinfo)
File "/usr/local/lib/python3.7/logging/__init__.py", line 1483, in makeRecord
sinfo)
File "/usr/local/lib/python3.7/logging/__init__.py", line 316, in __init__
self.filename = os.path.basename(pathname)
File "/usr/local/lib/python3.7/posixpath.py", line 147, in basename
sep = _get_sep(p)
File "/usr/local/lib/python3.7/posixpath.py", line 42, in _get_sep
if isinstance(path, bytes):
RecursionError: maximum recursion depth exceeded while calling a Python object

# Fixed code
def caller_attributes(cls, module_name: str = "") -> Tuple[str, str, str]:
    """
    Determine extra or changed fields for the caller.

    1. qualname finds the relevant object and its __qualname__
    2. caller_class_names is just the full class names of the calling
     class if relevant.
    3. module is munged if we detect the caller is an EDMC plugin,
     whether internal or found.
    """
    frame = cls.find_caller_frame()

    caller_qualname = caller_class_names = ""
    if frame:
        # <https://stackoverflow.com/questions/2203424/python-how-to-retrieve-class-information-from-a-frame-object#2220759>
        try:
            frame_info = inspect.getframeinfo(frame)
            # raise(IndexError)  # TODO: Remove, only for testing

        except Exception:
            # Separate from the print below to guarantee we see at least this much.
            print(
                "EDMCLogging:EDMCContextFilter:caller_attributes(): Failed in `inspect.getframinfo(frame)`"
            )

            # We want to *attempt* to show something about the nature of 'frame',
            # but at this point we can't trust it will work.
            try:
                print(f"frame: {frame}")

            except Exception:
                pass

            # We've given up, so just return all '??' to signal we couldn't get the info
            return "??", "??", "??"

        args, _, _, value_dict = inspect.getargvalues(frame)
        if len(args) and args[0] in ("self", "cls"):
            frame_class: "object" = value_dict[args[0]]

            if frame_class:
                # See https://en.wikipedia.org/wiki/Name_mangling#Python for how name mangling works.
                name = frame_info.function
                if name.startswith("__") and not name.endswith("__"):
                    name = f"_{frame_class.__class__.__name__}{frame_info.function}"

                # Find __qualname__ of the caller
                fn = inspect.getattr_static(frame_class, name, None)
                if fn is None:
                    # For some reason getattr_static cant grab this. Try and grab it with getattr, bail out
                    # if we get a RecursionError indicating a property
                    try:
                        fn = getattr(frame_class, name, None)
                    except RecursionError:
                        print(
                            "EDMCLogging:EDMCContextFilter:caller_attributes():"
                            "Failed to get attribute for function info. Bailing out"
                        )
                        return "??", "??", "??"

                if fn is not None:
                    if isinstance(fn, property):
                        class_name = str(frame_class)
                        # If somehow you make your __class__ or __class__.__qualname__ recursive, I'll be impressed.
                        if hasattr(frame_class, "__class__") and hasattr(
                            frame_class.__class__, "__qualname__"
                        ):
                            class_name = frame_class.__class__.__qualname__
                            caller_qualname = f"{class_name}.{name}(property)"

                        else:
                            caller_qualname = f"<property {name} on {class_name}>"

                    elif fn.__qualname__:
                        caller_qualname = fn.__qualname__

                # Find containing class name(s) of caller, if any
                if frame_class.__class__ and frame_class.__class__.__qualname__:
                    caller_class_names = frame_class.__class__.__qualname__

        # It's a call from the top level module file
        elif frame_info.function == "<module>":
            caller_class_names = "<none>"
            caller_qualname = value_dict["__name__"]

        elif frame_info.function != "":
            caller_class_names = "<none>"
            caller_qualname = frame_info.function

        module_name = cls.munge_module_name(frame_info, module_name)

        # https://docs.python.org/3.7/library/inspect.html#the-interpreter-stack
        del frame

    if caller_qualname == "":
        print("ALERT!  Something went wrong with finding caller qualname for logging!")
        caller_qualname = (
            '<ERROR in EDMCLogging.caller_class_and_qualname() for "qualname">'
        )

    if caller_class_names == "":
        print(
            "ALERT!  Something went wrong with finding caller class name(s) for logging!"
        )
        caller_class_names = (
            '<ERROR in EDMCLogging.caller_class_and_qualname() for "class">'
        )

    return caller_class_names, caller_qualname, module_name