Repository: modin-project/modin

Issue:
Support `from_dataframe` for pandas storage format
To fully support the Dataframe exchange protocol for pandas storage format we should add `from_dataframe` to PandasQC and PandasDataframe classes.


Discussion:

Resolving patch:
diff --git a/modin/core/dataframe/pandas/dataframe/dataframe.py b/modin/core/dataframe/pandas/dataframe/dataframe.py
--- a/modin/core/dataframe/pandas/dataframe/dataframe.py
+++ b/modin/core/dataframe/pandas/dataframe/dataframe.py
@@ -2859,3 +2859,36 @@ def __dataframe__(self, nan_as_null: bool = False, allow_copy: bool = True):
         return PandasProtocolDataframe(
             self, nan_as_null=nan_as_null, allow_copy=allow_copy
         )
+
+    @classmethod
+    def from_dataframe(cls, df: "ProtocolDataframe") -> "PandasDataframe":
+        """
+        Convert a DataFrame implementing the dataframe exchange protocol to a Core Modin Dataframe.
+
+        See more about the protocol in https://data-apis.org/dataframe-protocol/latest/index.html.
+
+        Parameters
+        ----------
+        df : ProtocolDataframe
+            The DataFrame object supporting the dataframe exchange protocol.
+
+        Returns
+        -------
+        PandasDataframe
+            A new Core Modin Dataframe object.
+        """
+        if type(df) == cls:
+            return df
+
+        if not hasattr(df, "__dataframe__"):
+            raise ValueError(
+                "`df` does not support DataFrame exchange protocol, i.e. `__dataframe__` method"
+            )
+
+        from modin.core.dataframe.pandas.exchange.dataframe_protocol.from_dataframe import (
+            from_dataframe_to_pandas,
+        )
+
+        ErrorMessage.default_to_pandas(message="`from_dataframe`")
+        pandas_df = from_dataframe_to_pandas(df)
+        return cls.from_pandas(pandas_df)
diff --git a/modin/core/storage_formats/pandas/query_compiler.py b/modin/core/storage_formats/pandas/query_compiler.py
--- a/modin/core/storage_formats/pandas/query_compiler.py
+++ b/modin/core/storage_formats/pandas/query_compiler.py
@@ -274,9 +274,7 @@ def to_dataframe(self, nan_as_null: bool = False, allow_copy: bool = True):
 
     @classmethod
     def from_dataframe(cls, df, data_cls):
-        raise NotImplementedError(
-            "The selected execution does not implement the DataFrame exchange protocol yet."
-        )
+        return cls(data_cls.from_dataframe(df))
 
     # END Dataframe exchange protocol
 


Test patch:
diff --git a/modin/test/exchange/dataframe_protocol/pandas/__init__.py b/modin/test/exchange/dataframe_protocol/pandas/__init__.py
new file mode 100644
--- /dev/null
+++ b/modin/test/exchange/dataframe_protocol/pandas/__init__.py
@@ -0,0 +1,12 @@
+# Licensed to Modin Development Team under one or more contributor license agreements.
+# See the NOTICE file distributed with this work for additional information regarding
+# copyright ownership.  The Modin Development Team licenses this file to you under the
+# Apache License, Version 2.0 (the "License"); you may not use this file except in
+# compliance with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under
+# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
+# ANY KIND, either express or implied. See the License for the specific language
+# governing permissions and limitations under the License.
diff --git a/modin/test/exchange/dataframe_protocol/pandas/test_protocol.py b/modin/test/exchange/dataframe_protocol/pandas/test_protocol.py
new file mode 100644
--- /dev/null
+++ b/modin/test/exchange/dataframe_protocol/pandas/test_protocol.py
@@ -0,0 +1,41 @@
+# Licensed to Modin Development Team under one or more contributor license agreements.
+# See the NOTICE file distributed with this work for additional information regarding
+# copyright ownership.  The Modin Development Team licenses this file to you under the
+# Apache License, Version 2.0 (the "License"); you may not use this file except in
+# compliance with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under
+# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
+# ANY KIND, either express or implied. See the License for the specific language
+# governing permissions and limitations under the License.
+
+"""Dataframe exchange protocol tests that are specific for pandas storage format implementation."""
+
+import modin.pandas as pd
+from modin.pandas.utils import from_dataframe
+from modin.pandas.test.utils import df_equals, test_data
+from modin.test.test_utils import warns_that_defaulting_to_pandas
+
+
+def test_simple_import():
+    modin_df_producer = pd.DataFrame(test_data["int_data"])
+    # Our configuration in pytest.ini requires that we explicitly catch all
+    # instances of defaulting to pandas, this one raises a warning on `.from_dataframe`
+    with warns_that_defaulting_to_pandas():
+        modin_df_consumer = from_dataframe(modin_df_producer)
+
+    # TODO: the following assertions verify that `from_dataframe` doesn't return
+    # the same object untouched due to optimization branching, it actually should
+    # do so but the logic is not implemented yet, so the assertions are passing
+    # for now. It's required to replace the producer's type with a different one
+    # to consumer when we have some other implementation of the protocol as the
+    # assertions may start failing shortly.
+    assert modin_df_producer is not modin_df_consumer
+    assert (
+        modin_df_producer._query_compiler._modin_frame
+        is not modin_df_consumer._query_compiler._modin_frame
+    )
+
+    df_equals(modin_df_producer, modin_df_consumer)