Repository: pydata/xarray

Issue:
Resample interpolate failing on tutorial dataset 
I'm getting some unexpected behavior/errors from the new resample/interpolate methods. 

@darothen - any idea what's going on here?

```Python-traceback
In [1]: import xarray as xr

In [2]: ds = xr.tutorial.load_dataset('air_temperature')

In [3]: ds.resample(time='15d').interpolate(kind='linear')
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-3-ef931d7ebbda> in <module>()
----> 1 ds.resample(time='15d').interpolate(kind='linear')

/glade/p/work/jhamman/storylines/src/xarray/xarray/core/resample.py in interpolate(self, kind)
    110
    111         """
--> 112         return self._interpolate(kind=kind)
    113
    114     def _interpolate(self, kind='linear'):

/glade/p/work/jhamman/storylines/src/xarray/xarray/core/resample.py in _interpolate(self, kind)
    312
    313         old_times = self._obj[self._dim].astype(float)
--> 314         new_times = self._full_index.values.astype(float)
    315
    316         data_vars = OrderedDict()

AttributeError: 'NoneType' object has no attribute 'values'
```

Discussion:

Patch:
diff --git a/doc/whats-new.rst b/doc/whats-new.rst
index 7025e2dd..9dea215b 100644
--- a/doc/whats-new.rst
+++ b/doc/whats-new.rst
@@ -54,9 +54,9 @@ Breaking changes
     [...]
 
   Note that both versions are currently supported, but using the old syntax will
-  produce a warning encouraging users to adopt the new syntax. 
+  produce a warning encouraging users to adopt the new syntax.
   By `Daniel Rothenberg <https://github.com/darothen>`_.
-  
+
 - ``repr`` and the Jupyter Notebook won't automatically compute dask variables.
   Datasets loaded with ``open_dataset`` won't automatically read coords from
   disk when calling ``repr`` (:issue:`1522`).
@@ -196,6 +196,7 @@ Bug fixes
 - ``:py:meth:`~xarray.Dataset.__init__` raises a ``MergeError`` if an
   coordinate shares a name with a dimension but is comprised of arbitrary
   dimensions(:issue:`1120`).
+
 - :py:func:`~xarray.open_rasterio` method now skips rasterio.crs -attribute if
   it is none.
   By `Leevi Annala <https://github.com/leevei>`_.
@@ -212,6 +213,14 @@ Bug fixes
   the first argument was a numpy variable (:issue:`1588`).
   By `Guido Imperiale <https://github.com/crusaderky>`_.
 
+- Fix ``netCDF4`` backend to properly roundtrip the ``shuffle`` encoding option
+  (:issue:`1606`).
+  By `Joe Hamman <https://github.com/jhamman>`_.
+
+- Fix bug when using ``pytest`` class decorators to skiping certain unittests.
+  The previous behavior unintentionally causing additional tests to be skipped
+  (:issue:`1531`). By `Joe Hamman <https://github.com/jhamman>`_.
+
 .. _whats-new.0.9.6:
 
 v0.9.6 (8 June 2017)
diff --git a/xarray/backends/netCDF4_.py b/xarray/backends/netCDF4_.py
index f42fa2b1..238e9ab4 100644
--- a/xarray/backends/netCDF4_.py
+++ b/xarray/backends/netCDF4_.py
@@ -153,7 +153,7 @@ def _extract_nc4_variable_encoding(variable, raise_on_invalid=False,
 
     safe_to_drop = set(['source', 'original_shape'])
     valid_encodings = set(['zlib', 'complevel', 'fletcher32', 'contiguous',
-                           'chunksizes'])
+                           'chunksizes', 'shuffle'])
     if lsd_okay:
         valid_encodings.add('least_significant_digit')
 
diff --git a/xarray/conventions.py b/xarray/conventions.py
index bbf2c023..d5394d5b 100644
--- a/xarray/conventions.py
+++ b/xarray/conventions.py
@@ -858,12 +858,14 @@ def decode_cf_variable(var, concat_characters=True, mask_and_scale=True,
             if ('_FillValue' in attributes and
                 not utils.equivalent(attributes['_FillValue'],
                                      attributes['missing_value'])):
-                raise ValueError("Discovered conflicting _FillValue "
-                                 "and missing_value.  Considering "
-                                 "opening the offending dataset using "
-                                 "decode_cf=False, corrected the attributes",
-                                 "and decoding explicitly using "
-                                 "xarray.conventions.decode_cf(ds)")
+                raise ValueError("Conflicting _FillValue and missing_value "
+                                 "attributes on a variable: {} vs. {}\n\n"
+                                 "Consider opening the offending dataset "
+                                 "using decode_cf=False, correcting the "
+                                 "attributes and decoding explicitly using "
+                                 "xarray.decode_cf()."
+                                 .format(attributes['_FillValue'],
+                                         attributes['missing_value']))
             attributes['_FillValue'] = attributes.pop('missing_value')
         fill_value = np.array(pop_to(attributes, encoding, '_FillValue'))
         if fill_value.size > 1:
diff --git a/xarray/core/groupby.py b/xarray/core/groupby.py
index b5fb02a8..320dcdd3 100644
--- a/xarray/core/groupby.py
+++ b/xarray/core/groupby.py
@@ -236,8 +236,8 @@ class GroupBy(object):
                 raise ValueError('index must be monotonic for resampling')
             s = pd.Series(np.arange(index.size), index)
             first_items = s.groupby(grouper).first()
+            full_index = first_items.index
             if first_items.isnull().any():
-                full_index = first_items.index
                 first_items = first_items.dropna()
             sbins = first_items.values.astype(np.int64)
             group_indices = ([slice(i, j)


Test patch:
diff --git a/xarray/tests/__init__.py b/xarray/tests/__init__.py
index d3f448f8..33bad7e5 100644
--- a/xarray/tests/__init__.py
+++ b/xarray/tests/__init__.py
@@ -5,6 +5,7 @@ import warnings
 from contextlib import contextmanager
 from distutils.version import LooseVersion
 import re
+import importlib
 
 import numpy as np
 from numpy.testing import assert_array_equal
@@ -25,111 +26,50 @@ try:
 except ImportError:
     import mock
 
-try:
-    import scipy
-    has_scipy = True
-except ImportError:
-    has_scipy = False
-
-try:
-    import pydap.client
-    has_pydap = True
-except ImportError:
-    has_pydap = False
-
-try:
-    import netCDF4
-    has_netCDF4 = True
-except ImportError:
-    has_netCDF4 = False
-
-
-try:
-    import h5netcdf
-    has_h5netcdf = True
-except ImportError:
-    has_h5netcdf = False
-
-
-try:
-    import Nio
-    has_pynio = True
-except ImportError:
-    has_pynio = False
-
-
-try:
-    import dask.array
-    import dask
-    dask.set_options(get=dask.get)
-    has_dask = True
-except ImportError:
-    has_dask = False
-
-
-try:
-    import matplotlib
-    has_matplotlib = True
-except ImportError:
-    has_matplotlib = False
-
 
-try:
-    import bottleneck
-    if LooseVersion(bottleneck.__version__) < LooseVersion('1.1'):
-        raise ImportError('Fall back to numpy')
-    has_bottleneck = True
-except ImportError:
-    has_bottleneck = False
-
-try:
-    import rasterio
-    has_rasterio = True
-except ImportError:
-    has_rasterio = False
-
-try:
-    import pathlib
-    has_pathlib = True
-except ImportError:
+def _importorskip(modname, minversion=None):
     try:
-        import pathlib2
-        has_pathlib = True
+        mod = importlib.import_module(modname)
+        has = True
+        if minversion is not None:
+            if LooseVersion(mod.__version__) < LooseVersion(minversion):
+                raise ImportError('Minimum version not satisfied')
     except ImportError:
-        has_pathlib = False
-
-
-# slighly simpler construction that the full functions.
-# Generally `pytest.importorskip('package')` inline is even easier
-requires_matplotlib = pytest.mark.skipif(
-    not has_matplotlib, reason='requires matplotlib')
-requires_scipy = pytest.mark.skipif(
-    not has_scipy, reason='requires scipy')
-requires_pydap = pytest.mark.skipif(
-    not has_pydap, reason='requires pydap')
-requires_netCDF4 = pytest.mark.skipif(
-    not has_netCDF4, reason='requires netCDF4')
-requires_h5netcdf = pytest.mark.skipif(
-    not has_h5netcdf, reason='requires h5netcdf')
-requires_pynio = pytest.mark.skipif(
-    not has_pynio, reason='requires pynio')
-requires_scipy_or_netCDF4 = pytest.mark.skipif(
-    not has_scipy and not has_netCDF4, reason='requires scipy or netCDF4')
-requires_dask = pytest.mark.skipif(
-    not has_dask, reason='requires dask')
-requires_bottleneck = pytest.mark.skipif(
-    not has_bottleneck, reason='requires bottleneck')
-requires_rasterio = pytest.mark.skipif(
-    not has_rasterio, reason='requires rasterio')
-requires_pathlib = pytest.mark.skipif(
-    not has_pathlib, reason='requires pathlib / pathlib2'
-)
-
+        has = False
+    # TODO: use pytest.skipif instead of unittest.skipUnless
+    # Using `unittest.skipUnless` is a temporary workaround for pytest#568,
+    # wherein class decorators stain inherited classes.
+    # xref: xarray#1531, implemented in xarray #1557.
+    func = unittest.skipUnless(has, reason='requires {}'.format(modname))
+    return has, func
+
+
+has_matplotlib, requires_matplotlib = _importorskip('matplotlib')
+has_scipy, requires_scipy = _importorskip('scipy')
+has_pydap, requires_pydap = _importorskip('pydap.client')
+has_netCDF4, requires_netCDF4 = _importorskip('netCDF4')
+has_h5netcdf, requires_h5netcdf = _importorskip('h5netcdf')
+has_pynio, requires_pynio = _importorskip('pynio')
+has_dask, requires_dask = _importorskip('dask')
+has_bottleneck, requires_bottleneck = _importorskip('bottleneck')
+has_rasterio, requires_rasterio = _importorskip('rasterio')
+has_pathlib, requires_pathlib = _importorskip('pathlib')
+
+# some special cases
+has_scipy_or_netCDF4 = has_scipy or has_netCDF4
+requires_scipy_or_netCDF4 = unittest.skipUnless(
+    has_scipy_or_netCDF4, reason='requires scipy or netCDF4')
+if not has_pathlib:
+    has_pathlib, requires_pathlib = _importorskip('pathlib2')
+
+if has_dask:
+    import dask
+    dask.set_options(get=dask.get)
 
 try:
     _SKIP_FLAKY = not pytest.config.getoption("--run-flaky")
     _SKIP_NETWORK_TESTS = not pytest.config.getoption("--run-network-tests")
-except ValueError:
+except (ValueError, AttributeError):
     # Can't get config from pytest, e.g., because xarray is installed instead
     # of being run from a development version (and hence conftests.py is not
     # available). Don't run flaky tests.
diff --git a/xarray/tests/test_backends.py b/xarray/tests/test_backends.py
index a977868c..c7bf5349 100644
--- a/xarray/tests/test_backends.py
+++ b/xarray/tests/test_backends.py
@@ -729,6 +729,7 @@ class BaseNetCDF4Test(CFEncodedDataTest):
         data['var2'].encoding.update({'zlib': True,
                                       'chunksizes': (5, 5),
                                       'fletcher32': True,
+                                      'shuffle': True,
                                       'original_shape': data.var2.shape})
         with self.roundtrip(data) as actual:
             for k, v in iteritems(data['var2'].encoding):
@@ -1179,7 +1180,7 @@ class H5NetCDFDataTest(BaseNetCDF4Test, TestCase):
 
 
 # tests pending h5netcdf fix
-@pytest.mark.xfail
+@unittest.skip
 class H5NetCDFDataTestAutocloseTrue(H5NetCDFDataTest):
     autoclose = True
 
@@ -1846,6 +1847,11 @@ class TestEncodingInvalid(TestCase):
         encoding = _extract_nc4_variable_encoding(var)
         self.assertEqual({}, encoding)
 
+        # regression test
+        var = xr.Variable(('x',), [1, 2, 3], {}, {'shuffle': True})
+        encoding = _extract_nc4_variable_encoding(var, raise_on_invalid=True)
+        self.assertEqual({'shuffle': True}, encoding)
+
     def test_extract_h5nc_encoding(self):
         # not supported with h5netcdf (yet)
         var = xr.Variable(('x',), [1, 2, 3], {},
diff --git a/xarray/tests/test_dataarray.py b/xarray/tests/test_dataarray.py
index e7920570..4b1a9d78 100644
--- a/xarray/tests/test_dataarray.py
+++ b/xarray/tests/test_dataarray.py
@@ -2077,6 +2077,15 @@ class TestDataArray(TestCase):
             # done here due to floating point arithmetic
             self.assertDataArrayAllClose(expected, actual, rtol=1e-16)
 
+    @requires_scipy
+    def test_upsample_interpolate_regression_1605(self):
+        dates = pd.date_range('2016-01-01', '2016-03-31', freq='1D')
+        expected = xr.DataArray(np.random.random((len(dates), 2, 3)),
+                                dims=('time', 'x', 'y'),
+                                coords={'time': dates})
+        actual = expected.resample(time='1D').interpolate('linear')
+        assert_allclose(actual, expected, rtol=1e-16)
+
     @requires_dask
     def test_upsample_interpolate_dask(self):
         import dask.array as da