Skip to content

Commit 4c745fa

Browse files
committed
enh: added output versioning as well
1 parent 9429806 commit 4c745fa

File tree

2 files changed

+83
-31
lines changed

2 files changed

+83
-31
lines changed

nipype/interfaces/base.py

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -823,29 +823,35 @@ def _check_mandatory_inputs(self):
823823
transient=None).items():
824824
self._check_requires(spec, name, getattr(self.inputs, name))
825825

826-
def _check_input_version_requirements(self):
826+
def _check_version_requirements(self, trait_object, raise_exception=True):
827827
""" Raises an exception on version mismatch
828828
"""
829+
unavailable_traits = []
829830
version = LooseVersion(str(self.version))
830831
if not version:
831832
return
832833
# check minimum version
833-
names = self.inputs.trait_names(**dict(min_ver=lambda t: t is not None))
834+
names = trait_object.trait_names(**dict(min_ver=lambda t: t is not None))
834835
for name in names:
835-
if not isdefined(getattr(self.inputs, name)):
836-
continue
837-
min_ver = LooseVersion(str(self.inputs.traits()[name].min_ver))
836+
min_ver = LooseVersion(str(trait_object.traits()[name].min_ver))
838837
if min_ver > version:
839-
raise Exception('Input %s (%s) (version %s < required %s)' %
838+
unavailable_traits.append(name)
839+
if not isdefined(getattr(trait_object, name)):
840+
continue
841+
if raise_exception:
842+
raise Exception('Trait %s (%s) (version %s < required %s)' %
840843
(name, self.__class__.__name__, version, min_ver))
841-
names = self.inputs.trait_names(**dict(max_ver=lambda t: t is not None))
844+
names = trait_object.trait_names(**dict(max_ver=lambda t: t is not None))
842845
for name in names:
843-
if not isdefined(getattr(self.inputs, name)):
844-
continue
845-
max_ver = LooseVersion(str(self.inputs.traits()[name].max_ver))
846+
max_ver = LooseVersion(str(trait_object.traits()[name].max_ver))
846847
if max_ver < version:
847-
raise Exception('Input %s (%s) (version %s > required %s)' %
848+
unavailable_traits.append(name)
849+
if not isdefined(getattr(trait_object, name)):
850+
continue
851+
if raise_exception:
852+
raise Exception('Trait %s (%s) (version %s > required %s)' %
848853
(name, self.__class__.__name__, version, max_ver))
854+
return unavailable_traits
849855

850856
def _run_interface(self, runtime):
851857
""" Core function that executes interface
@@ -869,7 +875,7 @@ def run(self, **inputs):
869875
"""
870876
self.inputs.set(**inputs)
871877
self._check_mandatory_inputs()
872-
self._check_input_version_requirements()
878+
self._check_version_requirements(self.inputs)
873879
interface = self.__class__
874880
# initialize provenance tracking
875881
env = deepcopy(os.environ.data)
@@ -929,9 +935,18 @@ def aggregate_outputs(self, runtime=None, needed_outputs=None):
929935
predicted_outputs = self._list_outputs()
930936
outputs = self._outputs()
931937
if predicted_outputs:
938+
_unavailable_outputs = []
939+
if outputs:
940+
_unavailable_outputs = \
941+
self._check_version_requirements(self._outputs())
932942
for key, val in predicted_outputs.items():
933943
if needed_outputs and key not in needed_outputs:
934944
continue
945+
if key in _unavailable_outputs:
946+
raise KeyError(('Output trait %s not available in version '
947+
'%s of interface %s. Please inform '
948+
'developers.') % (key, self.version,
949+
self.__class__.__name__))
935950
try:
936951
setattr(outputs, key, val)
937952
_ = getattr(outputs, key)

nipype/interfaces/tests/test_base.py

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -255,80 +255,117 @@ def _run_interface(self, runtime):
255255
nib.BaseInterface.input_spec = None
256256
yield assert_raises, Exception, nib.BaseInterface
257257

258-
def test_version():
258+
def test_input_version():
259259
class InputSpec(nib.TraitedSpec):
260-
foo = nib.traits.Int(desc='a random int', min_ver=0.9)
260+
foo = nib.traits.Int(desc='a random int', min_ver='0.9')
261261
class DerivedInterface1(nib.BaseInterface):
262262
input_spec = InputSpec
263263
obj = DerivedInterface1()
264264
not_raised = True
265265
try:
266-
obj._check_input_version_requirements()
266+
obj._check_version_requirements(obj.inputs)
267267
except:
268268
not_raised = False
269269
yield assert_true, not_raised
270270
config.set('execution', 'stop_on_unknown_version', True)
271271
try:
272-
obj._check_input_version_requirements()
272+
obj._check_version_requirements(obj.inputs)
273273
except:
274274
not_raised = False
275275
yield assert_false, not_raised
276276
config.set_default_config()
277277
class InputSpec(nib.TraitedSpec):
278-
foo = nib.traits.Int(desc='a random int', min_ver=0.9)
278+
foo = nib.traits.Int(desc='a random int', min_ver='0.9')
279279
class DerivedInterface1(nib.BaseInterface):
280280
input_spec = InputSpec
281-
_version = 0.8
281+
_version = '0.8'
282282
obj = DerivedInterface1()
283283
obj.inputs.foo = 1
284-
yield assert_raises, Exception, obj._check_input_version_requirements
284+
yield assert_raises, Exception, obj._check_version_requirements
285285
class InputSpec(nib.TraitedSpec):
286-
foo = nib.traits.Int(desc='a random int', min_ver=0.9)
286+
foo = nib.traits.Int(desc='a random int', min_ver='0.9')
287287
class DerivedInterface1(nib.BaseInterface):
288288
input_spec = InputSpec
289-
_version = 0.10
289+
_version = '0.10'
290290
obj = DerivedInterface1()
291291
not_raised = True
292292
try:
293-
obj._check_input_version_requirements()
293+
obj._check_version_requirements(obj.inputs)
294294
except:
295295
not_raised = False
296296
yield assert_true, not_raised
297297
class InputSpec(nib.TraitedSpec):
298-
foo = nib.traits.Int(desc='a random int', min_ver=0.9)
298+
foo = nib.traits.Int(desc='a random int', min_ver='0.9')
299299
class DerivedInterface1(nib.BaseInterface):
300300
input_spec = InputSpec
301-
_version = 0.9
301+
_version = '0.9'
302302
obj = DerivedInterface1()
303303
obj.inputs.foo = 1
304304
not_raised = True
305305
try:
306-
obj._check_input_version_requirements()
306+
obj._check_version_requirements(obj.inputs)
307307
except:
308308
not_raised = False
309309
yield assert_true, not_raised
310310
class InputSpec(nib.TraitedSpec):
311-
foo = nib.traits.Int(desc='a random int', max_ver=0.7)
311+
foo = nib.traits.Int(desc='a random int', max_ver='0.7')
312312
class DerivedInterface2(nib.BaseInterface):
313313
input_spec = InputSpec
314-
_version = 0.8
314+
_version = '0.8'
315315
obj = DerivedInterface2()
316316
obj.inputs.foo = 1
317-
yield assert_raises, Exception, obj._check_input_version_requirements
317+
yield assert_raises, Exception, obj._check_version_requirements
318318
class InputSpec(nib.TraitedSpec):
319-
foo = nib.traits.Int(desc='a random int', max_ver=0.9)
319+
foo = nib.traits.Int(desc='a random int', max_ver='0.9')
320320
class DerivedInterface1(nib.BaseInterface):
321321
input_spec = InputSpec
322-
_version = 0.9
322+
_version = '0.9'
323323
obj = DerivedInterface1()
324324
obj.inputs.foo = 1
325325
not_raised = True
326326
try:
327-
obj._check_input_version_requirements()
327+
obj._check_version_requirements(obj.inputs)
328328
except:
329329
not_raised = False
330330
yield assert_true, not_raised
331331

332+
def test_output_version():
333+
class InputSpec(nib.TraitedSpec):
334+
foo = nib.traits.Int(desc='a random int')
335+
class OutputSpec(nib.TraitedSpec):
336+
foo = nib.traits.Int(desc='a random int', min_ver='0.9')
337+
class DerivedInterface1(nib.BaseInterface):
338+
input_spec = InputSpec
339+
output_spec = OutputSpec
340+
_version = '0.10'
341+
obj = DerivedInterface1()
342+
yield assert_equal, obj._check_version_requirements(obj._outputs()), []
343+
344+
class InputSpec(nib.TraitedSpec):
345+
foo = nib.traits.Int(desc='a random int')
346+
class OutputSpec(nib.TraitedSpec):
347+
foo = nib.traits.Int(desc='a random int', min_ver='0.11')
348+
class DerivedInterface1(nib.BaseInterface):
349+
input_spec = InputSpec
350+
output_spec = OutputSpec
351+
_version = '0.10'
352+
obj = DerivedInterface1()
353+
yield assert_equal, obj._check_version_requirements(obj._outputs()), ['foo']
354+
class InputSpec(nib.TraitedSpec):
355+
foo = nib.traits.Int(desc='a random int')
356+
class OutputSpec(nib.TraitedSpec):
357+
foo = nib.traits.Int(desc='a random int', min_ver='0.11')
358+
class DerivedInterface1(nib.BaseInterface):
359+
input_spec = InputSpec
360+
output_spec = OutputSpec
361+
_version = '0.10'
362+
def _run_interface(self, runtime):
363+
return runtime
364+
def _list_outputs(self):
365+
return {'foo': 1}
366+
obj = DerivedInterface1()
367+
yield assert_raises, KeyError, obj.run
368+
332369
def test_Commandline():
333370
yield assert_raises, Exception, nib.CommandLine
334371
ci = nib.CommandLine(command='which')

0 commit comments

Comments
 (0)