Repository: pydata/xarray

Issue:
In-place operation on DataArray coordinate throws error but still modifies the coordinate values
Example:
```Py
import xarray as xr
import numpy as np
data = np.random.rand(4, 3)
locs = np.arange(3.)
times = np.arange(4.)
foo = xr.DataArray(data, coords=[times, locs], dims=["time", "space"])
foo
```
![Screenshot at 2021-03-15 13-47-22](https://user-images.githubusercontent.com/39047984/111155637-fe1e8880-8594-11eb-854a-efca5414e1f5.png)

When trying to do an in-place modification of the coordinate values
```Py
foo.coords['time'] += 10.
```
I get
```Py
ValueError: Cannot assign to the .values attribute of dimension coordinate a.k.a IndexVariable 'time'. Please use DataArray.assign_coords, Dataset.assign_coords or Dataset.assign as appropriate.
```
However, when looking at the values of the coordinate, they seem to have been modified by the operation:
![Screenshot at 2021-03-15 13-49-12](https://user-images.githubusercontent.com/39047984/111155819-3f169d00-8595-11eb-8481-1ca6bef020d8.png)

<details><summary>Output of <tt>xr.show_versions()</tt></summary>

<!-- Paste the output here xr.show_versions() here -->
INSTALLED VERSIONS
------------------
commit: None
python: 3.7.7 (default, May  7 2020, 21:25:33) 
[GCC 7.3.0]
python-bits: 64
OS: Linux
OS-release: 5.4.0-66-generic
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None
LANG: en_US.UTF-8
LOCALE: en_US.UTF-8
libhdf5: 1.10.6
libnetcdf: None

xarray: 0.16.2
pandas: 1.2.0
numpy: 1.19.4
scipy: 1.5.3
netCDF4: None
pydap: None
h5netcdf: None
h5py: 3.1.0
Nio: None
zarr: None
cftime: None
nc_time_axis: None
PseudoNetCDF: None
rasterio: None
cfgrib: None
iris: None
bottleneck: None
dask: None
distributed: None
matplotlib: 3.2.2
cartopy: None
seaborn: None
numbagg: None
pint: 0.16.1
setuptools: 49.6.0.post20201009
pip: 20.3.3
conda: 4.9.2
pytest: 6.1.2
IPython: 7.19.0
sphinx: 3.3.0

</details>


Discussion:

Patch:
diff --git a/CITATION.cff b/CITATION.cff
new file mode 100644
index 00000000..fcc04a6f
--- /dev/null
+++ b/CITATION.cff
@@ -0,0 +1,96 @@
+cff-version: 1.2.0
+message: "If you use this software, please cite it as below."
+authors:
+- family-names: "Hoyer"
+  given-names: "Stephan"
+  orcid: "https://orcid.org/0000-0002-5207-0380"
+- family-names: "Roos"
+  given-names: "Maximilian"
+- family-names: "Joseph"
+  given-names: "Hamman"
+  orcid: "https://orcid.org/0000-0001-7479-8439"
+- family-names: "Magin"
+  given-names: "Justus"
+- family-names: "Cherian"
+  given-names: "Deepak"
+  orcid: "https://orcid.org/0000-0002-6861-8734"
+- family-names: "Fitzgerald"
+  given-names: "Clark"
+  orcid: "https://orcid.org/0000-0003-3446-6389"
+- family-names: "Hauser"
+  given-names: "Mathias"
+  orcid: "https://orcid.org/0000-0002-0057-4878"
+- family-names: "Fujii"
+  given-names: "Keisuke"
+  orcid: "https://orcid.org/0000-0003-0390-9984"
+- family-names: "Maussion"
+  given-names: "Fabien"
+  orcid: "https://orcid.org/0000-0002-3211-506X"
+- family-names: "Imperiale"
+  given-names: "Guido"
+- family-names: "Clark"
+  given-names: "Spencer"
+  orcid: "https://orcid.org/0000-0001-5595-7895"
+- family-names: "Kleeman"
+  given-names: "Alex"
+- family-names: "Nicholas"
+  given-names: "Thomas"
+  orcid: "https://orcid.org/0000-0002-2176-0530"
+- family-names: "Kluyver"
+  given-names: "Thomas"
+  orcid: "https://orcid.org/0000-0003-4020-6364"
+- family-names: "Westling"
+  given-names: "Jimmy"
+- family-names: "Munroe"
+  given-names: "James"
+  orcid: "https://orcid.org/0000-0001-9098-6309"
+- family-names: "Amici"
+  given-names: "Alessandro"
+  orcid: "https://orcid.org/0000-0002-1778-4505"
+- family-names: "Barghini"
+  given-names: "Aureliana"
+- family-names: "Banihirwe"
+  given-names: "Anderson"
+  orcid: "https://orcid.org/0000-0001-6583-571X"
+- family-names: "Bell"
+  given-names: "Ray"
+  orcid: "https://orcid.org/0000-0003-2623-0587"
+- family-names: "Hatfield-Dodds"
+  given-names: "Zac"
+  orcid: "https://orcid.org/0000-0002-8646-8362"
+- family-names: "Abernathey"
+  given-names: "Ryan"
+  orcid: "https://orcid.org/0000-0001-5999-4917"
+- family-names: "Bovy"
+  given-names: "Benoît"
+- family-names: "Omotani"
+  given-names: "John"
+  orcid: "https://orcid.org/0000-0002-3156-8227"
+- family-names: "Mühlbauer"
+  given-names: "Kai"
+  orcid: "https://orcid.org/0000-0001-6599-1034"
+- family-names: "Roszko"
+  given-names: "Maximilian K."
+  orcid: "https://orcid.org/0000-0001-9424-2526"
+- family-names: "Wolfram"
+  given-names: "Phillip J."
+  orcid: "https://orcid.org/0000-0001-5971-4241"
+title: "xarray"
+doi: 10.5281/zenodo.598201
+url: "https://github.com/pydata/xarray"
+preferred-citation:
+  type: article
+  authors:
+  - family-names: "Hoyer"
+    given-names: "Stephan"
+    orcid: "https://orcid.org/0000-0002-5207-0380"
+  - family-names: "Joseph"
+    given-names: "Hamman"
+    orcid: "https://orcid.org/0000-0001-7479-8439"
+  doi: "10.5334/jors.148"
+  journal: "Journal of Open Research Software"
+  month: 4
+  title: "xarray: N-D labeled Arrays and Datasets in Python"
+  volume: 5
+  issue: 1
+  year: 2017
diff --git a/xarray/core/variable.py b/xarray/core/variable.py
index a96adb31..52125ec4 100644
--- a/xarray/core/variable.py
+++ b/xarray/core/variable.py
@@ -2813,6 +2813,11 @@ class IndexVariable(Variable):
     def name(self, value):
         raise AttributeError("cannot modify name of IndexVariable in-place")
 
+    def _inplace_binary_op(self, other, f):
+        raise TypeError(
+            "Values of an IndexVariable are immutable and can not be modified inplace"
+        )
+
 
 # for backwards compatibility
 Coordinate = utils.alias(IndexVariable, "Coordinate")


Test patch:
diff --git a/xarray/tests/test_dataarray.py b/xarray/tests/test_dataarray.py
index 53c65004..5e9c1b87 100644
--- a/xarray/tests/test_dataarray.py
+++ b/xarray/tests/test_dataarray.py
@@ -1987,6 +1987,18 @@ class TestDataArray:
         assert_array_equal(b.values, x)
         assert source_ndarray(b.values) is x
 
+    def test_inplace_math_error(self):
+        data = np.random.rand(4)
+        times = np.arange(4)
+        foo = DataArray(data, coords=[times], dims=["time"])
+        b = times.copy()
+        with pytest.raises(
+            TypeError, match=r"Values of an IndexVariable are immutable"
+        ):
+            foo.coords["time"] += 1
+        # Check error throwing prevented inplace operation
+        assert_array_equal(foo.coords["time"], b)
+
     def test_inplace_math_automatic_alignment(self):
         a = DataArray(range(5), [("x", range(5))])
         b = DataArray(range(1, 6), [("x", range(1, 6))])
diff --git a/xarray/tests/test_variable.py b/xarray/tests/test_variable.py
index 9c0e45c5..3267af8b 100644
--- a/xarray/tests/test_variable.py
+++ b/xarray/tests/test_variable.py
@@ -1656,6 +1656,14 @@ class TestVariable(VariableSubclassobjects):
         with pytest.raises(ValueError, match=r"dimensions cannot change"):
             v += Variable("y", np.arange(5))
 
+    def test_inplace_math_error(self):
+        x = np.arange(5)
+        v = IndexVariable(["x"], x)
+        with pytest.raises(
+            TypeError, match=r"Values of an IndexVariable are immutable"
+        ):
+            v += 1
+
     def test_reduce(self):
         v = Variable(["x", "y"], self.d, {"ignored": "attributes"})
         assert_identical(v.reduce(np.std, "x"), Variable(["y"], self.d.std(axis=0)))