# Buggy code
def copy_df_for_func(func):
    """Create a function that copies the dataframe, likely because `func` is inplace.

    Args:
        func: The function, usually updates a dataframe inplace.

    Returns:
        A callable function to be applied in the partitions
    """

    def caller(df, *args, **kwargs):
        df = df.copy()
        func(df, *args, **kwargs)
        return df

    return caller


# Traceback (IndexError)
import modin.pandas as pd
df = pd.DataFrame({'A': [1, 2, 3], 'B': [400, 500, 600]})
new_df = pd.DataFrame({'B': [4, 5, 6], 'C': [7, 8, 9]})
df.update(new_df)
df
IndexError                                Traceback (most recent call last)
python3.7/site-packages/IPython/core/formatters.py in __call__(self, obj)
700                 type_pprinters=self.type_printers,
701                 deferred_pprinters=self.deferred_printers)
--> 702             printer.pretty(obj)
703             printer.flush()
704             return stream.getvalue()

python3.7/site-packages/IPython/lib/pretty.py in pretty(self, obj)
392                         if cls is not object \
393                                 and callable(cls.__dict__.get('__repr__')):
--> 394                             return _repr_pprint(obj, self, cycle)
395
396             return _default_pprint(obj, self, cycle)

python3.7/site-packages/IPython/lib/pretty.py in _repr_pprint(obj, p, cycle)
682     """A pprint that just redirects to the normal repr function."""
683     # Find newlines and replace them with p.break_()
--> 684     output = repr(obj)
685     lines = output.splitlines()
686     with p.group():

modin/modin/pandas/dataframe.py in __repr__(self)
151
152             num_cols += len(self.columns) - i
--> 153         result = repr(self._build_repr_df(num_rows, num_cols))
154         if len(self.index) > num_rows or len(self.columns) > num_cols:
155             # The split here is so that we don't repr pandas row lengths.

modin/modin/pandas/base.py in _build_repr_df(self, num_rows, num_cols)
98         else:
99             indexer = row_indexer
--> 100         return self.iloc[indexer]._query_compiler.to_pandas()
101
102     def _update_inplace(self, new_query_compiler):

modin/modin/backends/pandas/query_compiler.py in to_pandas(self)
173
174     def to_pandas(self):
--> 175         return self._modin_frame.to_pandas()
176
177     @classmethod

modin/modin/engines/base/frame/data.py in to_pandas(self)
1291             Pandas DataFrame.
1292         """
-> 1293         df = self._frame_mgr_cls.to_pandas(self._partitions)
1294         if df.empty:
1295             if len(self.columns) != 0:

modin/modin/engines/base/frame/partition_manager.py in to_pandas(cls, partitions)
256             A Pandas DataFrame
257         """
--> 258         retrieved_objects = [[obj.to_pandas() for obj in part] for part in partitions]
259         if all(
260             isinstance(part, pandas.Series) for row in retrieved_objects for part in row

modin/modin/engines/base/frame/partition_manager.py in <listcomp>(.0)
256             A Pandas DataFrame
257         """
--> 258         retrieved_objects = [[obj.to_pandas() for obj in part] for part in partitions]
259         if all(
260             isinstance(part, pandas.Series) for row in retrieved_objects for part in row

modin/modin/engines/base/frame/partition_manager.py in <listcomp>(.0)
256             A Pandas DataFrame
257         """
--> 258         retrieved_objects = [[obj.to_pandas() for obj in part] for part in partitions]
259         if all(
260             isinstance(part, pandas.Series) for row in retrieved_objects for part in row

modin/modin/engines/python/pandas_on_python/frame/partition.py in to_pandas(self)
113             A Pandas DataFrame.
114         """
--> 115         dataframe = self.get()
116         assert type(dataframe) is pandas.DataFrame or type(dataframe) is pandas.Series
117

modin/modin/engines/python/pandas_on_python/frame/partition.py in get(self)
46             The object that was `put`.
47         """
---> 48         self.drain_call_queue()
49         return self.data.copy()
50

modin/modin/engines/python/pandas_on_python/frame/partition.py in drain_call_queue(self)
86         if len(self.call_queue) == 0:
87             return
---> 88         self.apply(lambda x: x)
89
90     def mask(self, row_indices=None, col_indices=None):

modin/modin/engines/python/pandas_on_python/frame/partition.py in apply(self, func, **kwargs)
74             return result
75
---> 76         self.data = call_queue_closure(self.data, self.call_queue)
77         self.call_queue = []
78         return PandasOnPythonFramePartition(func(self.data.copy(), **kwargs))

modin/modin/engines/python/pandas_on_python/frame/partition.py in call_queue_closure(data, call_queues)
71                 except Exception as e:
72                     self.call_queue = []
---> 73                     raise e
74             return result
75

modin/modin/engines/python/pandas_on_python/frame/partition.py in call_queue_closure(data, call_queues)
68             for func, kwargs in call_queues:
69                 try:
---> 70                     result = func(result, **kwargs)
71                 except Exception as e:
72                     self.call_queue = []

modin/modin/engines/python/pandas_on_python/frame/partition.py in <lambda>(df)
90     def mask(self, row_indices=None, col_indices=None):
91         new_obj = self.add_to_apply_calls(
---> 92             lambda df: pandas.DataFrame(df.iloc[row_indices, col_indices])
93         )
94         new_obj._length_cache = (

python3.7/site-packages/pandas/core/indexing.py in __getitem__(self, key)
1760                 except (KeyError, IndexError, AttributeError):
1761                     pass
-> 1762             return self._getitem_tuple(key)
1763         else:
1764             # we by definition only have the 0th axis

python3.7/site-packages/pandas/core/indexing.py in _getitem_tuple(self, tup)
2065     def _getitem_tuple(self, tup: Tuple):
2066
-> 2067         self._has_valid_tuple(tup)
2068         try:
2069             return self._getitem_lowerdim(tup)

python3.7/site-packages/pandas/core/indexing.py in _has_valid_tuple(self, key)
701                 raise IndexingError("Too many indexers")
702             try:
--> 703                 self._validate_key(k, i)
704             except ValueError:
705                 raise ValueError(

python3.7/site-packages/pandas/core/indexing.py in _validate_key(self, key, axis)
2007             # check that the key does not exceed the maximum size of the index
2008             if len(arr) and (arr.max() >= len_axis or arr.min() < -len_axis):
-> 2009                 raise IndexError("positional indexers are out-of-bounds")
2010         else:
2011             raise ValueError(f"Can only index by location with a [{self._valid_types}]")

IndexError: positional indexers are out-of-bounds

# Fixed code
def copy_df_for_func(func):
    """
    Create a function that copies the dataframe, likely because `func` is inplace.

    Parameters
    ----------
    func : callable
        The function, usually updates a dataframe inplace.

    Returns
    -------
    callable
        A callable function to be applied in the partitions
    """

    def caller(df, *args, **kwargs):
        df = df.copy()
        func(df, *args, **kwargs)
        return df

    return caller