Repository: gammapy/gammapy

Issue:
Parameters.to_table() does not handle priors correctly
**Gammapy version**
dev
**Bug description**
Creating a table from parameters without priors will produce a `"prior"` column containing `None` with `object` type. This is problematic as e.g. this `Table` cannot be written to disk.

**Expected behavior**
When a prior is set. The expected type is `str`, the content of the table when no prior is set should be `""`.

**To Reproduce**
```
from gammapy.modeling.models import SkyModel
model = SkyModel.create(spectral_model="pl")
model.parameters.to_table().write("test.fits")
```



Discussion:

Resolving patch:
diff --git a/gammapy/modeling/parameter.py b/gammapy/modeling/parameter.py
--- a/gammapy/modeling/parameter.py
+++ b/gammapy/modeling/parameter.py
@@ -443,6 +443,8 @@ def update_from_dict(self, data):
         """Update parameters from a dictionary."""
         keys = ["value", "unit", "min", "max", "frozen", "prior"]
         for k in keys:
+            if k == "prior" and data[k] == "":
+                data[k] = None
             setattr(self, k, data[k])
 
     def to_dict(self):
@@ -662,22 +664,32 @@ def to_dict(self):
 
         return data
 
+    @staticmethod
+    def _create_default_table():
+        name_to_type = {
+            "type": "str",
+            "name": "str",
+            "value": "float",
+            "unit": "str",
+            "error": "float",
+            "min": "float",
+            "max": "float",
+            "frozen": "bool",
+            "is_norm": "bool",
+            "link": "str",
+            "prior": "str",
+        }
+        return Table(names=name_to_type.keys(), dtype=name_to_type.values())
+
     def to_table(self):
         """Convert parameter attributes to `~astropy.table.Table`."""
-        rows = []
+        table = self._create_default_table()
+
         for p in self._parameters:
-            d = p.to_dict()
-            if "link" not in d:
-                d["link"] = ""
-            for key in ["scale_method", "interp"]:
-                if key in d:
-                    del d[key]
+            d = {k: v for k, v in p.to_dict().items() if k in table.colnames}
             if "prior" in d:
                 d["prior"] = d["prior"]["type"]
-            else:
-                d["prior"] = None
-            rows.append({**dict(type=p.type), **d})
-        table = Table(rows)
+            table.add_row(d)
 
         table["value"].format = ".4e"
         for name in ["error", "min", "max"]:


Test patch:
diff --git a/gammapy/modeling/tests/test_parameter.py b/gammapy/modeling/tests/test_parameter.py
--- a/gammapy/modeling/tests/test_parameter.py
+++ b/gammapy/modeling/tests/test_parameter.py
@@ -2,7 +2,9 @@
 import pytest
 import numpy as np
 from numpy.testing import assert_allclose
+from astropy.table import Table
 from gammapy.modeling import Parameter, Parameters, PriorParameter, PriorParameters
+from gammapy.modeling.models import GaussianPrior
 
 
 def test_parameter_init():
@@ -101,7 +103,12 @@ def test_parameter_autoscale(method, value, factor, scale):
 
 @pytest.fixture()
 def pars():
-    return Parameters([Parameter("spam", 42, "deg"), Parameter("ham", 99, "TeV")])
+    return Parameters(
+        [
+            Parameter("spam", 42, "deg"),
+            Parameter("ham", 99, "TeV", prior=GaussianPrior()),
+        ]
+    )
 
 
 def test_parameters_basics(pars):
@@ -155,7 +162,7 @@ def test_parameters_getitem(pars):
         pars[Parameter("bam!", 99)]
 
 
-def test_parameters_to_table(pars):
+def test_parameters_to_table(pars, tmp_path):
     pars["ham"].error = 1e-10
     pars["spam"]._link_label_io = "test"
 
@@ -165,6 +172,49 @@ def test_parameters_to_table(pars):
     assert table["link"][0] == "test"
     assert table["link"][1] == ""
 
+    assert table["prior"][0] == ""
+    assert table["prior"][1] == "GaussianPrior"
+    assert table["type"][1] == ""
+
+    table.write(tmp_path / "test_parameters.fits")
+    Table.read(tmp_path / "test_parameters.fits")
+
+
+def test_parameters_create_table():
+    table = Parameters._create_default_table()
+
+    assert len(table) == 0
+    assert len(table.columns) == 11
+
+    assert table.colnames == [
+        "type",
+        "name",
+        "value",
+        "unit",
+        "error",
+        "min",
+        "max",
+        "frozen",
+        "is_norm",
+        "link",
+        "prior",
+    ]
+    assert table.dtype == np.dtype(
+        [
+            ("type", "<U1"),
+            ("name", "<U1"),
+            ("value", "<f8"),
+            ("unit", "<U1"),
+            ("error", "<f8"),
+            ("min", "<f8"),
+            ("max", "<f8"),
+            ("frozen", "?"),
+            ("is_norm", "?"),
+            ("link", "<U1"),
+            ("prior", "<U1"),
+        ]
+    )
+
 
 def test_parameters_set_parameter_factors(pars):
     pars.set_parameter_factors([77, 78])