Repository: intake/intake

Issue:
.save() functionality in Catalog for custom local catalog class
Found a number of errors in trying to save a catalog that was defined in a custom class. Source here: https://github.com/manfralab/intake-qcodes

Here's the solution I ended up with, including some comments about what errors needed to be fixed. Sorry, this isn't a super clean example...

```
    def serialize(self):
        """
        Produce YAML version of this catalog.
        Note that this is not the same as ``.yaml()``, which produces a YAML
        block referring to this catalog.
        """
        import yaml
        output = {"metadata": self.metadata, "sources": {},
                  "name": self.name}
        for key, entry in self.items():
            # hack to fix serializing the name of the custom catalog
            kw = entry._captured_init_kwargs.copy()
            kw.pop('catalog')

            # 'parameters' should be a dict, is currently being written as a list
            # this hack only works because there are no parameters defined in my catalog
            if isinstance(kw['parameters'], list):
                if not kw['parameters']:
                    kw['parameters'] = {}
                else:
                    raise ValueError('not sure how to serialize this correctly')

            if not kw['cache']:
                kw['cache'] = [] # default value of None raises an error when read back

            output["sources"][key] = kw

        return yaml.dump(output)
```

Discussion:

Patch:
diff --git a/intake/catalog/base.py b/intake/catalog/base.py
index 34c2e7c..9fd7087 100644
--- a/intake/catalog/base.py
+++ b/intake/catalog/base.py
@@ -273,7 +273,10 @@ class Catalog(DataSource):
         output = {"metadata": self.metadata, "sources": {},
                   "name": self.name}
         for key, entry in self.items():
-            output["sources"][key] = entry._captured_init_kwargs
+            kw = entry._captured_init_kwargs.copy()
+            kw.pop('catalog', None)
+            kw['parameters'] = {k.name: k.__getstate__()['kwargs'] for k in kw.get('parameters', [])}
+            output["sources"][key] = kw
         return yaml.dump(output)
 
     def save(self, url, storage_options=None):


Test patch:
diff --git a/intake/catalog/tests/test_local.py b/intake/catalog/tests/test_local.py
index b5dc04e..7673307 100644
--- a/intake/catalog/tests/test_local.py
+++ b/intake/catalog/tests/test_local.py
@@ -617,7 +617,25 @@ def test_dict_save():
     cat2 = open_catalog(fn)
     assert 'trial' in cat2
     assert cat2.name == 'mycat'
-    assert cat2.trial._driver =='csv'
+    assert cat2.trial._driver == 'csv'
+
+
+def test_dict_save_complex():
+    from intake.catalog.base import Catalog
+    fn = os.path.join(tempfile.mkdtemp(), 'mycat.yaml')
+    cat = Catalog()
+    entry = LocalCatalogEntry(name='trial', description='get this back',
+                              driver='csv', cache=[], catalog=cat,
+                              parameters=[UserParameter(name='par1', description='desc', type='int')],
+                              args={'urlpath': 'none'})
+
+    cat._entries = {'trial': entry}
+    cat.save(fn)
+
+    cat2 = open_catalog(fn)
+    assert 'trial' in cat2
+    assert cat2.name == 'mycat'
+    assert cat2.trial.describe()['plugin'][0] == 'csv'
 
 
 def test_dict_adddel():