Repository: pydata/xarray

Issue:
Can't create weakrefs on DataArrays since xarray 0.13.0
#### MCVE Code Sample
<!-- In order for the maintainers to efficiently understand and prioritize issues, we ask you post a "Minimal, Complete and Verifiable Example" (MCVE): http://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports -->

```python
import xarray as xr
from weakref import ref
arr = xr.DataArray([1, 2, 3])
ref(arr)
```

#### Expected Output
I expect the weak reference to be created as in former versions

#### Problem Description
The above code raises the following exception:
`TypeError: cannot create weak reference to 'DataArray' object`


#### Output of ``xr.show_versions()``
<details>

INSTALLED VERSIONS
------------------
commit: None
python: 3.7.3 | packaged by conda-forge | (default, Jul  1 2019, 21:52:21) 
[GCC 7.3.0]
python-bits: 64
OS: Linux
OS-release: 3.10.0-1062.1.1.el7.x86_64
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None
LANG: en_GB.UTF-8
LOCALE: en_GB.UTF-8
libhdf5: 1.10.4
libnetcdf: 4.6.2

xarray: 0.13.0
pandas: 0.25.1
numpy: 1.17.0
scipy: 1.3.0
netCDF4: 1.5.1.2
pydap: None
h5netcdf: 0.7.4
h5py: 2.9.0
Nio: None
zarr: None
cftime: 1.0.3.4
nc_time_axis: None
PseudoNetCDF: None
rasterio: 1.0.22
cfgrib: None
iris: None
bottleneck: None
dask: 2.3.0
distributed: 2.4.0
matplotlib: 3.1.1
cartopy: 0.17.0
seaborn: None
numbagg: None
setuptools: 41.2.0
pip: 19.2.3
conda: None
pytest: 5.0.1
IPython: 7.8.0
sphinx: None


</details>


Discussion:
crusaderky: Ready for review and merge

Patch:
diff --git a/doc/whats-new.rst b/doc/whats-new.rst
index 42da3fa8..eeb76822 100644
--- a/doc/whats-new.rst
+++ b/doc/whats-new.rst
@@ -18,6 +18,14 @@ What's New
 v0.13.1 (unreleased)
 --------------------
 
+Bug fixes
+~~~~~~~~~
+- Reintroduce support for :mod:`weakref` (broken in v0.13.0). Support has been
+  reinstated for :class:`DataArray` and :class:`Dataset` objects only. Internal xarray
+  objects remain unaddressable by weakref in order to save memory.
+  (:issue:`3317`) by `Guido Imperiale <https://github.com/crusaderky>`_.
+
+
 .. _whats-new.0.13.0:
 
 v0.13.0 (17 Sep 2019)
diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py
index d9e98839..13a3a507 100644
--- a/xarray/core/dataarray.py
+++ b/xarray/core/dataarray.py
@@ -248,7 +248,15 @@ class DataArray(AbstractArray, DataWithCoords):
         Dictionary for holding arbitrary metadata.
     """
 
-    __slots__ = ("_accessors", "_coords", "_file_obj", "_name", "_indexes", "_variable")
+    __slots__ = (
+        "_accessors",
+        "_coords",
+        "_file_obj",
+        "_name",
+        "_indexes",
+        "_variable",
+        "__weakref__",
+    )
 
     _groupby_cls = groupby.DataArrayGroupBy
     _rolling_cls = rolling.DataArrayRolling
diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py
index 693e94e2..3ba1bd9e 100644
--- a/xarray/core/dataset.py
+++ b/xarray/core/dataset.py
@@ -420,6 +420,7 @@ class Dataset(Mapping, ImplementsDatasetReduce, DataWithCoords):
         "_file_obj",
         "_indexes",
         "_variables",
+        "__weakref__",
     )
 
     _groupby_cls = groupby.DatasetGroupBy


Test patch:
diff --git a/xarray/tests/test_dataarray.py b/xarray/tests/test_dataarray.py
index 01e92bdd..9ba3eecc 100644
--- a/xarray/tests/test_dataarray.py
+++ b/xarray/tests/test_dataarray.py
@@ -4672,3 +4672,14 @@ def test_subclass_slots():
             pass
 
     assert str(e.value) == "MyArray must explicitly define __slots__"
+
+
+def test_weakref():
+    """Classes with __slots__ are incompatible with the weakref module unless they
+    explicitly state __weakref__ among their slots
+    """
+    from weakref import ref
+
+    a = DataArray(1)
+    r = ref(a)
+    assert r() is a
diff --git a/xarray/tests/test_dataset.py b/xarray/tests/test_dataset.py
index 7d2b11d0..f02990a1 100644
--- a/xarray/tests/test_dataset.py
+++ b/xarray/tests/test_dataset.py
@@ -5786,3 +5786,14 @@ def test_subclass_slots():
             pass
 
     assert str(e.value) == "MyDS must explicitly define __slots__"
+
+
+def test_weakref():
+    """Classes with __slots__ are incompatible with the weakref module unless they
+    explicitly state __weakref__ among their slots
+    """
+    from weakref import ref
+
+    ds = Dataset()
+    r = ref(ds)
+    assert r() is ds