Repository: terrapower/armi
Pull request #1806 (2024-08-01 16:19:52)

Don't make a runLog named -mpi.log
## What is the change?

<!-- MANDATORY: Describe the change -->

Ensure that the `caseTitle` used to named the concatenated worker log file is not an empty string.

## Why is the change being made?

<!-- MANDATORY: Explain why the change is necessary -->
<!-- Optional: Link to any related GitHub Issues -->

In a parallel run, each worker writes logging output to a separate file in the `logs/` directory. At the end of the run, these files are all concatenated into a single file. The logic for picking the name of the file is to deduce the `caseTitle` from the name of one of these log files, e.g. `ARMI.caseTitle.0001.stdout`. However, sometimes a file with the name `ARMI..0001.stdout` may exist in the `logs/` directory in addition to the `ARMI.caseTitle.0001.stdout` file, which results in a concatenated file named `-mpi.log`.

Instead of grabbing the first string we find between the first and second period characters in any log file name, we take the first string that isn't simply `""`. Then the name of the log file will be `<something>-mpi.log`.

---

## Checklist

<!--
    You (the pull requester) should put an `x` in the boxes below you have completed.

    (If a checkbox doesn't apply to your PR, check it anyway.)
-->

- [x] This PR has only [one purpose or idea](https://terrapower.github.io/armi/developer/tooling.html#one-idea-one-pr).
- [x] [Tests](https://terrapower.github.io/armi/developer/tooling.html#test-it) have been added/updated to verify any new/changed code.

<!-- Check the code quality -->

- [x] The code style follows [good practices](https://terrapower.github.io/armi/developer/standards_and_practices.html).
- [x] The commit message(s) follow [good practices](https://terrapower.github.io/armi/developer/tooling.html).

<!-- Check the project-level cruft -->

- [x] The [release notes](https://terrapower.github.io/armi/developer/tooling.html#add-release-notes) have been updated if necessary.
- [x] The [documentation](https://terrapower.github.io/armi/developer/tooling.html#document-it) is still up-to-date in the `doc` folder.
- [x] The dependencies are still up-to-date in `pyproject.toml`.


Patch:
diff --git a/armi/runLog.py b/armi/runLog.py
index cb79e484..484672df 100644
--- a/armi/runLog.py
+++ b/armi/runLog.py
@@ -44,17 +44,16 @@ Or change the log level the same way:
 
     runLog.setVerbosity('debug')
 """
-from glob import glob
 import collections
 import logging
 import operator
 import os
 import sys
 import time
+from glob import glob
 
 from armi import context
 
-
 # global constants
 _ADD_LOG_METHOD_STR = """def {0}(self, message, *args, **kws):
     if self.isEnabledFor({1}):
@@ -354,8 +353,10 @@ def concatenateLogs(logDir=None):
         stdoutFile = os.path.normpath(stdoutPath).split(os.sep)[-1]
         prefix = STDOUT_LOGGER_NAME + "."
         if stdoutFile[0 : len(prefix)] == prefix:
-            caseTitle = stdoutFile.split(".")[-3]
-            break
+            candidate = stdoutFile.split(".")[-3]
+            if len(candidate) > 0:
+                caseTitle = candidate
+                break
 
     combinedLogName = os.path.join(logDir, "{}-mpi.log".format(caseTitle))
     with open(combinedLogName, "w") as workerLog:


Test patch:
diff --git a/armi/tests/test_runLog.py b/armi/tests/test_runLog.py
index c79bfd13..ddfc9198 100644
--- a/armi/tests/test_runLog.py
+++ b/armi/tests/test_runLog.py
@@ -12,11 +12,11 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 """Tests of the runLog tooling."""
-from io import StringIO
-from shutil import rmtree
 import logging
 import os
 import unittest
+from io import StringIO
+from shutil import rmtree
 
 from armi import runLog
 from armi.tests import mockRunLogs
@@ -388,8 +388,16 @@ class TestRunLog(unittest.TestCase):
             with open(stdoutFile2, "w") as f:
                 f.write("hello other world\n")
 
+            # verify behavior for a corner case
+            stdoutFile3 = os.path.join(
+                logDir, "{}..0000.stdout".format(runLog.STDOUT_LOGGER_NAME)
+            )
+            with open(stdoutFile3, "w") as f:
+                f.write("hello world again\n")
+
             self.assertTrue(os.path.exists(stdoutFile1))
             self.assertTrue(os.path.exists(stdoutFile2))
+            self.assertTrue(os.path.exists(stdoutFile3))
 
             # create a stderr file
             stderrFile = os.path.join(
@@ -408,8 +416,22 @@ class TestRunLog(unittest.TestCase):
             self.assertTrue(os.path.exists(combinedLogFile))
             self.assertFalse(os.path.exists(stdoutFile1))
             self.assertFalse(os.path.exists(stdoutFile2))
+            self.assertFalse(os.path.exists(stdoutFile3))
             self.assertFalse(os.path.exists(stderrFile))
 
+            # verify behavior for a corner case
+            stdoutFile3 = os.path.join(
+                logDir, "{}..0000.stdout".format(runLog.STDOUT_LOGGER_NAME)
+            )
+            with open(stdoutFile3, "w") as f:
+                f.write("hello world again\n")
+            # concat logs
+            runLog.concatenateLogs(logDir=logDir)
+            # verify output
+            combinedLogFile = os.path.join(logDir, "armi-workers-mpi.log")
+            self.assertTrue(os.path.exists(combinedLogFile))
+            self.assertFalse(os.path.exists(stdoutFile3))
+
     def test_createLogDir(self):
         """Test the createLogDir() method.