Repository: ESMCI/cime

Issue:
casename restrictions should be enforced 
There are certain characters which cannot be included in a casename, these characters should 
be trapped and an error reported.   This issue is moved from CESM-DEVELOPMENT/cime 38


Discussion:
See also CESM-DEVELOPMENT issue 334
In addition, scripts should check the case name length against the length currently allowed by various components - see https://github.com/CESM-Development/cime/issues/85
What maximum length do we want?  What is the list of restrictions for case name?

Resolving patch:
diff --git a/scripts/lib/CIME/XML/env_base.py b/scripts/lib/CIME/XML/env_base.py
--- a/scripts/lib/CIME/XML/env_base.py
+++ b/scripts/lib/CIME/XML/env_base.py
@@ -5,7 +5,7 @@
 from CIME.XML.standard_module_setup import *
 from CIME.XML.entry_id import EntryID
 from CIME.XML.headers import Headers
-from CIME.utils import convert_to_type, convert_to_string
+from CIME.utils import convert_to_type
 logger = logging.getLogger(__name__)
 
 class EnvBase(EntryID):
diff --git a/scripts/lib/CIME/utils.py b/scripts/lib/CIME/utils.py
--- a/scripts/lib/CIME/utils.py
+++ b/scripts/lib/CIME/utils.py
@@ -26,11 +26,39 @@ def expect(condition, error_msg, exc_type=SystemExit, error_prefix="ERROR:"):
         if logger.isEnabledFor(logging.DEBUG):
             import pdb
             pdb.set_trace()
-        raise exc_type("%s %s" % (error_prefix,error_msg))
+        logger.error("%s %s", error_prefix,error_msg, exc_info=True)
+        raise exc_type("%s %s"%(error_prefix,error_msg))
 
 def id_generator(size=6, chars=string.ascii_lowercase + string.digits):
     return ''.join(random.choice(chars) for _ in range(size))
 
+def check_name(fullname, additional_chars=None, fullpath=False):
+    """
+    check for unallowed characters in name, this routine only
+    checks the final name and does not check if path exists or is
+    writable
+
+    >>> check_name("test.id", additional_chars=".")
+    False
+    >>> check_name("case.name", fullpath=False)
+    True
+    >>> check_name("/some/file/path/case.name", fullpath=True)
+    True
+    """
+
+    chars = '<>/{}[\]~`@' # pylint: disable=anomalous-backslash-in-string
+    if additional_chars is not None:
+        chars += additional_chars
+    if fullpath:
+        _, name = os.path.split(fullname)
+    else:
+        name = fullname
+    match = re.search(r"["+re.escape(chars)+"]", name)
+    if match is not None:
+        logger.warn("Illegal character %s found in name %s"%(match.group(0), name))
+        return False
+    return True
+
 # Should only be called from get_cime_config()
 def _read_cime_config_file():
     """


Test patch:
diff --git a/scripts/create_test b/scripts/create_test
--- a/scripts/create_test
+++ b/scripts/create_test
@@ -20,6 +20,7 @@ import argparse, math, glob
 
 logger = logging.getLogger(__name__)
 
+
 ###############################################################################
 def parse_command_line(args, description):
 ###############################################################################
@@ -278,6 +279,9 @@ OR
 
     if args.test_id is None:
         args.test_id = "%s_%s"%(CIME.utils.get_timestamp(), CIME.utils.id_generator())
+    else:
+        expect(CIME.utils.check_name(args.test_id, additional_chars="."),
+               "invalid test-id argument provided")
 
     if args.testfile is not None:
         with open(args.testfile, "r") as fd: