Repository: rasterio/rasterio

Issue:
Replace 'src.set_*()' methods with getter/setter properties
Like `src.colorinterp() -> src.colorinterp` in https://github.com/mapbox/rasterio/pull/1134.  A quick search shows these:

```
$ git grep "def set_"
rasterio/_env.pyx:cpdef set_gdal_config(key, val, normalize=True):
rasterio/_io.pyx:    def set_crs(self, crs):
rasterio/_io.pyx:    def set_nodatavals(self, vals):
rasterio/_io.pyx:    def set_description(self, bidx, value):
rasterio/_io.pyx:    def set_units(self, bidx, value):
rasterio/_io.pyx:    def set_gcps(self, gcps, crs=None):
```

Looks like `src.gcps` might be a bit harder.


Discussion:
`set_crs` serves a dataset's `crs` property: https://github.com/mapbox/rasterio/blob/master/rasterio/_io.pyx#L1172-L1173

`set_nodatavals` does not yet serve a property.

`set_gcps` serves a dataset's `gcps` property.

`set_units` does not yet serve a property.

`set_description` does not yet serve a property.

Resolving patch:
diff --git a/rasterio/rio/edit_info.py b/rasterio/rio/edit_info.py
--- a/rasterio/rio/edit_info.py
+++ b/rasterio/rio/edit_info.py
@@ -53,7 +53,7 @@ def tags_handler(ctx, param, value):
     if retval is None and value:
         try:
             retval = dict(p.split('=') for p in value)
-        except:
+        except Exception:
             raise click.BadParameter(
                 "'%s' contains a malformed tag." % value,
                 param=param, param_hint='transform')
@@ -70,7 +70,7 @@ def transform_handler(ctx, param, value):
             pass
         try:
             retval = guard_transform(value)
-        except:
+        except Exception:
             raise click.BadParameter(
                 "'%s' is not recognized as an Affine array." % value,
                 param=param, param_hint='transform')
@@ -228,10 +228,10 @@ def in_dtype_range(value, dtype):
             dst.update_tags(**tags)
 
         if units:
-            dst.set_units(bidx, units)
+            dst.set_band_unit(bidx, units)
 
         if description:
-            dst.set_description(bidx, description)
+            dst.set_band_description(bidx, description)
 
         if colorinterp:
             if like and len(colorinterp) != dst.count:


Test patch:
diff --git a/tests/test_descriptions.py b/tests/test_descriptions.py
--- a/tests/test_descriptions.py
+++ b/tests/test_descriptions.py
@@ -1,15 +1,42 @@
+import pytest
+
 import rasterio
+from rasterio.errors import RasterioDeprecationWarning
 from rasterio.profiles import default_gtiff_profile
 
 
 def test_set_band_descriptions(tmpdir):
     """Descriptions can be set when dataset is open"""
     tmptiff = str(tmpdir.join('test.tif'))
+    with rasterio.open(
+            tmptiff, 'w', count=3, height=256, width=256,
+            **default_gtiff_profile) as dst:
+        assert dst.descriptions == (None, None, None)
+        dst.descriptions = ["this is a test band", "this is another test band", None]
+        assert dst.descriptions == (
+            "this is a test band", "this is another test band", None)
+
+
+@pytest.mark.parametrize('value', [[], ['x'], ['x', 'y', 'z']])
+def test_set_band_descriptions_error(tmpdir, value):
+    """Number of descriptions must match band count"""
+    tmptiff = str(tmpdir.join('test.tif'))
+    with rasterio.open(
+            tmptiff, 'w', count=2, height=256, width=256,
+            **default_gtiff_profile) as dst:
+        with pytest.raises(ValueError):
+            dst.descriptions = value
+
+
+def test_set_band_descriptions_deprecated(tmpdir):
+    """Warn about deprecation"""
+    tmptiff = str(tmpdir.join('test.tif'))
     with rasterio.open(
             tmptiff, 'w', count=2, height=256, width=256,
             **default_gtiff_profile) as dst:
         assert dst.descriptions == (None, None)
-        dst.set_description(1, "this is a test band")
-        dst.set_description(2, "this is another test band")
-        assert dst.descriptions == (
-            "this is a test band", "this is another test band")
+        with pytest.warns(RasterioDeprecationWarning):
+            dst.set_description(1, "this is a test band")
+            dst.set_description(2, "this is another test band")
+            assert dst.descriptions == (
+                "this is a test band", "this is another test band")
diff --git a/tests/test_units.py b/tests/test_units.py
--- a/tests/test_units.py
+++ b/tests/test_units.py
@@ -1,14 +1,40 @@
+import pytest
+
 import rasterio
+from rasterio.errors import RasterioDeprecationWarning
 from rasterio.profiles import default_gtiff_profile
 
 
 def test_set_units(tmpdir):
     """Units can be set when dataset is open"""
     tmptiff = str(tmpdir.join('test.tif'))
+    with rasterio.open(
+            tmptiff, 'w', count=3, height=256, width=256,
+            **default_gtiff_profile) as dst:
+        assert dst.units == (None, None, None)
+        dst.units = ['meters', 'degC', None]
+        assert dst.units == ('meters', 'degC', None)
+
+
+@pytest.mark.parametrize('value', [['m'], ['m', 'ft', 'sec'], []])
+def test_set_units_error(tmpdir, value):
+    """Number of values must match band count"""
+    tmptiff = str(tmpdir.join('test.tif'))
+    with rasterio.open(
+            tmptiff, 'w', count=2, height=256, width=256,
+            **default_gtiff_profile) as dst:
+        with pytest.raises(ValueError):
+            dst.units = value
+
+
+def test_set_units_deprecated(tmpdir):
+    """Deprecation is warned"""
+    tmptiff = str(tmpdir.join('test.tif'))
     with rasterio.open(
             tmptiff, 'w', count=2, height=256, width=256,
             **default_gtiff_profile) as dst:
-        assert dst.units == ('', '')
-        dst.set_units(1, 'meters')
-        dst.set_units(2, 'degC')
-        assert dst.units == ('meters', 'degC')
+        assert dst.units == (None, None)
+        with pytest.warns(RasterioDeprecationWarning):
+            dst.set_units(1, 'meters')
+            dst.set_units(2, 'degC')
+            assert dst.units == ('meters', 'degC')
diff --git a/tests/test_update.py b/tests/test_update.py
--- a/tests/test_update.py
+++ b/tests/test_update.py
@@ -58,7 +58,7 @@ def test_update_spatial_epsg(data):
         assert f.crs == {'init': 'epsg:4326'}
 
 
-def test_update_nodatavals(data):
+def test_update_nodata(data):
     tiffname = str(data.join('RGB.byte.tif'))
     with rasterio.open(tiffname, 'r+') as f:
         f.nodata = 255