Skip to content

Commit 37554d0

Browse files
committed
remove hashfiles on error situations, improve logging the diff of hashvalues
1 parent 94a4604 commit 37554d0

File tree

3 files changed

+75
-48
lines changed

3 files changed

+75
-48
lines changed

nipype/pipeline/engine/nodes.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
from future import standard_library
3131

3232
from ... import config, logging
33-
from ...utils.misc import (flatten, unflatten, str2bool)
33+
from ...utils.misc import flatten, unflatten, str2bool, dict_diff
3434
from ...utils.filemanip import (
3535
md5, FileNotFoundError, filename_to_list, list_to_filename,
3636
copyfiles, fnames_presuffix, loadpkl, split_filename, load_json, makedirs,
@@ -292,38 +292,46 @@ def hash_exists(self, updatehash=False):
292292
# Find previous hashfiles
293293
hashfiles = glob(op.join(outdir, '_0x*.json'))
294294
if len(hashfiles) > 1:
295+
for rmfile in hashfiles:
296+
os.remove(rmfile)
297+
295298
raise RuntimeError(
296-
'[Node] Cache ERROR - Found %d previous hashfiles that indicate '
299+
'[Node] Cache ERROR - Found %d previous hashfiles indicating '
297300
'that the ``base_dir`` for this node went stale. Please re-run the '
298301
'workflow.' % len(hashfiles))
299302

300303
# Find unfinished hashfiles and error if any
301304
unfinished = glob(op.join(outdir, '_0x*_unfinished.json'))
302305

306+
# This should not happen, but clean up and break if so.
303307
if unfinished and updatehash:
308+
for rmfile in unfinished:
309+
os.remove(rmfile)
310+
304311
raise RuntimeError(
305-
'[Node] Cache ERROR - Found unfinished hashfiles (%d) that indicate '
312+
'[Node] Cache ERROR - Found unfinished hashfiles (%d) indicating '
306313
'that the ``base_dir`` for this node went stale. Please re-run the '
307314
'workflow.' % len(unfinished))
308315

309316
# Remove outdated hashfile
310317
if hashfiles and hashfiles[0] != hashfile:
311318
logger.info('[Node] Outdated hashfile found for "%s", removing and forcing node '
312-
'to rerun', self.fullname)
319+
'to rerun.', self.fullname)
313320

314321
# If logging is more verbose than INFO (20), print diff between hashes
315-
if logger.getEffectiveLevel() < 20 and hash_exists: # Lazy logging: only < INFO
322+
loglevel = logger.getEffectiveLevel()
323+
if loglevel < 20: # Lazy logging: only < INFO
316324
split_out = split_filename(hashfiles[0])
317325
exp_hash_file_base = split_out[1]
318326
exp_hash = exp_hash_file_base[len('_0x'):]
319-
logger.debug("Previous node hash = %s", exp_hash)
327+
logger.log(loglevel, "[Node] Old/new hashes = %s/%s", exp_hash, hashvalue)
320328
try:
321329
prev_inputs = load_json(hashfiles[0])
322330
except Exception:
323331
pass
324332
else:
325-
logging.logdebug_dict_differences(
326-
prev_inputs, hashed_inputs)
333+
logger.log(loglevel, dict_diff(prev_inputs, hashed_inputs, 10))
334+
327335
os.remove(hashfiles[0])
328336

329337
# Update only possible if it exists

nipype/utils/logger.py

Lines changed: 4 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -97,42 +97,7 @@ def logdebug_dict_differences(self, dold, dnew, prefix=""):
9797
9898
typical use -- log difference for hashed_inputs
9999
"""
100-
# First check inputs, since they usually are lists of tuples
101-
# and dicts are required.
102-
if isinstance(dnew, list):
103-
dnew = dict(dnew)
104-
if isinstance(dold, list):
105-
dold = dict(dold)
106-
107-
# Compare against hashed_inputs
108-
# Keys: should rarely differ
109-
new_keys = set(dnew.keys())
110-
old_keys = set(dold.keys())
111-
if len(new_keys - old_keys):
112-
self._logger.debug("%s not previously seen: %s"
113-
% (prefix, new_keys - old_keys))
114-
if len(old_keys - new_keys):
115-
self._logger.debug("%s not presently seen: %s"
116-
% (prefix, old_keys - new_keys))
117-
118-
# Values in common keys would differ quite often,
119-
# so we need to join the messages together
120-
msgs = []
121-
for k in new_keys.intersection(old_keys):
122-
same = False
123-
try:
124-
new, old = dnew[k], dold[k]
125-
same = new == old
126-
if not same:
127-
# Since JSON does not discriminate between lists and
128-
# tuples, we might need to cast them into the same type
129-
# as the last resort. And lets try to be more generic
130-
same = old.__class__(new) == old
131-
except Exception as e:
132-
same = False
133-
if not same:
134-
msgs += ["%s: %r != %r"
135-
% (k, dnew[k], dold[k])]
136-
if len(msgs):
137-
self._logger.debug("%s values differ in fields: %s" % (prefix,
138-
", ".join(msgs)))
100+
from .misc import dict_diff
101+
self._logger.warning("logdebug_dict_differences has been deprecated, please use "
102+
"nipype.utils.misc.dict_diff.")
103+
self._logger.debug(dict_diff(dold, dnew))

nipype/utils/misc.py

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import inspect
1616

1717
from distutils.version import LooseVersion
18-
from textwrap import dedent
18+
from textwrap import dedent, indent as textwrap_indent
1919
import numpy as np
2020

2121
def human_order_sorted(l):
@@ -229,3 +229,57 @@ def normalize_mc_params(params, source):
229229
params[-1:2:-1] = aff2euler(matrix)
230230

231231
return params
232+
233+
234+
def dict_diff(dold, dnew, indent=0):
235+
"""Helper to log what actually changed from old to new values of
236+
dictionaries.
237+
238+
typical use -- log difference for hashed_inputs
239+
"""
240+
# First check inputs, since they usually are lists of tuples
241+
# and dicts are required.
242+
if isinstance(dnew, list):
243+
dnew = dict(dnew)
244+
if isinstance(dold, list):
245+
dold = dict(dold)
246+
247+
# Compare against hashed_inputs
248+
# Keys: should rarely differ
249+
new_keys = set(dnew.keys())
250+
old_keys = set(dold.keys())
251+
252+
diff = []
253+
if new_keys - old_keys:
254+
diff += [" * keys not previously seen: %s" % (new_keys - old_keys)]
255+
256+
if old_keys - new_keys:
257+
diff += [" * keys not presently seen: %s" % (old_keys - new_keys)]
258+
259+
# Add topical message
260+
if diff:
261+
diff.insert(0, "Dictionaries had differing keys:")
262+
263+
diffkeys = len(diff)
264+
265+
# Values in common keys would differ quite often,
266+
# so we need to join the messages together
267+
for k in new_keys.intersection(old_keys):
268+
same = False
269+
try:
270+
new, old = dnew[k], dold[k]
271+
same = new == old
272+
if not same:
273+
# Since JSON does not discriminate between lists and
274+
# tuples, we might need to cast them into the same type
275+
# as the last resort. And lets try to be more generic
276+
same = old.__class__(new) == old
277+
except Exception:
278+
same = False
279+
if not same:
280+
diff += ["%s: %r != %r" % (k, dnew[k], dold[k])]
281+
282+
if len(diff) > diffkeys:
283+
diff.insert(diffkeys, "Some dictionary entries had differing values:")
284+
285+
return textwrap_indent('\n'.join(diff), ' ' * indent)

0 commit comments

Comments
 (0)