Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions easybuild/framework/easyconfig/easyconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -790,9 +790,12 @@ def all_dependencies(self):

return self._all_dependencies

def dump(self, fp):
def dump(self, fp, overwrite=True, backup=False):
"""
Dump this easyconfig to file, with the given filename.

:param overwrite: overwrite existing file at specified location without use of --force
:param backup: create backup of existing file before overwriting it
"""
orig_enable_templating = self.enable_templating

Expand Down Expand Up @@ -828,7 +831,7 @@ def dump(self, fp):
ectxt = autopep8.fix_code(ectxt, options=autopep8_opts)
self.log.debug("Dumped easyconfig after autopep8 reformatting: %s", ectxt)

write_file(fp, ectxt)
write_file(fp, ectxt, overwrite=overwrite, backup=backup, verbose=backup)

self.enable_templating = orig_enable_templating

Expand Down
31 changes: 13 additions & 18 deletions easybuild/framework/easyconfig/tweak.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
:author: Toon Willems (Ghent University)
:author: Fotis Georgatos (Uni.Lu, NTUA)
:author: Alan O'Cais (Juelich Supercomputing Centre)
:author: Maxime Boissonneault (Universite Laval, Calcul Quebec, Compute Canada)
"""
import copy
import glob
Expand Down Expand Up @@ -357,14 +358,7 @@ def __repr__(self):
_log.debug("Generated file name for tweaked easyconfig file: %s", tweaked_ec)

# write out tweaked easyconfig file
if os.path.exists(tweaked_ec):
if build_option('force'):
print_warning("Overwriting existing file at %s with tweaked easyconfig file (due to --force)", tweaked_ec)
else:
raise EasyBuildError("A file already exists at %s where tweaked easyconfig file would be written",
tweaked_ec)

write_file(tweaked_ec, ectxt)
write_file(tweaked_ec, ectxt, backup=True, overwrite=False, verbose=True)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the point of this? If you're not going to overwrite then why backup? Shouldn't backup imply overwrite?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok, I understand it now but the kw name is not very helpful, how about unforced_overwrite?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overwrite=False means "don't overwrite unless --force is used"

Without also adding backup=True, you may overwrite when using --force without backing up the file.

Whether you want a backup or not depends on in what context you're calling write_file.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, but the kw name needs tweaking, it's not unreasonable to expect overwrite=False not to overwrite.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had overwrite_without_force before, which I found too long, but would that be better?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about always_overwrite ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overwrite_without_force is long but since the option is so connected to --force I think it is worth the keystrokes, it will save people having to read the docstring

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mboisson always_overwrite makes sense to me. Can you make that change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm kind of lost in all of the changes you PR'd to us now :/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I did it.

_log.info("Tweaked easyconfig file written to %s", tweaked_ec)

return tweaked_ec
Expand Down Expand Up @@ -489,17 +483,17 @@ def select_or_generate_ec(fp, paths, specs):
# TOOLCHAIN NAME

# we can't rely on set, because we also need to be able to obtain a list of unique lists
def unique(l):
def unique(lst):
"""Retain unique elements in a sorted list."""
l = sorted(l)
if len(l) > 1:
l2 = [l[0]]
for x in l:
if not x == l2[-1]:
l2.append(x)
return l2
lst = sorted(lst)
if len(lst) > 1:
res = [lst[0]]
for x in lst:
if not x == res[-1]:
res.append(x)
return res
else:
return l
return lst

# determine list of unique toolchain names
tcnames = unique([x[0]['toolchain']['name'] for x in ecs_and_files])
Expand Down Expand Up @@ -856,7 +850,8 @@ def map_easyconfig_to_target_tc_hierarchy(ec_spec, toolchain_mapping, targetdir=
# Determine the name of the modified easyconfig and dump it to target_dir
ec_filename = '%s-%s.eb' % (parsed_ec['ec']['name'], det_full_ec_version(parsed_ec['ec']))
tweaked_spec = os.path.join(targetdir or tempfile.gettempdir(), ec_filename)
parsed_ec['ec'].dump(tweaked_spec)

parsed_ec['ec'].dump(tweaked_spec, overwrite=False, backup=True)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto

_log.debug("Dumped easyconfig tweaked via --try-toolchain* to %s", tweaked_spec)

return tweaked_spec
19 changes: 15 additions & 4 deletions easybuild/tools/filetools.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ def read_file(path, log_error=True):
return txt


def write_file(path, txt, append=False, forced=False, backup=False):
def write_file(path, txt, append=False, forced=False, backup=False, overwrite=True, verbose=False):
"""
Write given contents to file at given path;
overwrites current file contents without backup by default!
Expand All @@ -203,15 +203,26 @@ def write_file(path, txt, append=False, forced=False, backup=False):
:param append: append to existing file rather than overwrite
:param forced: force actually writing file in (extended) dry run mode
:param backup: back up existing file before overwriting or modifying it
:param overwrite: don't require --force to overwrite an existing file
:param verbose: be verbose, i.e. inform where backup file was created
"""
# early exit in 'dry run' mode
if not forced and build_option('extended_dry_run'):
dry_run_msg("file written: %s" % path, silent=build_option('silent'))
return

if backup and os.path.exists(path):
backup = back_up_file(path)
_log.info("Existing file %s backed up to %s", path, backup)
if os.path.exists(path):
if not append:
if overwrite or build_option('force'):
_log.info("Overwriting existing file %s", path)
else:
raise EasyBuildError("File exists, not overwriting it without --force: %s", path)

if backup:
backed_up_fp = back_up_file(path)
_log.info("Existing file %s backed up to %s", path, backed_up_fp)
if verbose:
print_msg("Backup of %s created at %s" % (path, backed_up_fp))

# note: we can't use try-except-finally, because Python 2.4 doesn't support it as a single block
try:
Expand Down
29 changes: 28 additions & 1 deletion test/framework/filetools.py
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,34 @@ def test_read_write_file(self):
self.assertEqual(ft.read_file(backup1), txt + txt2)
self.assertEqual(ft.read_file(backup2), 'foo')

# tese use of 'verbose' to make write_file print location of backed up file
self.mock_stdout(True)
ft.write_file(fp, 'foo', backup=True, verbose=True)
stdout = self.get_stdout()
self.mock_stdout(False)
regex = re.compile("^== Backup of .*/test.txt created at .*/test.txt.bak_[0-9]*")
self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout))

# by default, write_file will just blindly overwrite an already existing file
self.assertTrue(os.path.exists(fp))
ft.write_file(fp, 'blah')
self.assertEqual(ft.read_file(fp), 'blah')

# blind overwriting can be disabled via 'overwrite'
error_pattern = "File exists, not overwriting it without --force: %s" % fp
self.assertErrorRegex(EasyBuildError, error_pattern, ft.write_file, fp, 'blah', overwrite=False)
self.assertErrorRegex(EasyBuildError, error_pattern, ft.write_file, fp, 'blah', overwrite=False, backup=True)

# use of --force ensuring that file gets written regardless of whether or not it exists already
build_options = {'force': True}
init_config(build_options=build_options)

ft.write_file(fp, 'overwrittenbyforce', overwrite=False)
self.assertEqual(ft.read_file(fp), 'overwrittenbyforce')

ft.write_file(fp, 'overwrittenbyforcewithbackup', overwrite=False, backup=True)
self.assertEqual(ft.read_file(fp), 'overwrittenbyforcewithbackup')

# also test behaviour of write_file under --dry-run
build_options = {
'extended_dry_run': True,
Expand All @@ -568,7 +596,6 @@ def test_read_write_file(self):
self.assertTrue(os.path.exists(foo))
self.assertEqual(ft.read_file(foo), 'bar')


def test_det_patched_files(self):
"""Test det_patched_files function."""
toy_patch_fn = 'toy-0.0_fix-silly-typo-in-printf-statement.patch'
Expand Down
2 changes: 1 addition & 1 deletion test/framework/tweak.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def test_tweak_one_version(self):
self.assertEqual(val, tweaked_val, "Different value for %s parameter: %s vs %s" % (key, val, tweaked_val))

# check behaviour if target file already exists
error_pattern = "A file already exists at .* where tweaked easyconfig file would be written"
error_pattern = "File exists, not overwriting it without --force"
self.assertErrorRegex(EasyBuildError, error_pattern, tweak_one, toy_ec, tweaked_toy_ec, {'version': '1.2.3'})

# existing file does get overwritten when --force is used
Expand Down