Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix safeCopy to work on Linux #1691

Merged
merged 9 commits into from Apr 25, 2024
27 changes: 24 additions & 3 deletions armi/utils/__init__.py
Expand Up @@ -806,17 +806,38 @@ def merge(self, *otherDictionaries) -> None:

def safeCopy(src: str, dst: str) -> None:
"""This copy overwrites ``shutil.copy`` and checks that copy operation is truly completed before continuing."""
waitTime = 0.01 # 10 ms
# Convert files to OS-independence
src = os.path.abspath(src)
dst = os.path.abspath(dst)
opotowsky marked this conversation as resolved.
Show resolved Hide resolved
if os.path.isdir(dst):
dst = os.path.join(dst, os.path.basename(src))
srcSize = os.path.getsize(src)
shutil.copyfile(src, dst)
john-science marked this conversation as resolved.
Show resolved Hide resolved
shutil.copymode(src, dst)
if "win" in sys.platform:
cmd = f'copy "{src}" "{dst}"'
elif "linux" in sys.platform:
cmd = f'cp "{src}" "{dst}"'
else:
opotowsky marked this conversation as resolved.
Show resolved Hide resolved
runLog.warning(
opotowsky marked this conversation as resolved.
Show resolved Hide resolved
"Cannot perform ``safeCopy`` on files because ARMI only supports "
+ "Linux and Windows."
)
os.system(cmd)
opotowsky marked this conversation as resolved.
Show resolved Hide resolved
waitTime = 0.01 # 10 ms
maxWaitTime = 1800 # 30 min
totalWaitTime = 0
while True:
opotowsky marked this conversation as resolved.
Show resolved Hide resolved
dstSize = os.path.getsize(dst)
if srcSize == dstSize:
break
time.sleep(waitTime)
totalWaitTime += waitTime
if totalWaitTime > maxWaitTime:
runLog.warning(
f"File copy from {dst} to {src} has failed due to exceeding "
+ f"a maximum wait time of {maxWaitTime/60} minutes."
)
break

runLog.extra("Copied {} -> {}".format(src, dst))


Expand Down
30 changes: 30 additions & 0 deletions armi/utils/tests/test_utils.py
Expand Up @@ -14,13 +14,15 @@

"""Testing some utility functions."""
from collections import defaultdict
import os
import unittest

import numpy as np

from armi import utils
from armi.reactor.tests.test_reactors import loadTestReactor
from armi.settings.caseSettings import Settings
from armi.tests import mockRunLogs
from armi.utils import (
directoryChangers,
getPowerFractions,
Expand All @@ -37,6 +39,7 @@
getCumulativeNodeNum,
hasBurnup,
codeTiming,
safeCopy,
)


Expand Down Expand Up @@ -166,6 +169,33 @@ def testFunc():
self.assertEqual(getattr(testFunc, "__doc__"), "Test function docstring.")
self.assertEqual(getattr(testFunc, "__name__"), "testFunc")

def test_safeCopy(self):
with directoryChangers.TemporaryDirectoryChanger():
os.mkdir("dir1")
os.mkdir("dir2")
file1 = "dir1/file1.txt"
with open(file1, "w") as f:
f.write("Hello")
file2 = "dir1\\file2.txt"
with open(file2, "w") as f:
f.write("Hello2")

with mockRunLogs.BufferLog() as mock:
# Test Linuxy file path
self.assertEqual("", mock.getStdout())
safeCopy(file1, "dir2")
self.assertIn("Copied", mock.getStdout())
self.assertIn("file1", mock.getStdout())
self.assertIn("->", mock.getStdout())
# Clean up for next safeCopy
mock.emptyStdout()
# Test Windowsy file path
self.assertEqual("", mock.getStdout())
safeCopy(file2, "dir2")
self.assertIn("Copied", mock.getStdout())
self.assertIn("file2", mock.getStdout())
self.assertIn("->", mock.getStdout())


class CyclesSettingsTests(unittest.TestCase):
"""
Expand Down
1 change: 1 addition & 0 deletions doc/release/0.3.rst
Expand Up @@ -37,6 +37,7 @@ API Changes
Bug Fixes
---------
#. Fixed four bugs with "corners up" hex grids. (`PR#1649 <https://github.com/terrapower/armi/pull/1649>`_)
#. Fixed ``safeCopy`` to work on both Windows and Linux with strict permissions (`PR#1691 <https://github.com/terrapower/armi/pull/1691>`_)
#. TBD

Quality Work
Expand Down