Repository: modin-project/modin

Issue:
read_csv failed on omnisci backend
### System information
- **OS Platform and Distribution (e.g., Linux Ubuntu 16.04)**: Ubuntu 20.04
- **Modin version** (`modin.__version__`): c6393b54639b75ad38f028ba06c6eb1e43bfb790
- **Python version**: 3.7.10
- **Code we can use to reproduce**:

```python
import os

os.environ["MODIN_ENGINE"] = "ray"
os.environ["MODIN_BACKEND"] = "omnisci"
os.environ["MODIN_EXPERIMENTAL"] = "True"

import modin.pandas as pd
import pandas

content = """
1,2,2013-08-01
2,2,2013-08-01
"""
filename = "test.csv"
with open(filename, mode="w+") as _f:
    _f.write(content)


columns_names = ["trip_id", "vendor_id", "pickup_datetime"]
print(pandas.read_csv(filename, names=columns_names, parse_dates=["pickup_datetime"]))
print(pd.read_csv(filename, names=columns_names, parse_dates=["pickup_datetime"]))
```

<!--
You can obtain the Modin version with

python -c "import modin; print(modin.__version__)"
-->

### Describe the problem
<!-- Describe the problem clearly here. -->
Modin on OmniSci raise Exception while Pandas no.

### Source code / logs
<!-- Include any logs or source code that would be helpful to diagnose the problem. If including tracebacks, please include the full traceback. Large logs and files should be attached. Try to provide a reproducible test case that is the bare minimum necessary to generate the problem. -->

```python
~/amyachev_test/omniscripts$ python test.py
   trip_id  vendor_id pickup_datetime
0        1          2      2013-08-01
1        2          2      2013-08-01
FutureWarning: pandas.core.index is deprecated and will be removed in a future version.  The public classes are available in the top-level namespace.
UserWarning: Ray execution environment not yet initialized. Initializing...
To remove this warning, run the following python code before doing dataframe operations:

    import ray
    ray.init()

Traceback (most recent call last):
  File "test.py", line 21, in <module>
    print(pd.read_csv(filename, names=columns_names, parse_dates=["pickup_datetime"]))
  File ".../modin/pandas/io.py", line 133, in read_csv
    return _read(**kwargs)
  File ".../modin/pandas/io.py", line 59, in _read
    pd_obj = FactoryDispatcher.read_csv(**kwargs)
  File ".../modin/data_management/factories/dispatcher.py", line 172, in read_csv
    return cls.__factory._read_csv(**kwargs)
  File ".../modin/data_management/factories/factories.py", line 206, in _read_csv
    return cls.io_cls.read_csv(**kwargs)
  File ".../modin/experimental/engines/omnisci_on_ray/io.py", line 210, in read_csv
    mykwargs,
  File ".../modin/experimental/engines/omnisci_on_ray/io.py", line 435, in _read_csv_check_support
    engine=None if engine == "arrow" else engine,
  File ".../pandas/io/parsers.py", line 610, in read_csv
    return _read(filepath_or_buffer, kwds)
  File ".../pandas/io/parsers.py", line 462, in _read
    parser = TextFileReader(filepath_or_buffer, **kwds)
  File ".../pandas/io/parsers.py", line 819, in __init__
    self._engine = self._make_engine(self.engine)
  File ".../pandas/io/parsers.py", line 1050, in _make_engine
    return mapping[engine](self.f, **self.options)  # type: ignore[call-arg]
  File ".../pandas/io/parsers.py", line 1937, in __init__
    self._validate_parse_dates_presence(self.names)
  File ".../pandas/io/parsers.py", line 1417, in _validate_parse_dates_presence
    f"Missing column provided to 'parse_dates': '{missing_cols}'"
ValueError: Missing column provided to 'parse_dates': 'pickup_datetime'
```




Discussion:

Resolving patch:
diff --git a/modin/experimental/engines/omnisci_on_ray/io.py b/modin/experimental/engines/omnisci_on_ray/io.py
--- a/modin/experimental/engines/omnisci_on_ray/io.py
+++ b/modin/experimental/engines/omnisci_on_ray/io.py
@@ -425,6 +425,7 @@ def _read_csv_check_support(
         engine = read_csv_kwargs.get("engine", None)
         skiprows = read_csv_kwargs.get("skiprows", None)
         delimiter = read_csv_kwargs.get("delimiter", None)
+        parse_dates = read_csv_kwargs.get("parse_dates", False)
 
         if read_csv_kwargs.get("compression", "infer") != "infer":
             return (
@@ -464,6 +465,15 @@ def _read_csv_check_support(
                 "Specified a delimiter with both sep and delim_whitespace=True; you can only specify one."
             )
 
+        parse_dates_unsupported = isinstance(parse_dates, dict) or (
+            isinstance(parse_dates, list) and isinstance(parse_dates[0], list)
+        )
+        if parse_dates_unsupported:
+            return (
+                False,
+                "read_csv with 'arrow' engine supports only bool and "
+                "flattened lists 'parse_dates' parameter",
+            )
         if names and names != lib.no_default:
             if header not in [None, 0, "infer"]:
                 return (
@@ -471,6 +481,8 @@ def _read_csv_check_support(
                     "read_csv with 'arrow' engine and provided 'names' parameter supports only 0, None and "
                     "'infer' header values",
                 )
+            if isinstance(parse_dates, list) and not set(parse_dates).issubset(names):
+                raise ValueError("Missing column provided to 'parse_dates'")
 
             empty_pandas_df = pandas.read_csv(
                 **dict(
@@ -481,6 +493,7 @@ def _read_csv_check_support(
                     usecols=None,
                     index_col=None,
                     names=None,
+                    parse_dates=None,
                     engine=None if engine == "arrow" else engine,
                 ),
             )
@@ -498,6 +511,20 @@ def _read_csv_check_support(
                     "read_csv with 'arrow' engine without 'names' parameter provided supports only 0 and 'infer' "
                     "header values",
                 )
+            if isinstance(parse_dates, list):
+                empty_pandas_df = pandas.read_csv(
+                    **dict(
+                        read_csv_kwargs,
+                        nrows=0,
+                        skiprows=None,
+                        skipfooter=0,
+                        usecols=None,
+                        index_col=None,
+                        engine=None if engine == "arrow" else engine,
+                    ),
+                )
+                if not set(parse_dates).issubset(empty_pandas_df.columns):
+                    raise ValueError("Missing column provided to 'parse_dates'")
 
         if not read_csv_kwargs.get("skip_blank_lines", True):
             # in some corner cases empty lines are handled as '',


Test patch:
diff --git a/modin/experimental/engines/omnisci_on_ray/test/test_dataframe.py b/modin/experimental/engines/omnisci_on_ray/test/test_dataframe.py
--- a/modin/experimental/engines/omnisci_on_ray/test/test_dataframe.py
+++ b/modin/experimental/engines/omnisci_on_ray/test/test_dataframe.py
@@ -19,6 +19,7 @@
 import re
 
 from modin.config import IsExperimental, Engine, Backend
+from modin.pandas.test.utils import io_ops_bad_exc
 
 IsExperimental.put(True)
 Engine.put("ray")
@@ -317,20 +318,40 @@ def test_float32(self):
             True,
             False,
             ["col2"],
+            ["c2"],
+            [["col2", "col3"]],
+            {"col23": ["col2", "col3"]},
         ],
     )
+    @pytest.mark.parametrize("names", [None, [f"c{x}" for x in range(1, 7)]])
     def test_read_csv_datetime(
         self,
         engine,
         parse_dates,
+        names,
     ):
 
+        parse_dates_unsupported = isinstance(parse_dates, dict) or (
+            isinstance(parse_dates, list) and isinstance(parse_dates[0], list)
+        )
+        if parse_dates_unsupported and engine == "arrow" and not names:
+            pytest.skip(
+                "In these cases Modin raises `ArrowEngineException` while pandas "
+                "doesn't raise any exceptions that causes tests fails"
+            )
+        # In these cases Modin raises `ArrowEngineException` while pandas
+        # raises `ValueError`, so skipping exception type checking
+        skip_exc_type_check = parse_dates_unsupported and engine == "arrow"
+
         eval_io(
             fn_name="read_csv",
             md_extra_kwargs={"engine": engine},
+            check_exception_type=not skip_exc_type_check,
+            raising_exceptions=None if skip_exc_type_check else io_ops_bad_exc,
             # read_csv kwargs
             filepath_or_buffer=pytest.csvs_names["test_read_csv_regular"],
             parse_dates=parse_dates,
+            names=names,
         )
 
     @pytest.mark.parametrize("engine", [None, "arrow"])