Skip to content

Commit e681974

Browse files
committed
[WIP,FIX] Bugs found after #1572 and #1591
Found corner cases that failed when calculating the hash of dicts - fixes #1620. Also found that list of lists of ints do not work in #1591. Fixes also that one.
1 parent 5c9a751 commit e681974

File tree

6 files changed

+59
-45
lines changed

6 files changed

+59
-45
lines changed

examples/smri_ants_registration.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import urllib.error
2525
import urllib.parse
2626
from nipype.interfaces.ants import Registration
27+
from nipype.testing import example_data
2728

2829
"""
2930
2. Download T1 volumes into home directory
@@ -61,11 +62,12 @@
6162

6263
"""
6364
3. Define the parameters of the registration. Settings are
64-
saved in ``smri_ants_registration_settings.json``.
65+
found in the file ``smri_ants_registration_settings.json``
66+
distributed with the ``example_data`` of `nipype`.
6567
6668
"""
6769

68-
reg = Registration(from_file='./smri_ants_registration_settings.json')
70+
reg = Registration(from_file=example_data('smri_ants_registration_settings.json'))
6971
reg.inputs.fixed_image = input_images[0]
7072
reg.inputs.moving_image = input_images[1]
7173

nipype/interfaces/base.py

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,14 @@ def _clean_container(self, object, undefinedval=None, skipundefined=False):
536536
out = undefinedval
537537
return out
538538

539+
def has_metadata(self, name, metadata, value=None, recursive=True):
540+
"""
541+
Return has_metadata for the requested trait name in this
542+
interface
543+
"""
544+
return has_metadata(self.trait(name).trait_type, metadata, value,
545+
recursive)
546+
539547
def get_hashval(self, hash_method=None):
540548
"""Return a dictionary of our items with hashes for each file.
541549
@@ -559,62 +567,61 @@ def get_hashval(self, hash_method=None):
559567

560568
dict_withhash = []
561569
dict_nofilename = []
562-
for name, val in sorted(self.get().items()):
563-
if isdefined(val):
564-
trait = self.trait(name)
565-
if has_metadata(trait.trait_type, "nohash", True):
566-
continue
567-
hash_files = (not has_metadata(trait.trait_type, "hash_files",
568-
False) and not
569-
has_metadata(trait.trait_type, "name_source"))
570-
dict_nofilename.append((name,
571-
self._get_sorteddict(val, hash_method=hash_method,
572-
hash_files=hash_files)))
573-
dict_withhash.append((name,
574-
self._get_sorteddict(val, True, hash_method=hash_method,
575-
hash_files=hash_files)))
570+
571+
for name, val in sorted(self.get_traitsfree().items()):
572+
if self.has_metadata(name, "nohash", True):
573+
continue
574+
575+
hash_files = (not has_metadata(name, "hash_files", False) and not
576+
has_metadata(name, "name_source"))
577+
dict_nofilename.append((name,
578+
self._get_sorteddict(val, hash_method=hash_method,
579+
hash_files=hash_files)))
580+
dict_withhash.append((name,
581+
self._get_sorteddict(val, True, hash_method=hash_method,
582+
hash_files=hash_files)))
576583
return dict_withhash, md5(encode_dict(dict_nofilename).encode()).hexdigest()
577584

578-
def _get_sorteddict(self, object, dictwithhash=False, hash_method=None,
585+
def _get_sorteddict(self, theobject, dictwithhash=False, hash_method=None,
579586
hash_files=True):
580-
if isinstance(object, dict):
587+
if isinstance(theobject, dict):
581588
out = []
582-
for key, val in sorted(object.items()):
589+
for key, val in sorted(theobject.items()):
583590
if isdefined(val):
584591
out.append((key,
585592
self._get_sorteddict(val, dictwithhash,
586593
hash_method=hash_method,
587594
hash_files=hash_files)))
588-
elif isinstance(object, (list, tuple)):
595+
elif isinstance(theobject, (list, tuple)):
589596
out = []
590-
for val in object:
597+
for val in theobject:
591598
if isdefined(val):
592599
out.append(self._get_sorteddict(val, dictwithhash,
593600
hash_method=hash_method,
594601
hash_files=hash_files))
595-
if isinstance(object, tuple):
602+
if isinstance(theobject, tuple):
596603
out = tuple(out)
597604
else:
598-
if isdefined(object):
599-
if (hash_files and isinstance(object, (str, bytes)) and
600-
os.path.isfile(object)):
605+
if isdefined(theobject):
606+
if (hash_files and isinstance(theobject, (str, bytes)) and
607+
os.path.isfile(theobject)):
601608
if hash_method is None:
602609
hash_method = config.get('execution', 'hash_method')
603610

604611
if hash_method.lower() == 'timestamp':
605-
hash = hash_timestamp(object)
612+
hash = hash_timestamp(theobject)
606613
elif hash_method.lower() == 'content':
607-
hash = hash_infile(object)
614+
hash = hash_infile(theobject)
608615
else:
609616
raise Exception("Unknown hash method: %s" % hash_method)
610617
if dictwithhash:
611-
out = (object, hash)
618+
out = (theobject, hash)
612619
else:
613620
out = hash
614-
elif isinstance(object, float):
615-
out = '%.10f' % object
621+
elif isinstance(theobject, float):
622+
out = '%.10f' % theobject
616623
else:
617-
out = object
624+
out = theobject
618625
return out
619626

620627

nipype/interfaces/tests/test_base.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
from nipype.testing import (assert_equal, assert_not_equal, assert_raises,
1515
assert_true, assert_false, with_setup, package_check,
16-
skipif)
16+
skipif, example_data)
1717
import nipype.interfaces.base as nib
1818
from nipype.utils.filemanip import split_filename
1919
from nipype.interfaces.base import Undefined, config
@@ -497,6 +497,13 @@ def __init__(self, **inputs):
497497
bif6.load_inputs_from_json(tmp_json)
498498
yield assert_equal, bif6.inputs.get_traitsfree(), inputs_dict
499499

500+
# test get hashval in a complex interface
501+
from nipype.interfaces.ants import Registration
502+
tsthash = Registration(from_file=example_data('smri_ants_registration_settings.json'))
503+
hashed_inputs, hashvalue = tsthash.inputs.get_hashval(hash_method='timestamp')
504+
# yield assert_equal, hashed_inputs, [('input1', 12), ('input3', True), ('input4', 'some string')]
505+
yield assert_equal, hashvalue, '9ab944cbccba61475becb9eac65052af'
506+
500507
def assert_not_raises(fn, *args, **kwargs):
501508
fn(*args, **kwargs)
502509
return True

nipype/utils/filemanip.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -94,18 +94,16 @@ def encode_dict(value):
9494
if isinstance(value, str):
9595
value = value.encode()
9696

97-
if isinstance(value, tuple):
98-
val0 = encode_dict(value[0])
99-
val1 = encode_dict(value[1])
100-
return '(' + val0 + ', ' + val1 + ')'
101-
102-
if isinstance(value, list):
103-
retval = '['
97+
istuple = isinstance(value, tuple)
98+
if isinstance(value, list) or istuple:
99+
retval = '(' if istuple else '['
104100
for i, v in enumerate(value):
105101
if i > 0:
106102
retval += ', '
107103
retval += encode_dict(v)
108-
return retval + ']'
104+
retval += ')' if istuple else ']'
105+
return retval
106+
109107
return repr(value)
110108

111109
def fname_presuffix(fname, prefix='', suffix='', newpath=None, use_ext=True):

nipype/utils/tests/test_filemanip.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010

1111
from ...testing import assert_equal, assert_true, assert_false, TempFATFS
1212
from ...utils.filemanip import (save_json, load_json,
13-
fname_presuffix, fnames_presuffix,
14-
hash_rename, check_forhash,
15-
copyfile, copyfiles,
16-
filename_to_list, list_to_filename,
17-
split_filename, get_related_files)
13+
fname_presuffix, fnames_presuffix,
14+
hash_rename, check_forhash,
15+
copyfile, copyfiles,
16+
filename_to_list, list_to_filename,
17+
split_filename, get_related_files)
1818

1919
import numpy as np
2020

0 commit comments

Comments
 (0)