# Buggy code
def _calculate_binary_op(
    f, dataset, other, dest_vars, inplace=False, drop_missing_vars=True
):
    dataset_variables = getattr(dataset, "variables", dataset)
    dataset_data_vars = getattr(dataset, "data_vars", dataset)
    if utils.is_dict_like(other):
        other_variables = getattr(other, "variables", other)
        other_data_vars = getattr(other, "data_vars", other)
        performed_op = False
        for k in dataset_data_vars:
            if k in other_data_vars:
                dest_vars[k] = f(dataset_variables[k], other_variables[k])
                performed_op = True
            elif inplace and k in dest_vars:
                raise ValueError(
                    "datasets must have the same data variables "
                    "for in-place arithmetic operations: %s, %s"
                    % (list(dataset_data_vars), list(other_data_vars))
                )
            elif not drop_missing_vars:
                # this shortcuts left alignment of variables for fillna
                dest_vars[k] = dataset_variables[k]
        if not performed_op:
            raise ValueError(
                "datasets have no overlapping data variables: "
                "%s, %s" % (list(dataset_data_vars), list(other_data_vars))
            )
    else:
        other_variable = getattr(other, "variable", other)
        for k in dataset_data_vars:
            dest_vars[k] = f(dataset_variables[k], other_variable)


# Traceback (AttributeError)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
/Users/shoyer/miniconda/envs/rapid/lib/python2.7/site-packages/IPython/core/formatters.pyc in __call__(self, obj)
693                 type_pprinters=self.type_printers,
694                 deferred_pprinters=self.deferred_printers)
--> 695             printer.pretty(obj)
696             printer.flush()
697             return stream.getvalue()

/Users/shoyer/miniconda/envs/rapid/lib/python2.7/site-packages/IPython/lib/pretty.pyc in pretty(self, obj)
399                             if callable(meth):
400                                 return meth(obj, self, cycle)
--> 401             return _default_pprint(obj, self, cycle)
402         finally:
403             self.end_group()

/Users/shoyer/miniconda/envs/rapid/lib/python2.7/site-packages/IPython/lib/pretty.pyc in _default_pprint(obj, p, cycle)
519     if _safe_getattr(klass, '__repr__', None) not in _baseclass_reprs:
520         # A user-provided repr. Find newlines and replace them with p.break_()
--> 521         _repr_pprint(obj, p, cycle)
522         return
523     p.begin_group(1, '<')

/Users/shoyer/miniconda/envs/rapid/lib/python2.7/site-packages/IPython/lib/pretty.pyc in _repr_pprint(obj, p, cycle)
701     """A pprint that just redirects to the normal repr function."""
702     # Find newlines and replace them with p.break_()
--> 703     output = repr(obj)
704     for idx,output_line in enumerate(output.splitlines()):
705         if idx:

/Users/shoyer/dev/xray/xray/core/dataset.pyc in __repr__(self)
1043
1044     def __repr__(self):
-> 1045         return formatting.dataset_repr(self)
1046
1047     def isel(self, **indexers):

/Users/shoyer/dev/xray/xray/core/formatting.pyc in dataset_repr(ds)
231
232     summary.append(coords_repr(ds.coords, col_width=col_width))
--> 233     summary.append(vars_repr(ds.data_vars, col_width=col_width))
234     if ds.attrs:
235         summary.append(attrs_repr(ds.attrs))

/Users/shoyer/dev/xray/xray/core/formatting.pyc in _mapping_repr(mapping, title, summarizer, col_width)
170     summary = ['%s:' % title]
171     if mapping:
--> 172         summary += [summarizer(k, v, col_width) for k, v in mapping.items()]
173     else:
174         summary += [EMPTY_REPR]

/Users/shoyer/miniconda/envs/rapid/lib/python2.7/_abcoll.pyc in items(self)
412     def items(self):
413         "D.items() -> list of D's (key, value) pairs, as 2-tuples"
--> 414         return [(key, self[key]) for key in self]
415
416     def values(self):

/Users/shoyer/dev/xray/xray/core/dataset.pyc in __getitem__(self, key)
371     def __getitem__(self, key):
372         if key not in self._dataset._coord_names:
--> 373             return self._dataset[key]
374         else:
375             raise KeyError(key)

/Users/shoyer/dev/xray/xray/core/dataset.pyc in __getitem__(self, key)
751         key = np.asarray(key)
752         if key.ndim == 0:
--> 753             return DataArray._new_from_dataset(self, key.item())
754         else:
755             return self._copy_listed(key)

/Users/shoyer/dev/xray/xray/core/dataarray.pyc in _new_from_dataset(cls, dataset, name)
198         """
199         obj = object.__new__(cls)
--> 200         obj._dataset = dataset._copy_listed([name], keep_attrs=False)
201         if name not in obj._dataset:
202             # handle virtual variables

/Users/shoyer/dev/xray/xray/core/dataset.pyc in _copy_listed(self, names, keep_attrs)
680         needed_dims = set()
681         for v in variables.values():
--> 682             needed_dims.update(v._dims)
683         for k in self._coord_names:
684             if set(self._variables[k]._dims) <= needed_dims:

AttributeError: 'NotImplementedType' object has no attribute '_dims'

# Fixed code
def _calculate_binary_op(self, f, other, inplace=False, drop_na_vars=True):
    def apply_over_both(lhs_data_vars, rhs_data_vars, lhs_vars, rhs_vars):
        dest_vars = OrderedDict()
        performed_op = False
        for k in lhs_data_vars:
            if k in rhs_data_vars:
                dest_vars[k] = f(lhs_vars[k], rhs_vars[k])
                performed_op = True
            elif inplace:
                raise ValueError(
                    "datasets must have the same data variables "
                    "for in-place arithmetic operations: %s, %s"
                    % (list(lhs_data_vars), list(rhs_data_vars))
                )
            elif not drop_na_vars:
                # this shortcuts left alignment of variables for fillna
                dest_vars[k] = lhs_vars[k]
        if not performed_op:
            raise ValueError(
                "datasets have no overlapping data variables: %s, %s"
                % (list(lhs_data_vars), list(rhs_data_vars))
            )
        return dest_vars

    if utils.is_dict_like(other) and not isinstance(other, Dataset):
        # can't use our shortcut of doing the binary operation with
        # Variable objects, so apply over our data vars instead.
        new_data_vars = apply_over_both(self.data_vars, other, self.data_vars, other)
        return Dataset(new_data_vars)

    other_coords = getattr(other, "coords", None)
    ds = self.coords.merge(other_coords)

    if isinstance(other, Dataset):
        new_vars = apply_over_both(
            self.data_vars, other.data_vars, self.variables, other.variables
        )
    else:
        other_variable = getattr(other, "variable", other)
        new_vars = OrderedDict(
            (k, f(self.variables[k], other_variable)) for k in self.data_vars
        )

    ds._variables.update(new_vars)
    return ds