Check that a particular argument is an array with a given shape. The
    shape may contain string attributes, which will be fetched from arg0 to
    the function (usually self).

def has_shape(shape):
    """Check that a particular argument is an array with a given shape. The
    shape may contain string attributes, which will be fetched from arg0 to
    the function (usually self).
    """

    def has_shape_inner(arg_id, args, kwargs):
        self = args[0]
        arg = args[arg_id]
        if not hasattr(arg, "shape"):
            raise ExpectedTypeError(arg, ["array"])
        shape_values = []
        for dim in shape:
            if not isinstance(dim, integer_types):
                dim = getattr(self, dim, None)
            shape_values.append(dim)
        if len(shape) != len(arg.shape):
            raise ShapeMismatchError(arg.shape, tuple(shape_values), shape)
        for i, dim in enumerate(shape_values):
            # Allow underspecified dimensions
            if dim is not None and arg.shape[i] != dim:
                raise ShapeMismatchError(arg.shape, shape_values, shape)

    return has_shape_inner