Repository: gramener/gramex

Issue:
Invalid computed variable crashes Gramex
If we have 

```yaml
variables:
    INVALID_FUNCTION:
        function: nonexistent()
```

... this prevents Gramex from starting up. It should warn and continue with the default value of the variable instead.

Discussion:

Patch:
diff --git a/gramex/config.py b/gramex/config.py
index 8c137231..4832bfe7 100644
--- a/gramex/config.py
+++ b/gramex/config.py
@@ -240,9 +240,12 @@ def _calc_value(val, key):
         if val.get('function'):
             from .transforms import build_transform
             function = build_transform(val, vars={'key': None}, filename=f'config:{key}')
-            for result in function(key):
-                if result is not None:
-                    return result
+            try:
+                for result in function(key):
+                    if result is not None:
+                        return result
+            except Exception:
+                app_log.exception(f'Error in calculated variable: {key}: {val}')
             return _calc_value(val.get('default', None), key)
         else:
             return {k: _calc_value(v, k) for k, v in val.items()}


Test patch:
diff --git a/testlib/test_config/__init__.py b/testlib/test_config/__init__.py
index 842bb17a..3177ae15 100644
--- a/testlib/test_config/__init__.py
+++ b/testlib/test_config/__init__.py
@@ -224,6 +224,8 @@ class TestPathConfig(unittest.TestCase):
             eq_(conf['%s_FUNCTION' % key], key)
             # Default functions "underride" values
             eq_(conf['%s_DEFAULT_FUNCTION' % key], 'base')
+            # Invalid functions switch to defaults
+            eq_(conf['%s_INVALID_FUNCTION' % key], 'DEFAULT')
             # Functions can use variables using gramex.config.variables
             eq_(conf['%s_FUNCTION_VAR' % key], conf.base_ROOT + key)
             # Derived variables
diff --git a/testlib/test_config/config.template.base.yaml b/testlib/test_config/config.template.base.yaml
index 60bbba70..d6273d6a 100644
--- a/testlib/test_config/config.template.base.yaml
+++ b/testlib/test_config/config.template.base.yaml
@@ -11,6 +11,9 @@ variables:
         default:
             function: str
             args: ['base']
+    INVALID_FUNCTION:
+        function: nonexistent()
+        default: DEFAULT
     DEFAULT_OBJECT:
         default:
             x: $THIS
@@ -85,6 +88,7 @@ base_THIS: $THIS
 base_DEFAULT: $BASE_DEFAULT
 base_FUNCTION: $FUNCTION
 base_DEFAULT_FUNCTION: $DEFAULT_FUNCTION
+base_INVALID_FUNCTION: $INVALID_FUNCTION
 base_FUNCTION_VAR: $FUNCTION_VAR
 base_DERIVED: $DERIVED
 base_YAMLURL_VAR: $YAMLURL_VAR
diff --git a/testlib/test_config/config.template.child.yaml b/testlib/test_config/config.template.child.yaml
index 0fe0b821..5cf07211 100644
--- a/testlib/test_config/config.template.child.yaml
+++ b/testlib/test_config/config.template.child.yaml
@@ -25,6 +25,7 @@ child_THIS: $THIS
 child_DEFAULT: $CHILD_DEFAULT
 child_FUNCTION: $FUNCTION
 child_DEFAULT_FUNCTION: $DEFAULT_FUNCTION
+child_INVALID_FUNCTION: $INVALID_FUNCTION
 child_FUNCTION_VAR: $FUNCTION_VAR
 child_DERIVED: $DERIVED
 child_URLROOT: $URLROOT
diff --git a/testlib/test_config/dir/config.template.subdir.yaml b/testlib/test_config/dir/config.template.subdir.yaml
index e94bcf13..c9fc6745 100644
--- a/testlib/test_config/dir/config.template.subdir.yaml
+++ b/testlib/test_config/dir/config.template.subdir.yaml
@@ -25,6 +25,7 @@ subdir_THIS: $THIS
 subdir_DEFAULT: $SUBDIR_DEFAULT
 subdir_FUNCTION: $FUNCTION
 subdir_DEFAULT_FUNCTION: $DEFAULT_FUNCTION
+subdir_INVALID_FUNCTION: $INVALID_FUNCTION
 subdir_FUNCTION_VAR: $FUNCTION_VAR
 subdir_DERIVED: $DERIVED
 subdir_YAMLURL_VAR: $YAMLURL_VAR