We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
to_netcdf fails with error writing PyMC3 trace containing pm.Data as observed
**Describe the bug**
If one creates a model that has an observed RV, and the observations for that RV are a `pm.Data` object, and one tries to invoke `to_netcdf()` on the resulting arviz `InferenceData` object, you get an error in `ensure_dtype_not_object()`.  Inspection of the backtrace shows that the `InferenceData` object has the *name of the `pm.Data()`* in the observation array, instead of the data that populates the `Data` object.
Note that trying to save a *prior predictive trace* alone does *not* trigger this bug. As far as I can tell, it must be a sample from the posterior.
**To Reproduce**
See attached jupyter notebook for a minimal case.


**Additional context**
`arviz` 0.4.0
`pymc3` from git
MacOS (but I think that's irrelevant)
[arviz-to_netcdf-bug.ipynb.txt](https://github.com/arviz-devs/arviz/files/3233360/arviz-to_netcdf-bug.ipynb.txt)



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `arviz/data/io_pymc3.py`
Content:
```
1 """PyMC3-specific conversion code."""
2 import numpy as np
3 import xarray as xr
4 
5 from .inference_data import InferenceData
6 from .base import requires, dict_to_dataset, generate_dims_coords, make_attrs
7 
8 
9 class PyMC3Converter:
10     """Encapsulate PyMC3 specific logic."""
11 
12     def __init__(
13         self, *, trace=None, prior=None, posterior_predictive=None, coords=None, dims=None
14     ):
15         self.trace = trace
16         self.prior = prior
17         self.posterior_predictive = posterior_predictive
18         self.coords = coords
19         self.dims = dims
20         import pymc3
21 
22         self.pymc3 = pymc3
23 
24     @requires("trace")
25     def _extract_log_likelihood(self):
26         """Compute log likelihood of each observation.
27 
28         Return None if there is not exactly 1 observed random variable.
29         """
30         # This next line is brittle and may not work forever, but is a secret
31         # way to access the model from the trace.
32         model = self.trace._straces[0].model  # pylint: disable=protected-access
33         if len(model.observed_RVs) != 1:
34             return None, None
35         else:
36             if self.dims is not None:
37                 coord_name = self.dims.get(model.observed_RVs[0].name)
38             else:
39                 coord_name = None
40 
41         cached = [(var, var.logp_elemwise) for var in model.observed_RVs]
42 
43         def log_likelihood_vals_point(point):
44             """Compute log likelihood for each observed point."""
45             log_like_vals = []
46             for var, log_like in cached:
47                 log_like_val = log_like(point)
48                 if var.missing_values:
49                     log_like_val = log_like_val[~var.observations.mask]
50                 log_like_vals.append(log_like_val)
51             return np.concatenate(log_like_vals)
52 
53         chain_likelihoods = []
54         for chain in self.trace.chains:
55             log_like = (log_likelihood_vals_point(point) for point in self.trace.points([chain]))
56             chain_likelihoods.append(np.stack(log_like))
57         return np.stack(chain_likelihoods), coord_name
58 
59     @requires("trace")
60     def posterior_to_xarray(self):
61         """Convert the posterior to an xarray dataset."""
62         var_names = self.pymc3.util.get_default_varnames(  # pylint: disable=no-member
63             self.trace.varnames, include_transformed=False
64         )
65         data = {}
66         for var_name in var_names:
67             data[var_name] = np.array(self.trace.get_values(var_name, combine=False, squeeze=False))
68         return dict_to_dataset(data, library=self.pymc3, coords=self.coords, dims=self.dims)
69 
70     @requires("trace")
71     def sample_stats_to_xarray(self):
72         """Extract sample_stats from PyMC3 trace."""
73         rename_key = {"model_logp": "lp"}
74         data = {}
75         for stat in self.trace.stat_names:
76             name = rename_key.get(stat, stat)
77             data[name] = np.array(self.trace.get_sampler_stats(stat, combine=False))
78         log_likelihood, dims = self._extract_log_likelihood()
79         if log_likelihood is not None:
80             data["log_likelihood"] = log_likelihood
81             dims = {"log_likelihood": dims}
82         else:
83             dims = None
84 
85         return dict_to_dataset(data, library=self.pymc3, dims=dims, coords=self.coords)
86 
87     @requires("posterior_predictive")
88     def posterior_predictive_to_xarray(self):
89         """Convert posterior_predictive samples to xarray."""
90         data = {k: np.expand_dims(v, 0) for k, v in self.posterior_predictive.items()}
91         return dict_to_dataset(data, library=self.pymc3, coords=self.coords, dims=self.dims)
92 
93     @requires("prior")
94     def prior_to_xarray(self):
95         """Convert prior samples to xarray."""
96         return dict_to_dataset(
97             {k: np.expand_dims(v, 0) for k, v in self.prior.items()},
98             library=self.pymc3,
99             coords=self.coords,
100             dims=self.dims,
101         )
102 
103     @requires("trace")
104     def observed_data_to_xarray(self):
105         """Convert observed data to xarray."""
106         # This next line is brittle and may not work forever, but is a secret
107         # way to access the model from the trace.
108         model = self.trace._straces[0].model  # pylint: disable=protected-access
109 
110         observations = {obs.name: obs.observations for obs in model.observed_RVs}
111         if self.dims is None:
112             dims = {}
113         else:
114             dims = self.dims
115         observed_data = {}
116         for name, vals in observations.items():
117             vals = np.atleast_1d(vals)
118             val_dims = dims.get(name)
119             val_dims, coords = generate_dims_coords(
120                 vals.shape, name, dims=val_dims, coords=self.coords
121             )
122             # filter coords based on the dims
123             coords = {key: xr.IndexVariable((key,), data=coords[key]) for key in val_dims}
124             observed_data[name] = xr.DataArray(vals, dims=val_dims, coords=coords)
125         return xr.Dataset(data_vars=observed_data, attrs=make_attrs(library=self.pymc3))
126 
127     def to_inference_data(self):
128         """Convert all available data to an InferenceData object.
129 
130         Note that if groups can not be created (i.e., there is no `trace`, so
131         the `posterior` and `sample_stats` can not be extracted), then the InferenceData
132         will not have those groups.
133         """
134         return InferenceData(
135             **{
136                 "posterior": self.posterior_to_xarray(),
137                 "sample_stats": self.sample_stats_to_xarray(),
138                 "posterior_predictive": self.posterior_predictive_to_xarray(),
139                 "prior": self.prior_to_xarray(),
140                 "observed_data": self.observed_data_to_xarray(),
141             }
142         )
143 
144 
145 def from_pymc3(trace=None, *, prior=None, posterior_predictive=None, coords=None, dims=None):
146     """Convert pymc3 data into an InferenceData object."""
147     return PyMC3Converter(
148         trace=trace,
149         prior=prior,
150         posterior_predictive=posterior_predictive,
151         coords=coords,
152         dims=dims,
153     ).to_inference_data()
154
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/arviz/data/io_pymc3.py b/arviz/data/io_pymc3.py
--- a/arviz/data/io_pymc3.py
+++ b/arviz/data/io_pymc3.py
@@ -114,6 +114,8 @@
             dims = self.dims
         observed_data = {}
         for name, vals in observations.items():
+            if hasattr(vals, "get_value"):
+                vals = vals.get_value()
             vals = np.atleast_1d(vals)
             val_dims = dims.get(name)
             val_dims, coords = generate_dims_coords(

{"golden_diff": "diff --git a/arviz/data/io_pymc3.py b/arviz/data/io_pymc3.py\n--- a/arviz/data/io_pymc3.py\n+++ b/arviz/data/io_pymc3.py\n@@ -114,6 +114,8 @@\n             dims = self.dims\n         observed_data = {}\n         for name, vals in observations.items():\n+            if hasattr(vals, \"get_value\"):\n+                vals = vals.get_value()\n             vals = np.atleast_1d(vals)\n             val_dims = dims.get(name)\n             val_dims, coords = generate_dims_coords(\n", "issue": "to_netcdf fails with error writing PyMC3 trace containing pm.Data as observed\n**Describe the bug**\r\nIf one creates a model that has an observed RV, and the observations for that RV are a `pm.Data` object, and one tries to invoke `to_netcdf()` on the resulting arviz `InferenceData` object, you get an error in `ensure_dtype_not_object()`.  Inspection of the backtrace shows that the `InferenceData` object has the *name of the `pm.Data()`* in the observation array, instead of the data that populates the `Data` object.\r\nNote that trying to save a *prior predictive trace* alone does *not* trigger this bug. As far as I can tell, it must be a sample from the posterior.\r\n**To Reproduce**\r\nSee attached jupyter notebook for a minimal case.\r\n\r\n\r\n**Additional context**\r\n`arviz` 0.4.0\r\n`pymc3` from git\r\nMacOS (but I think that's irrelevant)\r\n[arviz-to_netcdf-bug.ipynb.txt](https://github.com/arviz-devs/arviz/files/3233360/arviz-to_netcdf-bug.ipynb.txt)\r\n\r\n\n", "before_files": [{"content": "\"\"\"PyMC3-specific conversion code.\"\"\"\nimport numpy as np\nimport xarray as xr\n\nfrom .inference_data import InferenceData\nfrom .base import requires, dict_to_dataset, generate_dims_coords, make_attrs\n\n\nclass PyMC3Converter:\n    \"\"\"Encapsulate PyMC3 specific logic.\"\"\"\n\n    def __init__(\n        self, *, trace=None, prior=None, posterior_predictive=None, coords=None, dims=None\n    ):\n        self.trace = trace\n        self.prior = prior\n        self.posterior_predictive = posterior_predictive\n        self.coords = coords\n        self.dims = dims\n        import pymc3\n\n        self.pymc3 = pymc3\n\n    @requires(\"trace\")\n    def _extract_log_likelihood(self):\n        \"\"\"Compute log likelihood of each observation.\n\n        Return None if there is not exactly 1 observed random variable.\n        \"\"\"\n        # This next line is brittle and may not work forever, but is a secret\n        # way to access the model from the trace.\n        model = self.trace._straces[0].model  # pylint: disable=protected-access\n        if len(model.observed_RVs) != 1:\n            return None, None\n        else:\n            if self.dims is not None:\n                coord_name = self.dims.get(model.observed_RVs[0].name)\n            else:\n                coord_name = None\n\n        cached = [(var, var.logp_elemwise) for var in model.observed_RVs]\n\n        def log_likelihood_vals_point(point):\n            \"\"\"Compute log likelihood for each observed point.\"\"\"\n            log_like_vals = []\n            for var, log_like in cached:\n                log_like_val = log_like(point)\n                if var.missing_values:\n                    log_like_val = log_like_val[~var.observations.mask]\n                log_like_vals.append(log_like_val)\n            return np.concatenate(log_like_vals)\n\n        chain_likelihoods = []\n        for chain in self.trace.chains:\n            log_like = (log_likelihood_vals_point(point) for point in self.trace.points([chain]))\n            chain_likelihoods.append(np.stack(log_like))\n        return np.stack(chain_likelihoods), coord_name\n\n    @requires(\"trace\")\n    def posterior_to_xarray(self):\n        \"\"\"Convert the posterior to an xarray dataset.\"\"\"\n        var_names = self.pymc3.util.get_default_varnames(  # pylint: disable=no-member\n            self.trace.varnames, include_transformed=False\n        )\n        data = {}\n        for var_name in var_names:\n            data[var_name] = np.array(self.trace.get_values(var_name, combine=False, squeeze=False))\n        return dict_to_dataset(data, library=self.pymc3, coords=self.coords, dims=self.dims)\n\n    @requires(\"trace\")\n    def sample_stats_to_xarray(self):\n        \"\"\"Extract sample_stats from PyMC3 trace.\"\"\"\n        rename_key = {\"model_logp\": \"lp\"}\n        data = {}\n        for stat in self.trace.stat_names:\n            name = rename_key.get(stat, stat)\n            data[name] = np.array(self.trace.get_sampler_stats(stat, combine=False))\n        log_likelihood, dims = self._extract_log_likelihood()\n        if log_likelihood is not None:\n            data[\"log_likelihood\"] = log_likelihood\n            dims = {\"log_likelihood\": dims}\n        else:\n            dims = None\n\n        return dict_to_dataset(data, library=self.pymc3, dims=dims, coords=self.coords)\n\n    @requires(\"posterior_predictive\")\n    def posterior_predictive_to_xarray(self):\n        \"\"\"Convert posterior_predictive samples to xarray.\"\"\"\n        data = {k: np.expand_dims(v, 0) for k, v in self.posterior_predictive.items()}\n        return dict_to_dataset(data, library=self.pymc3, coords=self.coords, dims=self.dims)\n\n    @requires(\"prior\")\n    def prior_to_xarray(self):\n        \"\"\"Convert prior samples to xarray.\"\"\"\n        return dict_to_dataset(\n            {k: np.expand_dims(v, 0) for k, v in self.prior.items()},\n            library=self.pymc3,\n            coords=self.coords,\n            dims=self.dims,\n        )\n\n    @requires(\"trace\")\n    def observed_data_to_xarray(self):\n        \"\"\"Convert observed data to xarray.\"\"\"\n        # This next line is brittle and may not work forever, but is a secret\n        # way to access the model from the trace.\n        model = self.trace._straces[0].model  # pylint: disable=protected-access\n\n        observations = {obs.name: obs.observations for obs in model.observed_RVs}\n        if self.dims is None:\n            dims = {}\n        else:\n            dims = self.dims\n        observed_data = {}\n        for name, vals in observations.items():\n            vals = np.atleast_1d(vals)\n            val_dims = dims.get(name)\n            val_dims, coords = generate_dims_coords(\n                vals.shape, name, dims=val_dims, coords=self.coords\n            )\n            # filter coords based on the dims\n            coords = {key: xr.IndexVariable((key,), data=coords[key]) for key in val_dims}\n            observed_data[name] = xr.DataArray(vals, dims=val_dims, coords=coords)\n        return xr.Dataset(data_vars=observed_data, attrs=make_attrs(library=self.pymc3))\n\n    def to_inference_data(self):\n        \"\"\"Convert all available data to an InferenceData object.\n\n        Note that if groups can not be created (i.e., there is no `trace`, so\n        the `posterior` and `sample_stats` can not be extracted), then the InferenceData\n        will not have those groups.\n        \"\"\"\n        return InferenceData(\n            **{\n                \"posterior\": self.posterior_to_xarray(),\n                \"sample_stats\": self.sample_stats_to_xarray(),\n                \"posterior_predictive\": self.posterior_predictive_to_xarray(),\n                \"prior\": self.prior_to_xarray(),\n                \"observed_data\": self.observed_data_to_xarray(),\n            }\n        )\n\n\ndef from_pymc3(trace=None, *, prior=None, posterior_predictive=None, coords=None, dims=None):\n    \"\"\"Convert pymc3 data into an InferenceData object.\"\"\"\n    return PyMC3Converter(\n        trace=trace,\n        prior=prior,\n        posterior_predictive=posterior_predictive,\n        coords=coords,\n        dims=dims,\n    ).to_inference_data()\n", "path": "arviz/data/io_pymc3.py"}], "after_files": [{"content": "\"\"\"PyMC3-specific conversion code.\"\"\"\nimport numpy as np\nimport xarray as xr\n\nfrom .inference_data import InferenceData\nfrom .base import requires, dict_to_dataset, generate_dims_coords, make_attrs\n\n\nclass PyMC3Converter:\n    \"\"\"Encapsulate PyMC3 specific logic.\"\"\"\n\n    def __init__(\n        self, *, trace=None, prior=None, posterior_predictive=None, coords=None, dims=None\n    ):\n        self.trace = trace\n        self.prior = prior\n        self.posterior_predictive = posterior_predictive\n        self.coords = coords\n        self.dims = dims\n        import pymc3\n\n        self.pymc3 = pymc3\n\n    @requires(\"trace\")\n    def _extract_log_likelihood(self):\n        \"\"\"Compute log likelihood of each observation.\n\n        Return None if there is not exactly 1 observed random variable.\n        \"\"\"\n        # This next line is brittle and may not work forever, but is a secret\n        # way to access the model from the trace.\n        model = self.trace._straces[0].model  # pylint: disable=protected-access\n        if len(model.observed_RVs) != 1:\n            return None, None\n        else:\n            if self.dims is not None:\n                coord_name = self.dims.get(model.observed_RVs[0].name)\n            else:\n                coord_name = None\n\n        cached = [(var, var.logp_elemwise) for var in model.observed_RVs]\n\n        def log_likelihood_vals_point(point):\n            \"\"\"Compute log likelihood for each observed point.\"\"\"\n            log_like_vals = []\n            for var, log_like in cached:\n                log_like_val = log_like(point)\n                if var.missing_values:\n                    log_like_val = log_like_val[~var.observations.mask]\n                log_like_vals.append(log_like_val)\n            return np.concatenate(log_like_vals)\n\n        chain_likelihoods = []\n        for chain in self.trace.chains:\n            log_like = (log_likelihood_vals_point(point) for point in self.trace.points([chain]))\n            chain_likelihoods.append(np.stack(log_like))\n        return np.stack(chain_likelihoods), coord_name\n\n    @requires(\"trace\")\n    def posterior_to_xarray(self):\n        \"\"\"Convert the posterior to an xarray dataset.\"\"\"\n        var_names = self.pymc3.util.get_default_varnames(  # pylint: disable=no-member\n            self.trace.varnames, include_transformed=False\n        )\n        data = {}\n        for var_name in var_names:\n            data[var_name] = np.array(self.trace.get_values(var_name, combine=False, squeeze=False))\n        return dict_to_dataset(data, library=self.pymc3, coords=self.coords, dims=self.dims)\n\n    @requires(\"trace\")\n    def sample_stats_to_xarray(self):\n        \"\"\"Extract sample_stats from PyMC3 trace.\"\"\"\n        rename_key = {\"model_logp\": \"lp\"}\n        data = {}\n        for stat in self.trace.stat_names:\n            name = rename_key.get(stat, stat)\n            data[name] = np.array(self.trace.get_sampler_stats(stat, combine=False))\n        log_likelihood, dims = self._extract_log_likelihood()\n        if log_likelihood is not None:\n            data[\"log_likelihood\"] = log_likelihood\n            dims = {\"log_likelihood\": dims}\n        else:\n            dims = None\n\n        return dict_to_dataset(data, library=self.pymc3, dims=dims, coords=self.coords)\n\n    @requires(\"posterior_predictive\")\n    def posterior_predictive_to_xarray(self):\n        \"\"\"Convert posterior_predictive samples to xarray.\"\"\"\n        data = {k: np.expand_dims(v, 0) for k, v in self.posterior_predictive.items()}\n        return dict_to_dataset(data, library=self.pymc3, coords=self.coords, dims=self.dims)\n\n    @requires(\"prior\")\n    def prior_to_xarray(self):\n        \"\"\"Convert prior samples to xarray.\"\"\"\n        return dict_to_dataset(\n            {k: np.expand_dims(v, 0) for k, v in self.prior.items()},\n            library=self.pymc3,\n            coords=self.coords,\n            dims=self.dims,\n        )\n\n    @requires(\"trace\")\n    def observed_data_to_xarray(self):\n        \"\"\"Convert observed data to xarray.\"\"\"\n        # This next line is brittle and may not work forever, but is a secret\n        # way to access the model from the trace.\n        model = self.trace._straces[0].model  # pylint: disable=protected-access\n\n        observations = {obs.name: obs.observations for obs in model.observed_RVs}\n        if self.dims is None:\n            dims = {}\n        else:\n            dims = self.dims\n        observed_data = {}\n        for name, vals in observations.items():\n            if hasattr(vals, \"get_value\"):\n                vals = vals.get_value()\n            vals = np.atleast_1d(vals)\n            val_dims = dims.get(name)\n            val_dims, coords = generate_dims_coords(\n                vals.shape, name, dims=val_dims, coords=self.coords\n            )\n            # filter coords based on the dims\n            coords = {key: xr.IndexVariable((key,), data=coords[key]) for key in val_dims}\n            observed_data[name] = xr.DataArray(vals, dims=val_dims, coords=coords)\n        return xr.Dataset(data_vars=observed_data, attrs=make_attrs(library=self.pymc3))\n\n    def to_inference_data(self):\n        \"\"\"Convert all available data to an InferenceData object.\n\n        Note that if groups can not be created (i.e., there is no `trace`, so\n        the `posterior` and `sample_stats` can not be extracted), then the InferenceData\n        will not have those groups.\n        \"\"\"\n        return InferenceData(\n            **{\n                \"posterior\": self.posterior_to_xarray(),\n                \"sample_stats\": self.sample_stats_to_xarray(),\n                \"posterior_predictive\": self.posterior_predictive_to_xarray(),\n                \"prior\": self.prior_to_xarray(),\n                \"observed_data\": self.observed_data_to_xarray(),\n            }\n        )\n\n\ndef from_pymc3(trace=None, *, prior=None, posterior_predictive=None, coords=None, dims=None):\n    \"\"\"Convert pymc3 data into an InferenceData object.\"\"\"\n    return PyMC3Converter(\n        trace=trace,\n        prior=prior,\n        posterior_predictive=posterior_predictive,\n        coords=coords,\n        dims=dims,\n    ).to_inference_data()\n", "path": "arviz/data/io_pymc3.py"}]}