Repository: modin-project/modin

Issue:
Empty dataframe inplace operations (e.g. `__setitem__` do not change the dataframe inplace)

### Describe the problem
<!-- Describe the problem clearly here. -->
This was reported via email.

Empty dataframe inplace operations (e.g. `__setitem__` do not change the dataframe inplace). The defaulting to pandas code clause for `inplace` updates needs to be updated to accommodate empty dataframes.

### Source code / logs
```python
from modin import pandas as pd 
df = pd.DataFrame()
df["a"] = [1,2,3,4,45,1]
df["b"] = [1,2,3,45,6,7]
```



Discussion:

Resolving patch:
diff --git a/modin/pandas/base.py b/modin/pandas/base.py
--- a/modin/pandas/base.py
+++ b/modin/pandas/base.py
@@ -217,17 +217,23 @@ def _default_to_pandas(self, op, *args, **kwargs):
             k: v._to_pandas() if hasattr(v, "_to_pandas") else v
             for k, v in kwargs.items()
         }
+        pandas_obj = self._to_pandas()
         if callable(op):
-            result = op(self._to_pandas(), *args, **kwargs)
+            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)(
-                self._to_pandas(), *args, **kwargs
+                pandas_obj, *args, **kwargs
             )
-        # SparseDataFrames cannot be serialize by arrow and cause problems for Modin.
+        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)
@@ -243,6 +249,14 @@ def _default_to_pandas(self, op, *args, **kwargs):
             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 (
@@ -3415,6 +3429,8 @@ def __getattribute__(self, item):
             "_reduce_dimension",
             "__repr__",
             "__len__",
+            "_create_or_update_from_compiler",
+            "_update_inplace",
         ]
         if item not in default_behaviors:
             method = object.__getattribute__(self, item)


Test patch:
diff --git a/modin/pandas/test/test_dataframe.py b/modin/pandas/test/test_dataframe.py
--- a/modin/pandas/test/test_dataframe.py
+++ b/modin/pandas/test/test_dataframe.py
@@ -575,6 +575,18 @@ def test_empty_df(self):
         assert len(df.index) == 0
         assert len(df.columns) == 0
 
+        df = pd.DataFrame()
+        pd_df = pandas.DataFrame()
+        df["a"] = [1, 2, 3, 4, 5]
+        pd_df["a"] = [1, 2, 3, 4, 5]
+        df_equals(df, pd_df)
+
+        df = pd.DataFrame()
+        pd_df = pandas.DataFrame()
+        df["a"] = list("ABCDEF")
+        pd_df["a"] = list("ABCDEF")
+        df_equals(df, pd_df)
+
     @pytest.mark.parametrize("data", test_data_values, ids=test_data_keys)
     def test_abs(self, request, data):
         modin_df = pd.DataFrame(data)