# Buggy code
def __init__(
    self,
    obj,
    groupby_obj=None,
    keys=None,
    axis=0,
    level=None,
    grouper=None,
    exclusions=None,
    selection=None,
    as_index=True,
    sort=True,
    group_keys=True,
    squeeze=False,
    observed=False,
    mutated=False,
    grouper_cache=None,
):
    def fill_value(v, key):
        return v if v is not None or groupby_obj is None else getattr(groupby_obj, key)

    self.obj = obj
    self.keys = fill_value(keys, "keys")
    self.axis = fill_value(axis, "axis")
    self.level = fill_value(level, "level")
    self.exclusions = fill_value(exclusions, "exclusions")
    self.selection = selection
    self.as_index = fill_value(as_index, "as_index")
    self.sort = fill_value(sort, "sort")
    self.group_keys = fill_value(group_keys, "group_keys")
    self.squeeze = fill_value(squeeze, "squeeze")
    self.observed = fill_value(observed, "observed")
    self.mutated = fill_value(mutated, "mutated")

    if groupby_obj is None:
        if obj.ndim == 2:
            self.groupby_obj = DataFrameGroupBy(
                obj,
                keys=keys,
                axis=axis,
                level=level,
                grouper=grouper,
                exclusions=exclusions,
                as_index=as_index,
                group_keys=group_keys,
                squeeze=squeeze,
                observed=observed,
                mutated=mutated,
            )
        else:
            self.groupby_obj = SeriesGroupBy(
                obj,
                keys=keys,
                axis=axis,
                level=level,
                grouper=grouper,
                exclusions=exclusions,
                as_index=as_index,
                group_keys=group_keys,
                squeeze=squeeze,
                observed=observed,
                mutated=mutated,
            )
    else:
        self.groupby_obj = groupby_obj

    self.is_frame = isinstance(self.groupby_obj, DataFrameGroupBy)

    if grouper_cache:
        self.groupby_obj.grouper._cache = grouper_cache
    if selection:
        self.groupby_obj = self.groupby_obj[selection]


# Traceback (AttributeError)
In [1]: import pandas as pd; import numpy as np

In [2]: df = pd.DataFrame(np.random.rand(4, 3), index=np.arange(5, 1, -1))

In [4]: import mars.dataframe as md

In [5]: mdf = md.DataFrame(df)

In [6]: mdf.groupby(0).execute()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-6-491b51043e08> in <module>
----> 1 mdf.groupby(0).execute()

~/Workspace/mars/mars/core.py in execute(self, session, **kw)
426         if session is None:
427             session = Session.default_or_local()
--> 428         return session.run(self, **kw)
429
430     def fetch(self, session=None, **kw):

~/Workspace/mars/mars/session.py in run(self, *tileables, **kw)
181         tileables = tuple(mt.tensor(t) if not isinstance(t, (Entity, Base)) else t
182                           for t in tileables)
--> 183         result = self._sess.run(*tileables, **kw)
184
185         for t in tileables:

~/Workspace/mars/mars/session.py in run(self, *tileables, **kw)
88             # set number of running cores
89             self.context.set_ncores(kw['n_parallel'])
---> 90             res = self._executor.execute_tileables(tileables, **kw)
91             return res
92

~/Workspace/mars/mars/utils.py in _wrapped(*args, **kwargs)
380                 _kernel_mode.eager = False
381             _kernel_mode.eager_count = enter_eager_count + 1
--> 382             return func(*args, **kwargs)
383         finally:
384             _kernel_mode.eager_count -= 1

~/Workspace/mars/mars/utils.py in inner(*args, **kwargs)
468     def inner(*args, **kwargs):
469         with build_mode():
--> 470             return func(*args, **kwargs)
471     return inner
472

~/Workspace/mars/mars/executor.py in execute_tileables(self, tileables, fetch, n_parallel, n_thread, print_progress, mock, compose)
828                 # update shape of tileable and its chunks whatever it's successful or not
829                 self._update_tileable_and_chunk_shape(
--> 830                     tileable_graph, chunk_result, chunk_graph_builder.interrupted_ops)
831                 if chunk_graph_builder.done:
832                     if len(intermediate_result_keys) > 0:

~/Workspace/mars/mars/executor.py in _update_tileable_and_chunk_shape(self, tileable_graph, chunk_result, failed_ops)
726                     continue
727                 for c in tiled_n.chunks:
--> 728                     c.data._shape = chunk_result[c.key].shape
729                 new_nsplits = self.get_tileable_nsplits(n, chunk_result=chunk_result)
730                 for node in (n, tiled_n):

~/Workspace/mars/mars/lib/groupby_wrapper.py in __getattr__(self, item)
74         if item in getattr(self.obj, 'columns', ()):
75             return self.__getitem__(item)
---> 76         return getattr(self.groupby_obj, item)
77
78     def __iter__(self):

~/miniconda3/lib/python3.7/site-packages/pandas/core/groupby/groupby.py in __getattr__(self, attr)
578
579         raise AttributeError(
--> 580             f"'{type(self).__name__}' object has no attribute '{attr}'"
581         )
582

AttributeError: 'DataFrameGroupBy' object has no attribute 'shape'

# Fixed code
def __init__(
    self,
    obj,
    groupby_obj=None,
    keys=None,
    axis=0,
    level=None,
    grouper=None,
    exclusions=None,
    selection=None,
    as_index=True,
    sort=True,
    group_keys=True,
    squeeze=False,
    observed=False,
    mutated=False,
    grouper_cache=None,
):
    def fill_value(v, key):
        return v if v is not None or groupby_obj is None else getattr(groupby_obj, key)

    self.obj = obj
    self.keys = fill_value(keys, "keys")
    self.axis = fill_value(axis, "axis")
    self.level = fill_value(level, "level")
    self.exclusions = fill_value(exclusions, "exclusions")
    self.selection = selection
    self.as_index = fill_value(as_index, "as_index")
    self.sort = fill_value(sort, "sort")
    self.group_keys = fill_value(group_keys, "group_keys")
    self.squeeze = fill_value(squeeze, "squeeze")
    self.observed = fill_value(observed, "observed")
    self.mutated = fill_value(mutated, "mutated")

    if groupby_obj is None:
        if obj.ndim == 2:
            self.groupby_obj = DataFrameGroupBy(
                obj,
                keys=keys,
                axis=axis,
                level=level,
                grouper=grouper,
                exclusions=exclusions,
                as_index=as_index,
                group_keys=group_keys,
                squeeze=squeeze,
                observed=observed,
                mutated=mutated,
            )
        else:
            self.groupby_obj = SeriesGroupBy(
                obj,
                keys=keys,
                axis=axis,
                level=level,
                grouper=grouper,
                exclusions=exclusions,
                as_index=as_index,
                group_keys=group_keys,
                squeeze=squeeze,
                observed=observed,
                mutated=mutated,
            )
    else:
        self.groupby_obj = groupby_obj

    if grouper_cache:
        self.groupby_obj.grouper._cache = grouper_cache
    if selection:
        self.groupby_obj = self.groupby_obj[selection]

    self.is_frame = isinstance(self.groupby_obj, DataFrameGroupBy)