# Buggy code
def _default_to_pandas(self, op, *args, **kwargs):
    """Helper method to use default pandas function"""
    empty_self_str = "" if not self.empty else " for empty DataFrame"
    ErrorMessage.default_to_pandas(
        "`{}.{}`{}".format(
            self.__name__,
            op if isinstance(op, str) else op.__name__,
            empty_self_str,
        )
    )

    args = try_cast_to_pandas(args)
    kwargs = try_cast_to_pandas(kwargs)
    pandas_obj = self._to_pandas()
    if callable(op):
        result = op(pandas_obj, *args, **kwargs)
    elif isinstance(op, str):
        # The inner `getattr` is ensuring that we are treating this object (whether
        # it is a DataFrame, Series, etc.) as a pandas object. The outer `getattr`
        # will get the operation (`op`) from the pandas version of the class and run
        # it on the object after we have converted it to pandas.
        result = getattr(getattr(pandas, self.__name__), op)(
            pandas_obj, *args, **kwargs
        )
    else:
        ErrorMessage.catch_bugs_and_request_email(
            failure_condition=True,
            extra_log="{} is an unsupported operation".format(op),
        )
    # SparseDataFrames cannot be serialized by arrow and cause problems for Modin.
    # For now we will use pandas.
    if isinstance(result, type(self)) and not isinstance(
        result, (pandas.SparseDataFrame, pandas.SparseSeries)
    ):
        return self._create_or_update_from_compiler(
            result, inplace=kwargs.get("inplace", False)
        )
    elif isinstance(result, pandas.DataFrame):
        from .dataframe import DataFrame

        return DataFrame(result)
    elif isinstance(result, pandas.Series):
        from .series import Series

        return Series(result)
    # inplace
    elif result is None:
        import modin.pandas as pd

        return self._create_or_update_from_compiler(
            getattr(pd, type(pandas_obj).__name__)(pandas_obj)._query_compiler,
            inplace=True,
        )
    else:
        try:
            if (
                isinstance(result, (list, tuple))
                and len(result) == 2
                and isinstance(result[0], pandas.DataFrame)
            ):
                # Some operations split the DataFrame into two (e.g. align). We need to wrap
                # both of the returned results
                if isinstance(result[1], pandas.DataFrame):
                    second = self.__constructor__(result[1])
                else:
                    second = result[1]
                return self.__constructor__(result[0]), second
            else:
                return result
        except TypeError:
            return result


# Traceback (RecursionError)
Traceback (most recent call last):
File "1.py", line 14, in <module>
ref = df["a"].add(df["b"])
File "/localdisk/ilyaenko/modin/modin/pandas/series.py", line 376, in add
return super(Series, new_self).add(
File "/localdisk/ilyaenko/modin/modin/pandas/series.py", line 376, in add
return super(Series, new_self).add(
File "/localdisk/ilyaenko/modin/modin/pandas/series.py", line 376, in add
return super(Series, new_self).add(
[Previous line repeated 978 more times]
...
RecursionError: maximum recursion depth exceeded while calling a Python object

# Fixed code
def _default_to_pandas(self, op, *args, **kwargs):
    """Helper method to use default pandas function"""
    empty_self_str = "" if not self.empty else " for empty DataFrame"
    ErrorMessage.default_to_pandas(
        "`{}.{}`{}".format(
            type(self).__name__,
            op if isinstance(op, str) else op.__name__,
            empty_self_str,
        )
    )

    args = try_cast_to_pandas(args)
    kwargs = try_cast_to_pandas(kwargs)
    pandas_obj = self._to_pandas()
    if callable(op):
        result = op(pandas_obj, *args, **kwargs)
    elif isinstance(op, str):
        # The inner `getattr` is ensuring that we are treating this object (whether
        # it is a DataFrame, Series, etc.) as a pandas object. The outer `getattr`
        # will get the operation (`op`) from the pandas version of the class and run
        # it on the object after we have converted it to pandas.
        result = getattr(getattr(pandas, type(self).__name__), op)(
            pandas_obj, *args, **kwargs
        )
    else:
        ErrorMessage.catch_bugs_and_request_email(
            failure_condition=True,
            extra_log="{} is an unsupported operation".format(op),
        )
    # SparseDataFrames cannot be serialized by arrow and cause problems for Modin.
    # For now we will use pandas.
    if isinstance(result, type(self)) and not isinstance(
        result, (pandas.SparseDataFrame, pandas.SparseSeries)
    ):
        return self._create_or_update_from_compiler(
            result, inplace=kwargs.get("inplace", False)
        )
    elif isinstance(result, pandas.DataFrame):
        from .dataframe import DataFrame

        return DataFrame(result)
    elif isinstance(result, pandas.Series):
        from .series import Series

        return Series(result)
    # inplace
    elif result is None:
        import modin.pandas as pd

        return self._create_or_update_from_compiler(
            getattr(pd, type(pandas_obj).__name__)(pandas_obj)._query_compiler,
            inplace=True,
        )
    else:
        try:
            if (
                isinstance(result, (list, tuple))
                and len(result) == 2
                and isinstance(result[0], pandas.DataFrame)
            ):
                # Some operations split the DataFrame into two (e.g. align). We need to wrap
                # both of the returned results
                if isinstance(result[1], pandas.DataFrame):
                    second = self.__constructor__(result[1])
                else:
                    second = result[1]
                return self.__constructor__(result[0]), second
            else:
                return result
        except TypeError:
            return result