Repository: ESMCI/cime

Issue:
Setting PROJECT_REQUIRED to FALSE has the same effect as TRUE
On my local machine, I tried having this in my config_machines.xml file:

```xml
    <PROJECT_REQUIRED>FALSE</PROJECT_REQUIRED>
```

But get this message from create_newcase:

```
ERROR: PROJECT_REQUIRED is true but no project found
```

It seems that a FALSE value still results in the conditional being True in this line in case.py:

```python
        elif machobj.get_value("PROJECT_REQUIRED"):
            expect(project is not None, "PROJECT_REQUIRED is true but no project found")
```

A simple fix would be to call convert_to_type on the return value in the above conditional, to convert it to logical. But I have a feeling that there is a more elegant solution that is probably used for other xml variables....

The workaround is to leave out the <PROJECT_REQUIRED> entry entirely for your machine.


Discussion:

Resolving patch:
diff --git a/scripts/lib/CIME/XML/machines.py b/scripts/lib/CIME/XML/machines.py
--- a/scripts/lib/CIME/XML/machines.py
+++ b/scripts/lib/CIME/XML/machines.py
@@ -4,6 +4,7 @@
 from CIME.XML.standard_module_setup import *
 from CIME.XML.generic_xml import GenericXML
 from CIME.XML.files import Files
+from CIME.utils import convert_to_unknown_type
 
 import socket
 
@@ -191,6 +192,8 @@ def get_value(self, name, attributes=None, resolved=True, subgroup=None):
             elif name in os.environ:
                 value = os.environ[name]
 
+            value = convert_to_unknown_type(value)
+
         return value
 
     def get_field_from_list(self, listname, reqval=None, attributes=None):
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
@@ -714,9 +714,9 @@ def convert_to_type(value, type_str, vid=""):
                 expect(False, "Entry %s was listed as type int but value '%s' is not valid int" % (vid, value))
 
         elif type_str == "logical":
-            expect(value in ["TRUE", "FALSE","true","false"],
+            expect(value.upper() in ["TRUE", "FALSE"],
                    "Entry %s was listed as type logical but had val '%s' instead of TRUE or FALSE" % (vid, value))
-            value = value == "TRUE" or value == "true"
+            value = value.upper() == "TRUE"
 
         elif type_str == "real":
             try:
@@ -729,6 +729,36 @@ def convert_to_type(value, type_str, vid=""):
 
     return value
 
+def convert_to_unknown_type(value):
+    """
+    Convert value to it's real type by probing conversions.
+    """
+    if value is not None:
+
+        # Attempt to convert to logical
+        if value.upper() in ["TRUE", "FALSE"]:
+            return value.upper() == "TRUE"
+
+        # Attempt to convert to integer
+        try:
+            value = int(eval(value))
+        except:
+            pass
+        else:
+            return value
+
+        # Attempt to convert to float
+        try:
+            value = float(value)
+        except:
+            pass
+        else:
+            return value
+
+        # Just treat as string
+
+    return value
+
 def convert_to_string(value, type_str=None, vid=""):
     """
     Convert value back to string.


Test patch:
diff --git a/scripts/create_test b/scripts/create_test
--- a/scripts/create_test
+++ b/scripts/create_test
@@ -412,7 +412,7 @@ def single_submit_impl(machine_name, test_id, proc_pool, project, args, job_cost
         submit_cmd  = env_batch.get_value("batch_submit", subgroup=None)
         submit_args = env_batch.get_submit_args(case, "case.run")
 
-    tasks_per_node = int(mach.get_value("PES_PER_NODE"))
+    tasks_per_node = mach.get_value("PES_PER_NODE")
     num_nodes = int(math.ceil(float(proc_pool) / tasks_per_node))
     if wall_time is None:
         wall_time = compute_total_time(job_cost_map, proc_pool)
diff --git a/scripts/lib/CIME/test_scheduler.py b/scripts/lib/CIME/test_scheduler.py
--- a/scripts/lib/CIME/test_scheduler.py
+++ b/scripts/lib/CIME/test_scheduler.py
@@ -150,7 +150,7 @@ def __init__(self, test_names, test_data=None,
 
         if parallel_jobs is None:
             self._parallel_jobs = min(len(test_names),
-                                      int(self._machobj.get_value("MAX_TASKS_PER_NODE")))
+                                      self._machobj.get_value("MAX_TASKS_PER_NODE"))
         else:
             self._parallel_jobs = parallel_jobs
 
diff --git a/scripts/tests/scripts_regression_tests.py b/scripts/tests/scripts_regression_tests.py
--- a/scripts/tests/scripts_regression_tests.py
+++ b/scripts/tests/scripts_regression_tests.py
@@ -1268,6 +1268,9 @@ def test_cime_case(self):
         run_cmd_assert_result(self, "%s/create_test TESTRUNPASS_P1.f19_g16_rx1.A -t %s --no-build --test-root %s --output-root %s"
                               % (SCRIPT_DIR, self._baseline_name, TEST_ROOT, TEST_ROOT))
 
+        self.assertEqual(type(MACHINE.get_value("MAX_TASKS_PER_NODE")), int)
+        self.assertTrue(type(MACHINE.get_value("PROJECT_REQUIRED")) in [type(None) , bool])
+
         casedir = os.path.join(self._testroot,
                                "%s.%s" % (CIME.utils.get_full_test_name("TESTRUNPASS_P1.f19_g16_rx1.A", machine=self._machine, compiler=self._compiler), self._baseline_name))
         self.assertTrue(os.path.isdir(casedir), msg="Missing casedir '%s'" % casedir)