Skip to content

Commit dbf640d

Browse files
committed
enh: added deprecation related metadata to traitedspec
1 parent 47cdfdb commit dbf640d

File tree

4 files changed

+86
-2
lines changed

4 files changed

+86
-2
lines changed

CHANGES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Next release
66
* ENH: New examples: how to use ANTS template building workflows (smri_ants_build_tmeplate),
77
how to set SGE specific options (smri_ants_build_template_new)
88
* ENH: added no_flatten option to Merge
9+
* ENH: added deprecation metadata to traits
910

1011
Release 0.6.0 (Jun 30, 2012)
1112
============================

doc/devel/interface_specs.rst

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,35 @@ Common
262262
can be set to either `True` or `False`. `False` indicates that contents
263263
should be symlinked, while `True` indicates that the contents should be
264264
copied over.
265-
265+
266+
``deprecated``
267+
This is metadata for removing or renaming an input field from a spec.::
268+
269+
class RealignInputSpec(BaseInterfaceInputSpec):
270+
jobtype = traits.Enum('estwrite', 'estimate', 'write',
271+
deprecated='0.8',
272+
desc='one of: estimate, write, estwrite',
273+
usedefault=True)
274+
275+
In the above example this means that the `jobtype` input is deprecated and
276+
will be removed in version 0.8.
277+
278+
``new_name``
279+
For inputs that are being renamed, one can specify the new name of the field
280+
281+
class RealignInputSpec(BaseInterfaceInputSpec):
282+
jobtype = traits.Enum('estwrite', 'estimate', 'write',
283+
deprecated='0.8', new_name='job_type',
284+
desc='one of: estimate, write, estwrite',
285+
usedefault=True)
286+
job_type = traits.Enum('estwrite', 'estimate', 'write',
287+
desc='one of: estimate, write, estwrite',
288+
usedefault=True)
289+
290+
In the above example, the `jobtype` field is being renamed to `job_type`.
291+
When `new_name` is provided it must exist as a trait, otherwise an exception
292+
will be raised.
293+
266294
CommandLine
267295
^^^^^^^^^^^
268296

nipype/interfaces/base.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
hash_timestamp)
3030
from ..utils.misc import is_container, trim
3131
from .. import config, logging
32+
from .. import __version__ as nipype_version
3233

3334
iflogger = logging.getLogger('interface')
3435

@@ -326,6 +327,10 @@ def _generate_handlers(self):
326327
requires = self.trait_names(**has_requires)
327328
for elem in requires:
328329
self.on_trait_change(self._requires_warn, elem)
330+
has_deprecation = dict(deprecated=lambda t: t is not None)
331+
deprecated = self.trait_names(**has_deprecation)
332+
for elem in deprecated:
333+
self.on_trait_change(self._deprecated_warn, elem)
329334

330335
def _xor_warn(self, obj, name, old, new):
331336
""" Generates warnings for xor traits
@@ -347,7 +352,7 @@ def _xor_warn(self, obj, name, old, new):
347352
def _requires_warn(self, obj, name, old, new):
348353
"""Part of the xor behavior
349354
"""
350-
if new:
355+
if isdefined(new):
351356
trait_spec = self.traits()[name]
352357
msg = None
353358
for trait_name in trait_spec.requires:
@@ -358,6 +363,30 @@ def _requires_warn(self, obj, name, old, new):
358363
if msg:
359364
warn(msg)
360365

366+
def _deprecated_warn(self, obj, name, old, new):
367+
"""Checks if a user assigns a value to a deprecated trait
368+
"""
369+
if isdefined(new):
370+
trait_spec = self.traits()[name]
371+
msg1 = ('Input %s in interface %s is deprecated.') % (name,
372+
self.__class__.__name__.split('InputSpec')[0])
373+
msg2 = ('Will be removed or raise an error as of release %s') % \
374+
trait_spec.deprecated
375+
self.trait_set(trait_change_notify=False, **{'%s' % name: Undefined})
376+
if trait_spec.new_name:
377+
if trait_spec.new_name not in self.copyable_trait_names():
378+
raise TraitError(msg1 + ' Replacement trait %s not found' %
379+
trait_spec.new_name)
380+
msg3 = 'It has been replaced by %s.' % trait_spec.new_name
381+
else:
382+
msg3 = ''
383+
msg = ' '.join((msg1, msg2, msg3))
384+
if str(trait_spec.deprecated) < nipype_version:
385+
raise TraitError(msg)
386+
else:
387+
warn(msg)
388+
389+
361390
def _hash_infile(self, adict, key):
362391
""" Inject file hashes into adict[key]"""
363392
stuff = adict[key]

nipype/interfaces/tests/test_base.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,32 @@ class MyInterface(nib.BaseInterface):
132132
myif.inputs.kung = 2
133133
yield assert_equal, myif.inputs.kung, 2.0
134134

135+
def test_deprecation():
136+
class DeprecationSpec1(nib.TraitedSpec):
137+
foo = nib.traits.Int(deprecated='0.1')
138+
spec_instance = DeprecationSpec1()
139+
set_foo = lambda : setattr(spec_instance, 'foo', 1)
140+
yield assert_raises, nib.TraitError, set_foo
141+
class DeprecationSpec1numeric(nib.TraitedSpec):
142+
foo = nib.traits.Int(deprecated=0.1)
143+
spec_instance = DeprecationSpec1numeric()
144+
set_foo = lambda : setattr(spec_instance, 'foo', 1)
145+
yield assert_raises, nib.TraitError, set_foo
146+
class DeprecationSpec2(nib.TraitedSpec):
147+
foo = nib.traits.Int(deprecated='100', new_name='bar')
148+
spec_instance = DeprecationSpec2()
149+
set_foo = lambda : setattr(spec_instance, 'foo', 1)
150+
yield assert_raises, nib.TraitError, set_foo
151+
class DeprecationSpec3(nib.TraitedSpec):
152+
foo = nib.traits.Int(deprecated='1000', new_name='bar')
153+
bar = nib.traits.Int()
154+
spec_instance = DeprecationSpec3()
155+
not_raised = True
156+
try:
157+
spec_instance.foo = 1
158+
except nib.TraitError:
159+
not_raised = False
160+
yield assert_true, not_raised
135161

136162
def checknose():
137163
"""check version of nose for known incompatability"""

0 commit comments

Comments
 (0)