Skip to content

Commit 8040f6d

Browse files
committed
Merge pull request #353 from bcipolli/gifti-fix2
RF: Deprecate get/set methods in favor of Python properties Test consistency of data array counter Warn on inconsistent NumberofDataArrays and DataArray elements Remove commented code from nibabel/gifti/gifti.py
2 parents 593dce2 + 9d67e3c commit 8040f6d

File tree

4 files changed

+245
-75
lines changed

4 files changed

+245
-75
lines changed

nibabel/gifti/gifti.py

Lines changed: 71 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,12 @@ def from_dict(klass, data_dict):
3838
meda.data.append(nv)
3939
return meda
4040

41+
@np.deprecate_with_doc("Use the metadata property instead.")
4142
def get_metadata(self):
43+
return self.metadata
44+
45+
@property
46+
def metadata(self):
4247
""" Returns metadata as dictionary """
4348
self.data_as_dict = {}
4449
for ele in self.data:
@@ -59,7 +64,7 @@ def to_xml(self):
5964
return res
6065

6166
def print_summary(self):
62-
print(self.get_metadata())
67+
print(self.metadata)
6368

6469

6570
class GiftiNVPairs(object):
@@ -128,10 +133,27 @@ def __init__(self, key=0, label='', red=None, green=None, blue=None,
128133
self.blue = blue
129134
self.alpha = alpha
130135

136+
@np.deprecate_with_doc("Use the rgba property instead.")
131137
def get_rgba(self):
138+
return self.rgba
139+
140+
@property
141+
def rgba(self):
132142
""" Returns RGBA as tuple """
133143
return (self.red, self.green, self.blue, self.alpha)
134144

145+
@rgba.setter
146+
def rgba(self, rgba):
147+
""" Set RGBA via tuple
148+
149+
Parameters
150+
----------
151+
rgba : tuple (red, green, blue, alpha)
152+
153+
"""
154+
if len(rgba) != 4:
155+
raise ValueError('rgba must be length 4.')
156+
self.red, self.green, self.blue, self.alpha = rgba
135157

136158
def _arr2txt(arr, elem_fmt):
137159
arr = np.asarray(arr)
@@ -338,69 +360,67 @@ def print_summary(self):
338360
print('Coordinate System:')
339361
print(self.coordsys.print_summary())
340362

363+
@np.deprecate_with_doc("Use the metadata property instead.")
341364
def get_metadata(self):
365+
return self.meta.metadata
366+
367+
@property
368+
def metadata(self):
342369
""" Returns metadata as dictionary """
343-
return self.meta.get_metadata()
370+
return self.meta.metadata
344371

345372

346373
class GiftiImage(object):
347-
348-
numDA = int
349-
version = str
350-
filename = str
351-
352374
def __init__(self, meta=None, labeltable=None, darrays=None,
353375
version="1.0"):
354376
if darrays is None:
355377
darrays = []
356-
self.darrays = darrays
357378
if meta is None:
358-
self.meta = GiftiMetaData()
359-
else:
360-
self.meta = meta
379+
meta = GiftiMetaData()
361380
if labeltable is None:
362-
self.labeltable = GiftiLabelTable()
363-
else:
364-
self.labeltable = labeltable
365-
self.numDA = len(self.darrays)
381+
labeltable = GiftiLabelTable()
382+
383+
self._labeltable = labeltable
384+
self._meta = meta
385+
386+
self.darrays = darrays
366387
self.version = version
367388

368-
# @classmethod
369-
# def from_array(cls):
370-
# pass
371-
#def GiftiImage_fromarray(data, intent = GiftiIntentCode.NIFTI_INTENT_NONE, encoding=GiftiEncoding.GIFTI_ENCODING_B64GZ, endian = GiftiEndian.GIFTI_ENDIAN_LITTLE):
372-
# """ Returns a GiftiImage from a Numpy array with a given intent code and
373-
# encoding """
374-
375-
# @classmethod
376-
# def from_vertices_and_triangles(cls):
377-
# pass
378-
# def from_vertices_and_triangles(cls, vertices, triangles, coordsys = None, \
379-
# encoding = GiftiEncoding.GIFTI_ENCODING_B64GZ,\
380-
# endian = GiftiEndian.GIFTI_ENDIAN_LITTLE):
381-
# """ Returns a GiftiImage from two numpy arrays representing the vertices
382-
# and the triangles. Additionally defining the coordinate system and encoding """
389+
@property
390+
def numDA(self):
391+
return len(self.darrays)
383392

384-
def get_labeltable(self):
385-
return self.labeltable
393+
@property
394+
def labeltable(self):
395+
return self._labeltable
386396

387-
def set_labeltable(self, labeltable):
397+
@labeltable.setter
398+
def labeltable(self, labeltable):
388399
""" Set the labeltable for this GiftiImage
389400
390401
Parameters
391402
----------
392403
labeltable : GiftiLabelTable
393404
394405
"""
395-
if isinstance(labeltable, GiftiLabelTable):
396-
self.labeltable = labeltable
397-
else:
398-
print("Not a valid GiftiLabelTable instance")
406+
if not isinstance(labeltable, GiftiLabelTable):
407+
raise ValueError("Not a valid GiftiLabelTable instance")
408+
self._labeltable = labeltable
399409

400-
def get_metadata(self):
401-
return self.meta
410+
@np.deprecate_with_doc("Use the gifti_img.labeltable property instead.")
411+
def set_labeltable(self, labeltable):
412+
self.labeltable = labeltable
402413

403-
def set_metadata(self, meta):
414+
@np.deprecate_with_doc("Use the gifti_img.labeltable property instead.")
415+
def get_labeltable(self):
416+
return self.labeltable
417+
418+
@property
419+
def meta(self):
420+
return self._meta
421+
422+
@meta.setter
423+
def meta(self, meta):
404424
""" Set the metadata for this GiftiImage
405425
406426
Parameters
@@ -411,12 +431,17 @@ def set_metadata(self, meta):
411431
-------
412432
None
413433
"""
414-
if isinstance(meta, GiftiMetaData):
415-
self.meta = meta
416-
print("New Metadata set. Be aware of changing "
417-
"coordinate transformation!")
418-
else:
419-
print("Not a valid GiftiMetaData instance")
434+
if not isinstance(meta, GiftiMetaData):
435+
raise ValueError("Not a valid GiftiMetaData instance")
436+
self._meta = meta
437+
438+
@np.deprecate_with_doc("Use the gifti_img.labeltable property instead.")
439+
def set_metadata(self, meta):
440+
self.meta = meta
441+
442+
@np.deprecate_with_doc("Use the gifti_img.labeltable property instead.")
443+
def get_meta(self):
444+
return self.meta
420445

421446
def add_gifti_data_array(self, dataarr):
422447
""" Adds a data array to the GiftiImage
@@ -427,22 +452,19 @@ def add_gifti_data_array(self, dataarr):
427452
"""
428453
if isinstance(dataarr, GiftiDataArray):
429454
self.darrays.append(dataarr)
430-
self.numDA += 1
431455
else:
432456
print("dataarr paramater must be of tzpe GiftiDataArray")
433457

434458
def remove_gifti_data_array(self, ith):
435459
""" Removes the ith data array element from the GiftiImage """
436460
self.darrays.pop(ith)
437-
self.numDA -= 1
438461

439462
def remove_gifti_data_array_by_intent(self, intent):
440463
""" Removes all the data arrays with the given intent type """
441464
intent2remove = intent_codes.code[intent]
442465
for dele in self.darrays:
443466
if dele.intent == intent2remove:
444467
self.darrays.remove(dele)
445-
self.numDA -= 1
446468

447469
def get_arrays_from_intent(self, intent):
448470
""" Returns a a list of GiftiDataArray elements matching

nibabel/gifti/parse_gifti_fast.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import base64
1212
import sys
13+
import warnings
1314
import zlib
1415
from ..externals.six import StringIO
1516
from xml.parsers.expat import ParserCreate, ExpatError
@@ -109,8 +110,7 @@ def StartElementHandler(self, name, attrs):
109110
if 'Version' in attrs:
110111
self.img.version = attrs['Version']
111112
if 'NumberOfDataArrays' in attrs:
112-
self.img.numDA = int(attrs['NumberOfDataArrays'])
113-
self.count_da = False
113+
self.expected_numDA = int(attrs['NumberOfDataArrays'])
114114

115115
self.fsm_state.append('GIFTI')
116116
elif name == 'MetaData':
@@ -207,6 +207,10 @@ def EndElementHandler(self, name):
207207
if DEBUG_PRINT:
208208
print('End element:\n\t', repr(name))
209209
if name == 'GIFTI':
210+
if hasattr(self, 'expected_numDA') and self.expected_numDA != self.img.numDA:
211+
warnings.warn("Actual # of data arrays does not match "
212+
"# expected: %d != %d." % (self.expected_numDA,
213+
self.img.numDA))
210214
# remove last element of the list
211215
self.fsm_state.pop()
212216
# assert len(self.fsm_state) == 0
@@ -234,8 +238,6 @@ def EndElementHandler(self, name):
234238
self.img.labeltable = self.lata
235239
self.lata = None
236240
elif name == 'DataArray':
237-
if self.count_da:
238-
self.img.numDA += 1
239241
self.fsm_state.pop()
240242
elif name == 'CoordinateSystemTransformMatrix':
241243
self.fsm_state.pop()

nibabel/gifti/tests/test_gifti.py

Lines changed: 95 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
""" Testing gifti objects
22
"""
3+
import warnings
34

45
import numpy as np
56

7+
from ..gifti import (GiftiImage, GiftiDataArray, GiftiLabel, GiftiLabelTable,
8+
GiftiMetaData)
69
from ...nifti1 import data_type_codes, intent_codes
710

8-
from ..gifti import GiftiImage, GiftiDataArray
9-
1011
from numpy.testing import (assert_array_almost_equal,
1112
assert_array_equal)
12-
13-
from nose.tools import assert_true, assert_equal, assert_raises
13+
from nose.tools import (assert_true, assert_false, assert_equal, assert_raises)
14+
from ...testing import clear_and_catch_warnings
1415

1516

1617
def test_gifti_image():
@@ -24,6 +25,35 @@ def test_gifti_image():
2425
gi = GiftiImage()
2526
assert_equal(gi.darrays, [])
2627

28+
# Test darrays / numDA
29+
gi = GiftiImage()
30+
assert_equal(gi.numDA, 0)
31+
32+
da = GiftiDataArray(data='data')
33+
gi.add_gifti_data_array(da)
34+
assert_equal(gi.numDA, 1)
35+
assert_equal(gi.darrays[0].data, 'data')
36+
37+
gi.remove_gifti_data_array(0)
38+
assert_equal(gi.numDA, 0)
39+
40+
# Remove from empty
41+
gi = GiftiImage()
42+
gi.remove_gifti_data_array_by_intent(0)
43+
assert_equal(gi.numDA, 0)
44+
45+
# Remove one
46+
gi = GiftiImage()
47+
da = GiftiDataArray(data='data')
48+
gi.add_gifti_data_array(da)
49+
50+
gi.remove_gifti_data_array_by_intent(0)
51+
assert_equal(gi.numDA, 1)
52+
53+
gi.darrays[0].intent = 0
54+
gi.remove_gifti_data_array_by_intent(0)
55+
assert_equal(gi.numDA, 0)
56+
2757

2858
def test_dataarray():
2959
for dt_code in data_type_codes.value_set():
@@ -36,3 +66,64 @@ def test_dataarray():
3666
bs_arr = arr.byteswap().newbyteorder()
3767
da = GiftiDataArray.from_array(bs_arr, 'triangle')
3868
assert_equal(da.datatype, data_type_codes[arr.dtype])
69+
70+
71+
def test_labeltable():
72+
img = GiftiImage()
73+
assert_equal(len(img.labeltable.labels), 0)
74+
75+
new_table = GiftiLabelTable()
76+
new_table.labels += ['test', 'me']
77+
img.labeltable = new_table
78+
assert_equal(len(img.labeltable.labels), 2)
79+
80+
# Try to set to non-table
81+
def assign_labeltable(val):
82+
img.labeltable = val
83+
assert_raises(ValueError, assign_labeltable, 'not-a-table')
84+
85+
86+
def test_metadata():
87+
# Test deprecation
88+
with clear_and_catch_warnings() as w:
89+
warnings.filterwarnings('once', category=DeprecationWarning)
90+
assert_equal(len(GiftiDataArray().get_metadata()), 0)
91+
92+
# Test deprecation
93+
with clear_and_catch_warnings() as w:
94+
warnings.filterwarnings('once', category=DeprecationWarning)
95+
assert_equal(len(GiftiMetaData().get_metadata()), 0)
96+
97+
98+
def test_gifti_label_rgba():
99+
rgba = np.random.rand(4)
100+
kwargs = dict(zip(['red', 'green', 'blue', 'alpha'], rgba))
101+
102+
gl1 = GiftiLabel(**kwargs)
103+
assert_array_equal(rgba, gl1.rgba)
104+
105+
gl1.red = 2 * gl1.red
106+
assert_false(np.allclose(rgba, gl1.rgba)) # don't just store the list!
107+
108+
gl2 = GiftiLabel()
109+
gl2.rgba = rgba
110+
assert_array_equal(rgba, gl2.rgba)
111+
112+
gl2.blue = 2 * gl2.blue
113+
assert_false(np.allclose(rgba, gl2.rgba)) # don't just store the list!
114+
115+
def assign_rgba(gl, val):
116+
gl.rgba = val
117+
gl3 = GiftiLabel(**kwargs)
118+
assert_raises(ValueError, assign_rgba, gl3, rgba[:2])
119+
assert_raises(ValueError, assign_rgba, gl3, rgba.tolist() + rgba.tolist())
120+
121+
# Test deprecation
122+
with clear_and_catch_warnings() as w:
123+
warnings.filterwarnings('once', category=DeprecationWarning)
124+
assert_equal(kwargs['red'], gl3.get_rgba()[0])
125+
126+
# Test default value
127+
gl4 = GiftiLabel()
128+
assert_equal(len(gl4.rgba), 4)
129+
assert_true(np.all([elem is None for elem in gl4.rgba]))

0 commit comments

Comments
 (0)