# Buggy code
def parse_index(index_value, *args, store_data=False, key=None):
    from .core import IndexValue

    def _extract_property(index, tp, ret_data):
        kw = {
            "_min_val": _get_index_min(index),
            "_max_val": _get_index_max(index),
            "_min_val_close": True,
            "_max_val_close": True,
            "_key": key or _tokenize_index(index, *args),
        }
        if ret_data:
            kw["_data"] = index.values
        for field in tp._FIELDS:
            if field in kw or field == "_data":
                continue
            val = getattr(index, field.lstrip("_"), None)
            if val is not None:
                kw[field] = val
        return kw

    def _tokenize_index(index, *token_objects):
        if not index.empty:
            return tokenize(index)
        else:
            return tokenize(index, *token_objects)

    def _get_index_min(index):
        try:
            return index.min()
        except ValueError:
            if isinstance(index, pd.IntervalIndex):
                return None
            raise
        except TypeError:
            return None

    def _get_index_max(index):
        try:
            return index.max()
        except ValueError:
            if isinstance(index, pd.IntervalIndex):
                return None
            raise
        except TypeError:
            return None

    def _serialize_index(index):
        tp = getattr(IndexValue, type(index).__name__)
        properties = _extract_property(index, tp, store_data)
        return tp(**properties)

    def _serialize_range_index(index):
        if is_pd_range_empty(index):
            properties = {
                "_is_monotonic_increasing": True,
                "_is_monotonic_decreasing": False,
                "_is_unique": True,
                "_min_val": _get_index_min(index),
                "_max_val": _get_index_max(index),
                "_min_val_close": True,
                "_max_val_close": False,
                "_key": key or _tokenize_index(index, *args),
                "_name": index.name,
                "_dtype": index.dtype,
            }
        else:
            properties = _extract_property(index, IndexValue.RangeIndex, False)
        return IndexValue.RangeIndex(
            _slice=slice(
                _get_range_index_start(index),
                _get_range_index_stop(index),
                _get_range_index_step(index),
            ),
            **properties,
        )

    def _serialize_multi_index(index):
        kw = _extract_property(index, IndexValue.MultiIndex, store_data)
        kw["_sortorder"] = index.sortorder
        return IndexValue.MultiIndex(**kw)

    if index_value is None:
        return IndexValue(
            _index_value=IndexValue.Index(
                _is_monotonic_increasing=False,
                _is_monotonic_decreasing=False,
                _is_unique=False,
                _min_val=None,
                _max_val=None,
                _min_val_close=True,
                _max_val_close=True,
                _key=key or tokenize(*args),
            )
        )
    if isinstance(index_value, pd.RangeIndex):
        return IndexValue(_index_value=_serialize_range_index(index_value))
    elif isinstance(index_value, pd.MultiIndex):
        return IndexValue(_index_value=_serialize_multi_index(index_value))
    else:
        return IndexValue(_index_value=_serialize_index(index_value))


# Traceback (TypeError)
In [1]: import mars.dataframe as md

In [2]: df = md.DataFrame({'a': [1, 2, 3], 'b': ['a', 'b', 'c']})

In [3]: df['b'] = df['b'].astype(md.ArrowStringDtype())

In [6]: df.groupby('b').count()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
~/miniconda3/lib/python3.7/site-packages/pandas/core/construction.py in _try_cast(arr, dtype, copy, raise_cast_failure)
545         elif not is_extension_array_dtype(subarr):
--> 546             subarr = construct_1d_ndarray_preserving_na(subarr, dtype, copy=copy)
547     except OutOfBoundsDatetime:

~/miniconda3/lib/python3.7/site-packages/pandas/core/dtypes/cast.py in construct_1d_ndarray_preserving_na(values, dtype, copy)
1506     """
-> 1507     subarr = np.array(values, dtype=dtype, copy=copy)
1508

TypeError: Cannot interpret '<mars.dataframe.arrays.ArrowStringDtype object at 0x7f7f81874150>' as a data type

During handling of the above exception, another exception occurred:

TypeError                                 Traceback (most recent call last)
<ipython-input-6-884154032b17> in <module>
----> 1 df.groupby('b').count()

~/Workspace/mars/mars/dataframe/groupby/__init__.py in <lambda>(groupby, **kw)
40         setattr(cls, 'max', lambda groupby, **kw: agg(groupby, 'max', **kw))
41         setattr(cls, 'min', lambda groupby, **kw: agg(groupby, 'min', **kw))
---> 42         setattr(cls, 'count', lambda groupby, **kw: agg(groupby, 'count', **kw))
43         setattr(cls, 'size', lambda groupby, **kw: agg(groupby, 'size', **kw))
44         setattr(cls, 'mean', lambda groupby, **kw: agg(groupby, 'mean', **kw))

~/Workspace/mars/mars/dataframe/groupby/aggregation.py in agg(groupby, func, method, *args, **kwargs)
650     agg_op = DataFrameGroupByAgg(func=func, method=method, raw_func=func,
651                                  groupby_params=groupby.op.groupby_params)
--> 652     return agg_op(groupby)

~/Workspace/mars/mars/dataframe/groupby/aggregation.py in __call__(self, groupby)
201
202         if self.output_types[0] == OutputType.dataframe:
--> 203             return self._call_dataframe(groupby, df)
204         else:
205             return self._call_series(groupby, df)

~/Workspace/mars/mars/dataframe/groupby/aggregation.py in _call_dataframe(self, groupby, input_df)
144
145     def _call_dataframe(self, groupby, input_df):
--> 146         grouped = groupby.op.build_mock_groupby()
147         agg_df = grouped.aggregate(self.func)
148

~/Workspace/mars/mars/dataframe/groupby/core.py in build_mock_groupby(self, **kwargs)
98         in_df = self.inputs[0]
99         if self.is_dataframe_obj:
--> 100             empty_df = build_empty_df(in_df.dtypes, index=pd.RangeIndex(2))
101             obj_dtypes = in_df.dtypes[in_df.dtypes == np.dtype('O')]
102             empty_df[obj_dtypes.index] = 'O'

~/Workspace/mars/mars/dataframe/utils.py in build_empty_df(dtypes, index)
446     df = pd.DataFrame(columns=pd.RangeIndex(len(columns)), index=index)
447     for i, d in enumerate(dtypes):
--> 448         df[i] = pd.Series(dtype=d, index=index)
449     df.columns = columns
450     return df

~/miniconda3/lib/python3.7/site-packages/pandas/core/series.py in __init__(self, data, index, dtype, name, copy, fastpath)
254                 data = data._data
255             elif is_dict_like(data):
--> 256                 data, index = self._init_dict(data, index, dtype)
257                 dtype = None
258                 copy = False

~/miniconda3/lib/python3.7/site-packages/pandas/core/series.py in _init_dict(self, data, index, dtype)
347         # TODO: passing np.float64 to not break anything yet. See GH-17261
348         s = create_series_with_explicit_dtype(
--> 349             values, index=keys, dtype=dtype, dtype_if_empty=np.float64
350         )
351

~/miniconda3/lib/python3.7/site-packages/pandas/core/construction.py in create_series_with_explicit_dtype(data, index, dtype, name, copy, fastpath, dtype_if_empty)
623         dtype = dtype_if_empty
624     return Series(
--> 625         data=data, index=index, dtype=dtype, name=name, copy=copy, fastpath=fastpath
626     )

~/miniconda3/lib/python3.7/site-packages/pandas/core/series.py in __init__(self, data, index, dtype, name, copy, fastpath)
303                     data = data.copy()
304             else:
--> 305                 data = sanitize_array(data, index, dtype, copy, raise_cast_failure=True)
306
307                 data = SingleBlockManager(data, index, fastpath=True)

~/miniconda3/lib/python3.7/site-packages/pandas/core/construction.py in sanitize_array(data, index, dtype, copy, raise_cast_failure)
447         subarr = _try_cast(arr, dtype, copy, raise_cast_failure)
448     else:
--> 449         subarr = _try_cast(data, dtype, copy, raise_cast_failure)
450
451     # scalar like, GH

~/miniconda3/lib/python3.7/site-packages/pandas/core/construction.py in _try_cast(arr, dtype, copy, raise_cast_failure)
560             dtype = cast(ExtensionDtype, dtype)
561             array_type = dtype.construct_array_type()._from_sequence
--> 562             subarr = array_type(arr, dtype=dtype, copy=copy)
563         elif dtype is not None and raise_cast_failure:
564             raise

~/Workspace/mars/mars/dataframe/arrays.py in _from_sequence(cls, scalars, dtype, copy)
253     def _from_sequence(cls, scalars, dtype=None, copy=False):
254         if not hasattr(scalars, 'dtype'):
--> 255             ret = np.empty(len(scalars), dtype=object)
256             for i, s in enumerate(scalars):
257                 ret[i] = s

TypeError: object of type 'pyarrow.lib.NullScalar' has no len()

# Fixed code
def parse_index(index_value, *args, store_data=False, key=None):
    from .core import IndexValue

    def _extract_property(index, tp, ret_data):
        kw = {
            "_min_val": _get_index_min(index),
            "_max_val": _get_index_max(index),
            "_min_val_close": True,
            "_max_val_close": True,
            "_key": key or _tokenize_index(index, *args),
        }
        if ret_data:
            kw["_data"] = index.values
        for field in tp._FIELDS:
            if field in kw or field == "_data":
                continue
            val = getattr(index, field.lstrip("_"), None)
            if val is not None:
                kw[field] = val
        return kw

    def _tokenize_index(index, *token_objects):
        if not index.empty:
            return tokenize(index)
        else:
            return tokenize(index, *token_objects)

    def _get_index_min(index):
        try:
            return index.min()
        except ValueError:
            if isinstance(index, pd.IntervalIndex):
                return None
            raise
        except TypeError:
            return None

    def _get_index_max(index):
        try:
            return index.max()
        except ValueError:
            if isinstance(index, pd.IntervalIndex):
                return None
            raise
        except TypeError:
            return None

    def _serialize_index(index):
        tp = getattr(IndexValue, type(index).__name__)
        properties = _extract_property(index, tp, store_data)
        return tp(**properties)

    def _serialize_range_index(index):
        if is_pd_range_empty(index):
            properties = {
                "_is_monotonic_increasing": True,
                "_is_monotonic_decreasing": False,
                "_is_unique": True,
                "_min_val": _get_index_min(index),
                "_max_val": _get_index_max(index),
                "_min_val_close": True,
                "_max_val_close": False,
                "_key": key or _tokenize_index(index, *args),
                "_name": index.name,
                "_dtype": index.dtype,
            }
        else:
            properties = _extract_property(index, IndexValue.RangeIndex, False)
        return IndexValue.RangeIndex(
            _slice=slice(
                _get_range_index_start(index),
                _get_range_index_stop(index),
                _get_range_index_step(index),
            ),
            **properties,
        )

    def _serialize_multi_index(index):
        kw = _extract_property(index, IndexValue.MultiIndex, store_data)
        kw["_sortorder"] = index.sortorder
        kw["_dtypes"] = [lev.dtype for lev in index.levels]
        return IndexValue.MultiIndex(**kw)

    if index_value is None:
        return IndexValue(
            _index_value=IndexValue.Index(
                _is_monotonic_increasing=False,
                _is_monotonic_decreasing=False,
                _is_unique=False,
                _min_val=None,
                _max_val=None,
                _min_val_close=True,
                _max_val_close=True,
                _key=key or tokenize(*args),
            )
        )
    if isinstance(index_value, pd.RangeIndex):
        return IndexValue(_index_value=_serialize_range_index(index_value))
    elif isinstance(index_value, pd.MultiIndex):
        return IndexValue(_index_value=_serialize_multi_index(index_value))
    else:
        return IndexValue(_index_value=_serialize_index(index_value))