Repository: esmvalgroup/esmvalcore
Issue: Automatic alias definition breaks concatenation of different experiments

The new alias definition option (#167) does not work when experiment concatenation is used, e.g. in [examples/recipe_concatenate_exps.yml](https://github.com/ESMValGroup/ESMValTool/blob/version2_development/esmvaltool/recipes/examples/recipe_concatenate_exps.yml):

```

Traceback (most recent call last):
  File "/mnt/lustre01/pf/b/b309057/ESMValTool/core/esmvalcore/_main.py", line 228, in run
    conf = main(args)
  File "/mnt/lustre01/pf/b/b309057/ESMValTool/core/esmvalcore/_main.py", line 156, in main
    process_recipe(recipe_file=recipe, config_user=cfg)
  File "/mnt/lustre01/pf/b/b309057/ESMValTool/core/esmvalcore/_main.py", line 202, in process_recipe
    recipe = read_recipe_file(recipe_file, config_user)
  File "/mnt/lustre01/pf/b/b309057/ESMValTool/core/esmvalcore/_recipe.py", line 70, in read_recipe_file
    raw_recipe, config_user, initialize_tasks, recipe_file=filename)
  File "/mnt/lustre01/pf/b/b309057/ESMValTool/core/esmvalcore/_recipe.py", line 806, in __init__
    raw_recipe['diagnostics'], raw_recipe.get('datasets', []))
  File "/mnt/lustre01/pf/b/b309057/ESMValTool/core/esmvalcore/_recipe.py", line 848, in _initialize_diagnostics
    raw_diagnostic.get('additional_datasets', []))
  File "/mnt/lustre01/pf/b/b309057/ESMValTool/core/esmvalcore/_recipe.py", line 946, in _initialize_preprocessor_output
    self._set_alias(preprocessor_output)
  File "/mnt/lustre01/pf/b/b309057/ESMValTool/core/esmvalcore/_recipe.py", line 999, in _set_alias
    datasets_info.add(alias)
TypeError: unhashable type: 'list'
2019-08-06 08:18:38,325 UTC [27804] INFO    If you suspect this is a bug or need help, please open an issue on https://github.com/ESMValGroup/ESMValTool/issu
es and attach the run/recipe_*.yml and run/main_log_debug.txt files from the output directory.
```

Changed files:
esmvalcore/_recipe.py, tests/integration/test_recipe.py

diff --git a/esmvalcore/_recipe.py b/esmvalcore/_recipe.py
index 4b240e91b..1bc696612 100644
--- a/esmvalcore/_recipe.py
+++ b/esmvalcore/_recipe.py
@@ -960,6 +960,9 @@ class Recipe:
         to the alias only if the previous ones where not enough to fully
         identify the dataset.
 
+        If key values are not strings, they will be joint using '-' if they
+        are iterables or replaced by they string representation if they are not
+
         Function will not modify alias if it is manually added to the recipe
         but it will use the dataset info to compute the others
 
@@ -993,9 +996,20 @@ class Recipe:
             preprocessor output dictionary
         """
         datasets_info = set()
+
+        def _key_str(obj):
+            if isinstance(obj, str):
+                return obj
+            try:
+                return '-'.join(obj)
+            except TypeError:
+                return str(obj)
+
         for variable in preprocessor_output.values():
             for dataset in variable:
-                alias = tuple(dataset.get(key, None) for key in self.info_keys)
+                alias = tuple(
+                    _key_str(dataset.get(key, None)) for key in self.info_keys
+                )
                 datasets_info.add(alias)
                 if 'alias' not in dataset:
                     dataset['alias'] = alias
diff --git a/tests/integration/test_recipe.py b/tests/integration/test_recipe.py
index 7e3b00a5f..d2134cb09 100644
--- a/tests/integration/test_recipe.py
+++ b/tests/integration/test_recipe.py
@@ -845,3 +845,38 @@ def test_alias_generation(tmp_path, patched_datafinder, config_user):
                 assert dataset['alias'] == 'OBS_1'
             else:
                 assert dataset['alias'] == 'OBS_2'
+
+
+def test_concatenation(tmp_path, patched_datafinder, config_user):
+    content = dedent("""
+        diagnostics:
+          diagnostic_name:
+            variables:
+              ta:
+                project: CMIP5
+                mip: Amon
+                start_year: 2000
+                end_year: 2005
+                grid: gn
+                type: reanaly
+                tier: 2
+                version: latest
+                additional_datasets:
+                  - dataset: GFDL-CM3
+                    ensemble: r1i1p1
+                    exp: [historical, rcp85]
+                  - dataset: GFDL-CM3
+                    ensemble: r1i1p1
+                    exp: historical
+            scripts: null
+        """)
+
+    recipe = get_recipe(tmp_path, content, config_user)
+    assert len(recipe.diagnostics) == 1
+    diag = recipe.diagnostics['diagnostic_name']
+    var = diag['preprocessor_output']['ta']
+    for dataset in var:
+        if dataset['exp'] == 'historical':
+            assert dataset['alias'] == 'historical'
+        else:
+            assert dataset['alias'] == 'historical-rcp85'