From 89902124e17957744073cee0d0ed64e11f6a11a4 Mon Sep 17 00:00:00 2001 From: Brendan Moloney Date: Wed, 2 Mar 2016 20:37:30 -0800 Subject: [PATCH 1/9] ENH: Add "element" containers and make dicom wrappers compatible Add two main types: 'ElemDict' and 'ElemList'. Each can only store objects with an attribute 'value'. Standard lookups with '__getitem__' and 'get' return just this 'value' attribute. To access the underlying object, a 'get_elem' method is provided. Also updated the DICOM wrappers to provide a 'get_elem' method that returns the full DICOM element rather than just the value. Added a __iter__ method that returns the available DICOM keys. --- nibabel/elemcont.py | 188 ++++++++++++++++++++++ nibabel/nicom/dicomwrappers.py | 11 ++ nibabel/nicom/tests/test_dicomwrappers.py | 12 ++ nibabel/tests/test_elemcont.py | 90 +++++++++++ 4 files changed, 301 insertions(+) create mode 100644 nibabel/elemcont.py create mode 100644 nibabel/tests/test_elemcont.py diff --git a/nibabel/elemcont.py b/nibabel/elemcont.py new file mode 100644 index 0000000000..e3176e3859 --- /dev/null +++ b/nibabel/elemcont.py @@ -0,0 +1,188 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: +'''Containers for storing "elements" which have both a core data value as well +as some additional meta data. When indexing into these containers it is this +core value that is returned, which allows for much cleaner and more readable +access to nested structures. + +Each object stored in these containers must have an attribute `value` which +provides the core data value for the element. To get the element object itself +the `get_elem` method must be used. +''' +from collections import MutableMapping, MutableSequence + +from .externals import OrderedDict +from .externals.six import iteritems + + +class Elem(object): + '''Basic element type has a `value` and a `meta` attribute.''' + def __init__(self, value, meta=None): + self.value = value + self.meta = {} if meta is None else meta + + +class InvalidElemError(Exception): + '''Raised when trying to add an object without a `value` attribute to an + `ElemDict`.''' + def __init__(self, invalid_val): + self.invalid_val = invalid_val + message = ("Provided value '%s' of type %s does not have a 'value' " + "attribute" % (self.invalid_val, type(invalid_val))) + super(InvalidElemError, self).__init__(message) + + +class ElemDict(MutableMapping): + '''Ordered dict-like where each value is an "element", which is defined as + any object which has a `value` attribute. + + When looking up an item in the dict, it is this `value` attribute that + is returned. To get the element itself use the `get_elem` method. + ''' + + def __init__(self, *args, **kwargs): + if len(args) > 1: + raise TypeError("At most one arg expected, got %d" % len(args)) + self._elems = OrderedDict() + if len(args) == 1: + arg = args[0] + if hasattr(arg, 'get_elem'): + it = ((k, arg.get_elem(k)) for k in arg) + elif hasattr(arg, 'items'): + it = iteritems(arg) + else: + it = arg + for key, val in it: + self[key] = val + for key, val in iteritems(kwargs): + self[key] = val + + def __getitem__(self, key): + return self._elems[key].value + + def __setitem__(self, key, val): + if not hasattr(val, 'value'): + raise InvalidElemError(val) + self._elems[key] = val + + def __delitem__(self, key): + del self._elems[key] + + def __iter__(self): + return iter(self._elems) + + def __len__(self): + return len(self._elems) + + def __repr__(self): + return ('ElemDict(%s)' % + ', '.join(['%r=%r' % x for x in self.items()])) + + def update(self, other): + if hasattr(other, 'get_elem'): + for key in other: + self[key] = other.get_elem(key) + else: + for key, elem in iteritems(other): + self[key] = elem + + def get_elem(self, key): + return self._elems[key] + + +class ElemList(MutableSequence): + '''A list-like container where each value is an "element", which is + defined as any object which has a `value` attribute. + + When looking up an item in the list, it is this `value` attribute that + is returned. To get the element itself use the `get_elem` method. + ''' + def __init__(self, data=None): + self._elems = list() + if data is not None: + if isinstance(data, self.__class__): + for idx in range(len(data)): + self.append(data.get_elem(idx)) + else: + for elem in data: + self.append(elem) + + def _tuple_from_slice(self, slc): + '''Get (start, end, step) tuple from slice object. + ''' + (start, end, step) = slc.indices(len(self)) + # Replace (0, -1, 1) with (0, 0, 1) (misfeature in .indices()). + if step == 1: + if end < start: + end = start + step = None + if slc.step == None: + step = None + return (start, end, step) + + def __getitem__(self, idx): + if isinstance(idx, slice): + return ElemList(self._elems[idx]) + else: + return self._elems[idx].value + + def __setitem__(self, idx, val): + if isinstance(idx, slice): + (start, end, step) = self._tuple_from_slice(idx) + if step != None: + # Extended slice + indices = range(start, end, step) + if len(val) != len(indices): + raise ValueError(('attempt to assign sequence of size %d' + + ' to extended slice of size %d') % + (len(value), len(indices))) + for j, assign_val in enumerate(val): + self.insert(indices[j], assign_val) + else: + # Normal slice + for j, assign_val in enumerate(val): + self.insert(start + j, assign_val) + else: + self.insert(idx, val) + + def __delitem__(self, idx): + del self._elems[idx] + + def __len__(self): + return len(self._elems) + + def __repr__(self): + return ('ElemList([%s])' % ', '.join(['%r' % x for x in self])) + + def __add__(self, other): + result = self.__class__(self) + if isinstance(other, self.__class__): + for idx in range(len(other)): + result.append(other.get_elem(idx)) + else: + for e in other: + result.append(e) + return result + + def __radd__(self, other): + result = self.__class__(other) + for idx in range(len(self)): + result.append(self.get_elem(idx)) + return result + + def __iadd__(self, other): + if isinstance(other, self.__class__): + for idx in range(len(other)): + self.append(other.get_elem(idx)) + else: + for e in other: + self.append(e) + return self + + def insert(self, idx, val): + if not hasattr(val, 'value'): + raise InvalidElemError(val) + self._elems.insert(idx, val) + + def get_elem(self, idx): + return self._elems[idx] \ No newline at end of file diff --git a/nibabel/nicom/dicomwrappers.py b/nibabel/nicom/dicomwrappers.py index f37d0323a8..4370b8f7f5 100755 --- a/nibabel/nicom/dicomwrappers.py +++ b/nibabel/nicom/dicomwrappers.py @@ -293,6 +293,17 @@ def get(self, key, default=None): """ Get values from underlying dicom data """ return self.dcm_data.get(key, default) + def get_elem(self, key): + """ Get DICOM element instead of just the value """ + tag = tag_for_keyword(key) + if tag is None or tag not in self.dcm_data: + raise KeyError('"%s" not in self.dcm_data' % key) + return self.dcm_data.get(tag) + + def __iter__(self): + """ Iterate over available DICOM keywords """ + return iter(self.dcm_data.dir()) + @deprecate_with_version('get_affine method is deprecated.\n' 'Please use the ``img.affine`` property ' 'instead.', diff --git a/nibabel/nicom/tests/test_dicomwrappers.py b/nibabel/nicom/tests/test_dicomwrappers.py index 0bb875002b..fbcc0794d3 100755 --- a/nibabel/nicom/tests/test_dicomwrappers.py +++ b/nibabel/nicom/tests/test_dicomwrappers.py @@ -74,6 +74,8 @@ def test_wrappers(): assert dw.get('AcquisitionNumber') is None with pytest.raises(KeyError): dw['not an item'] + with pytest.raises(KeyError): + dw.get_elem('not an item') with pytest.raises(didw.WrapperError): dw.get_data() with pytest.raises(didw.WrapperError): @@ -93,8 +95,18 @@ def test_wrappers(): dw = maker(DATA) assert dw.get('InstanceNumber') == 2 assert dw.get('AcquisitionNumber') == 2 + elem = dw.get_elem('InstanceNumber') + assert type(elem) == pydicom.dataelem.DataElement + assert elem.value == 2 + assert elem.VR == 'IS' with pytest.raises(KeyError): dw['not an item'] + with pytest.raises(KeyError): + dw.get_elem('not an item') + # Test we get a key error when using real DICOM keyword that isn't in + # this dataset + with pytest.raises(KeyError): + dw.get_elem('ShutterShape') for maker in (didw.MosaicWrapper, didw.wrapper_from_data): dw = maker(DATA) assert dw.is_mosaic diff --git a/nibabel/tests/test_elemcont.py b/nibabel/tests/test_elemcont.py new file mode 100644 index 0000000000..8db38943f7 --- /dev/null +++ b/nibabel/tests/test_elemcont.py @@ -0,0 +1,90 @@ +""" Testing element containers +""" +from __future__ import print_function + +from ..elemcont import Elem, ElemDict, ElemList, InvalidElemError +from nose.tools import (assert_true, assert_false, assert_equal, assert_raises) + + +def test_elemdict(): + # Test ElemDict class + e = ElemDict() + assert_raises(InvalidElemError, e.__setitem__, 'some', 'thing') + assert_equal(list(e.keys()), []) + elem = Elem('thing') + e['some'] = elem + assert_equal(list(e.keys()), ['some']) + assert_equal(e['some'], 'thing') + assert_equal(e.get_elem('some'), elem) + + # Test constructor + assert_raises(InvalidElemError, ElemDict, dict(some='thing')) + e = ElemDict(dict(some=Elem('thing'))) + assert_equal(list(e.keys()), ['some']) + assert_equal(e['some'], 'thing') + e = ElemDict(some=Elem('thing')) + assert_equal(list(e.keys()), ['some']) + assert_equal(e['some'], 'thing') + e2 = ElemDict(e) + assert_equal(list(e2.keys()), ['some']) + assert_equal(e2['some'], 'thing') + + +def test_elemdict_update(): + e1 = ElemDict(dict(some=Elem('thing'))) + e1.update(dict(hello=Elem('world'))) + assert_equal(list(e1.items()), [('some', 'thing'), ('hello', 'world')]) + e1 = ElemDict(dict(some=Elem('thing'))) + e2 = ElemDict(dict(hello=Elem('world'))) + e1.update(e2) + assert_equal(list(e1.items()), [('some', 'thing'), ('hello', 'world')]) + + +def test_elemlist(): + # Test ElemList class + el = ElemList() + assert_equal(len(el), 0) + assert_raises(InvalidElemError, el.append, 'something') + elem = Elem('something') + el.append(elem) + assert_equal(len(el), 1) + assert_equal(el[0], 'something') + assert_equal(el.get_elem(0), elem) + assert_equal([x for x in el], ['something']) + + # Test constructor + assert_raises(InvalidElemError, ElemList, ['something']) + el = ElemList([elem]) + assert_equal(len(el), 1) + assert_equal(el[0], 'something') + assert_equal(el.get_elem(0), elem) + el2 = ElemList(el) + assert_equal(len(el2), 1) + assert_equal(el2[0], 'something') + assert_equal(el2.get_elem(0), elem) + + +def test_elemlist_slicing(): + el = ElemList() + el[5:6] = [Elem('hello'), Elem('there'), Elem('world')] + assert_equal([x for x in el], ['hello', 'there', 'world']) + assert_true(isinstance(el[:2], ElemList)) + assert_equal([x for x in el[:2]], ['hello', 'there']) + + +def test_elemlist_add(): + res = ElemList([Elem('hello'), Elem('there')]) + ElemList([Elem('world')]) + assert_true(isinstance(res, ElemList)) + assert_equal([x for x in res], ['hello', 'there', 'world']) + res = ElemList([Elem('hello'), Elem('there')]) + [Elem('world')] + assert_true(isinstance(res, ElemList)) + assert_equal([x for x in res], ['hello', 'there', 'world']) + res = [Elem('hello'), Elem('there')] + ElemList([Elem('world')]) + assert_true(isinstance(res, ElemList)) + assert_equal([x for x in res], ['hello', 'there', 'world']) + res = ElemList([Elem('hello'), Elem('there')]) + res += [Elem('world')] + assert_equal([x for x in res], ['hello', 'there', 'world']) + res = ElemList([Elem('hello'), Elem('there')]) + res += ElemList([Elem('world')]) + assert_equal([x for x in res], ['hello', 'there', 'world']) \ No newline at end of file From 55768ccf857940885a26fb9fa2db82db6c1bc1a5 Mon Sep 17 00:00:00 2001 From: Brendan Moloney Date: Thu, 3 Mar 2016 14:21:43 -0800 Subject: [PATCH 2/9] DOC: Update Wrapper docstring to include new methods --- nibabel/nicom/dicomwrappers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nibabel/nicom/dicomwrappers.py b/nibabel/nicom/dicomwrappers.py index 4370b8f7f5..5cb419eb05 100755 --- a/nibabel/nicom/dicomwrappers.py +++ b/nibabel/nicom/dicomwrappers.py @@ -107,6 +107,8 @@ class Wrapper(object): * is_same_series(other) * __getitem__ : return attributes from `dcm_data` * get(key[, default]) - as usual given __getitem__ above + * get_elem(key) - Provide full DICOM element instead of just value + * __iter__ - Iterate over DICOM keys available in this data set Attributes and things that look like attributes: From 109c65ea873c1267ebd493f3c926234f636e8574 Mon Sep 17 00:00:00 2001 From: Brendan Moloney Date: Thu, 19 Mar 2020 15:46:24 -0700 Subject: [PATCH 3/9] DOC: Convert doc strings to single line header --- nibabel/elemcont.py | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/nibabel/elemcont.py b/nibabel/elemcont.py index e3176e3859..0838c012a5 100644 --- a/nibabel/elemcont.py +++ b/nibabel/elemcont.py @@ -1,9 +1,11 @@ # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- # vi: set ft=python sts=4 ts=4 sw=4 et: -'''Containers for storing "elements" which have both a core data value as well -as some additional meta data. When indexing into these containers it is this -core value that is returned, which allows for much cleaner and more readable -access to nested structures. +'''Containers that provide easy access to the values of nested elements + +These containers are for storing "elements" which have both a core data value +as well as some additional meta data. When indexing into these containers it is +this core value that is returned, which allows for much cleaner and more +readable access to nested structures. Each object stored in these containers must have an attribute `value` which provides the core data value for the element. To get the element object itself @@ -23,8 +25,8 @@ def __init__(self, value, meta=None): class InvalidElemError(Exception): - '''Raised when trying to add an object without a `value` attribute to an - `ElemDict`.''' + '''The object being added to the container doesn't have a `value` attribute + ''' def __init__(self, invalid_val): self.invalid_val = invalid_val message = ("Provided value '%s' of type %s does not have a 'value' " @@ -33,11 +35,11 @@ def __init__(self, invalid_val): class ElemDict(MutableMapping): - '''Ordered dict-like where each value is an "element", which is defined as - any object which has a `value` attribute. - - When looking up an item in the dict, it is this `value` attribute that - is returned. To get the element itself use the `get_elem` method. + '''Ordered dict-like providing easy access to nested elements + + Each value added to the dict must in turn have a `value` attribute, which + is what is returned by subsequent calls to `__getitem__`. To get the + element itself use the `get_elem` method. ''' def __init__(self, *args, **kwargs): @@ -91,11 +93,11 @@ def get_elem(self, key): class ElemList(MutableSequence): - '''A list-like container where each value is an "element", which is - defined as any object which has a `value` attribute. + '''A list-like container providing easy access to nested elements - When looking up an item in the list, it is this `value` attribute that - is returned. To get the element itself use the `get_elem` method. + Each value added to the list must in turn have a `value` attribute, which + is what is returned by subsequent calls to `__getitem__`. To get the + element itself use the `get_elem` method. ''' def __init__(self, data=None): self._elems = list() @@ -185,4 +187,5 @@ def insert(self, idx, val): self._elems.insert(idx, val) def get_elem(self, idx): - return self._elems[idx] \ No newline at end of file + return self._elems[idx] + From 46be012acc6138359ae1c9595c44ea571a9a7a6c Mon Sep 17 00:00:00 2001 From: Brendan Moloney Date: Thu, 19 Mar 2020 16:01:21 -0700 Subject: [PATCH 4/9] RF: Don't store value in InvalidElemError --- nibabel/elemcont.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nibabel/elemcont.py b/nibabel/elemcont.py index 0838c012a5..f1fe4e5041 100644 --- a/nibabel/elemcont.py +++ b/nibabel/elemcont.py @@ -28,9 +28,8 @@ class InvalidElemError(Exception): '''The object being added to the container doesn't have a `value` attribute ''' def __init__(self, invalid_val): - self.invalid_val = invalid_val message = ("Provided value '%s' of type %s does not have a 'value' " - "attribute" % (self.invalid_val, type(invalid_val))) + "attribute" % (invalid_val, type(invalid_val))) super(InvalidElemError, self).__init__(message) From b1ad6ebc6aa58b1f9c146aa420c71afb4f07d107 Mon Sep 17 00:00:00 2001 From: Brendan Moloney Date: Thu, 19 Mar 2020 16:04:30 -0700 Subject: [PATCH 5/9] RF: rename class to --- nibabel/elemcont.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nibabel/elemcont.py b/nibabel/elemcont.py index f1fe4e5041..7d0c24aac6 100644 --- a/nibabel/elemcont.py +++ b/nibabel/elemcont.py @@ -17,7 +17,7 @@ from .externals.six import iteritems -class Elem(object): +class MetaElem(object): '''Basic element type has a `value` and a `meta` attribute.''' def __init__(self, value, meta=None): self.value = value From be4129f305aa822acb2ee3c4f0722a5b4d164cb1 Mon Sep 17 00:00:00 2001 From: Brendan Moloney Date: Thu, 19 Mar 2020 16:32:16 -0700 Subject: [PATCH 6/9] RF: Minor code cleanup --- nibabel/elemcont.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/nibabel/elemcont.py b/nibabel/elemcont.py index 7d0c24aac6..7cf9409355 100644 --- a/nibabel/elemcont.py +++ b/nibabel/elemcont.py @@ -100,13 +100,13 @@ class ElemList(MutableSequence): ''' def __init__(self, data=None): self._elems = list() - if data is not None: - if isinstance(data, self.__class__): - for idx in range(len(data)): - self.append(data.get_elem(idx)) - else: - for elem in data: - self.append(elem) + if data is None: return + if isinstance(data, self.__class__): + for idx in range(len(data)): + self.append(data.get_elem(idx)) + else: + for elem in data: + self.append(elem) def _tuple_from_slice(self, slc): '''Get (start, end, step) tuple from slice object. @@ -117,7 +117,7 @@ def _tuple_from_slice(self, slc): if end < start: end = start step = None - if slc.step == None: + if slc.step is None: step = None return (start, end, step) @@ -130,19 +130,19 @@ def __getitem__(self, idx): def __setitem__(self, idx, val): if isinstance(idx, slice): (start, end, step) = self._tuple_from_slice(idx) - if step != None: - # Extended slice - indices = range(start, end, step) - if len(val) != len(indices): - raise ValueError(('attempt to assign sequence of size %d' + - ' to extended slice of size %d') % - (len(value), len(indices))) - for j, assign_val in enumerate(val): - self.insert(indices[j], assign_val) - else: + if step is None: # Normal slice for j, assign_val in enumerate(val): self.insert(start + j, assign_val) + return + # Extended slice + indices = range(start, end, step) + if len(val) != len(indices): + raise ValueError(('attempt to assign sequence of size %d' + + ' to extended slice of size %d') % + (len(value), len(indices))) + for j, assign_val in enumerate(val): + self.insert(indices[j], assign_val) else: self.insert(idx, val) From 86ce9bf4a8bbaa456180e71574d576d295693c94 Mon Sep 17 00:00:00 2001 From: Brendan Moloney Date: Mon, 23 Mar 2020 12:04:55 -0700 Subject: [PATCH 7/9] CLN: Cleanup whitespace and formatting --- nibabel/elemcont.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/nibabel/elemcont.py b/nibabel/elemcont.py index 7cf9409355..22451a172f 100644 --- a/nibabel/elemcont.py +++ b/nibabel/elemcont.py @@ -2,9 +2,9 @@ # vi: set ft=python sts=4 ts=4 sw=4 et: '''Containers that provide easy access to the values of nested elements -These containers are for storing "elements" which have both a core data value -as well as some additional meta data. When indexing into these containers it is -this core value that is returned, which allows for much cleaner and more +These containers are for storing "elements" which have both a core data value +as well as some additional meta data. When indexing into these containers it is +this core value that is returned, which allows for much cleaner and more readable access to nested structures. Each object stored in these containers must have an attribute `value` which @@ -26,7 +26,7 @@ def __init__(self, value, meta=None): class InvalidElemError(Exception): '''The object being added to the container doesn't have a `value` attribute - ''' + ''' def __init__(self, invalid_val): message = ("Provided value '%s' of type %s does not have a 'value' " "attribute" % (invalid_val, type(invalid_val))) @@ -35,10 +35,10 @@ def __init__(self, invalid_val): class ElemDict(MutableMapping): '''Ordered dict-like providing easy access to nested elements - - Each value added to the dict must in turn have a `value` attribute, which - is what is returned by subsequent calls to `__getitem__`. To get the - element itself use the `get_elem` method. + + Each value added to the dict must in turn have a `value` attribute, which + is what is returned by subsequent calls to `__getitem__`. To get the + element itself use the `get_elem` method. ''' def __init__(self, *args, **kwargs): @@ -94,13 +94,14 @@ def get_elem(self, key): class ElemList(MutableSequence): '''A list-like container providing easy access to nested elements - Each value added to the list must in turn have a `value` attribute, which - is what is returned by subsequent calls to `__getitem__`. To get the - element itself use the `get_elem` method. + Each value added to the list must in turn have a `value` attribute, which + is what is returned by subsequent calls to `__getitem__`. To get the + element itself use the `get_elem` method. ''' def __init__(self, data=None): self._elems = list() - if data is None: return + if data is None: + return if isinstance(data, self.__class__): for idx in range(len(data)): self.append(data.get_elem(idx)) @@ -114,11 +115,11 @@ def _tuple_from_slice(self, slc): (start, end, step) = slc.indices(len(self)) # Replace (0, -1, 1) with (0, 0, 1) (misfeature in .indices()). if step == 1: - if end < start: - end = start - step = None + if end < start: + end = start + step = None if slc.step is None: - step = None + step = None return (start, end, step) def __getitem__(self, idx): @@ -187,4 +188,3 @@ def insert(self, idx, val): def get_elem(self, idx): return self._elems[idx] - From 5d86f6fe01358e9d646caf9a3c07be4ad4e95f07 Mon Sep 17 00:00:00 2001 From: Brendan Moloney Date: Mon, 23 Mar 2020 15:15:43 -0700 Subject: [PATCH 8/9] RF: Don't use six.iteritems --- nibabel/elemcont.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/nibabel/elemcont.py b/nibabel/elemcont.py index 22451a172f..6a3840d411 100644 --- a/nibabel/elemcont.py +++ b/nibabel/elemcont.py @@ -14,7 +14,6 @@ from collections import MutableMapping, MutableSequence from .externals import OrderedDict -from .externals.six import iteritems class MetaElem(object): @@ -50,12 +49,12 @@ def __init__(self, *args, **kwargs): if hasattr(arg, 'get_elem'): it = ((k, arg.get_elem(k)) for k in arg) elif hasattr(arg, 'items'): - it = iteritems(arg) + it = arg.items() else: it = arg for key, val in it: self[key] = val - for key, val in iteritems(kwargs): + for key, val in kwargs.items(): self[key] = val def __getitem__(self, key): @@ -84,7 +83,7 @@ def update(self, other): for key in other: self[key] = other.get_elem(key) else: - for key, elem in iteritems(other): + for key, elem in other.items(): self[key] = elem def get_elem(self, key): From bd21464ab38a0dbb3556de6bff11c3870dd9e84c Mon Sep 17 00:00:00 2001 From: Brendan Moloney Date: Mon, 23 Mar 2020 15:16:10 -0700 Subject: [PATCH 9/9] TST: Update tests to use pytest, fix imported name --- nibabel/tests/test_elemcont.py | 117 +++++++++++++++++---------------- 1 file changed, 61 insertions(+), 56 deletions(-) diff --git a/nibabel/tests/test_elemcont.py b/nibabel/tests/test_elemcont.py index 8db38943f7..02429ca0ed 100644 --- a/nibabel/tests/test_elemcont.py +++ b/nibabel/tests/test_elemcont.py @@ -2,89 +2,94 @@ """ from __future__ import print_function -from ..elemcont import Elem, ElemDict, ElemList, InvalidElemError -from nose.tools import (assert_true, assert_false, assert_equal, assert_raises) +from ..elemcont import MetaElem, ElemDict, ElemList, InvalidElemError + +import pytest def test_elemdict(): # Test ElemDict class e = ElemDict() - assert_raises(InvalidElemError, e.__setitem__, 'some', 'thing') - assert_equal(list(e.keys()), []) - elem = Elem('thing') + with pytest.raises(InvalidElemError): + e['some'] = 'thing' + assert list(e.keys()) == [] + elem = MetaElem('thing') e['some'] = elem - assert_equal(list(e.keys()), ['some']) - assert_equal(e['some'], 'thing') - assert_equal(e.get_elem('some'), elem) + assert list(e.keys()) == ['some'] + assert e['some'] == 'thing' + assert e.get_elem('some') == elem # Test constructor - assert_raises(InvalidElemError, ElemDict, dict(some='thing')) - e = ElemDict(dict(some=Elem('thing'))) - assert_equal(list(e.keys()), ['some']) - assert_equal(e['some'], 'thing') - e = ElemDict(some=Elem('thing')) - assert_equal(list(e.keys()), ['some']) - assert_equal(e['some'], 'thing') + with pytest.raises(InvalidElemError): + ElemDict(dict(some='thing')) + e = ElemDict(dict(some=MetaElem('thing'))) + assert list(e.keys()) == ['some'] + assert e['some'] == 'thing' + e = ElemDict(some=MetaElem('thing')) + assert list(e.keys()) == ['some'] + assert e['some'] == 'thing' e2 = ElemDict(e) - assert_equal(list(e2.keys()), ['some']) - assert_equal(e2['some'], 'thing') + assert list(e2.keys()) == ['some'] + assert e2['some'] == 'thing' def test_elemdict_update(): - e1 = ElemDict(dict(some=Elem('thing'))) - e1.update(dict(hello=Elem('world'))) - assert_equal(list(e1.items()), [('some', 'thing'), ('hello', 'world')]) - e1 = ElemDict(dict(some=Elem('thing'))) - e2 = ElemDict(dict(hello=Elem('world'))) + e1 = ElemDict(dict(some=MetaElem('thing'))) + e1.update(dict(hello=MetaElem('world'))) + assert list(e1.items()) == [('some', 'thing'), ('hello', 'world')] + e1 = ElemDict(dict(some=MetaElem('thing'))) + e2 = ElemDict(dict(hello=MetaElem('world'))) e1.update(e2) - assert_equal(list(e1.items()), [('some', 'thing'), ('hello', 'world')]) + assert list(e1.items()) == [('some', 'thing'), ('hello', 'world')] def test_elemlist(): # Test ElemList class el = ElemList() - assert_equal(len(el), 0) - assert_raises(InvalidElemError, el.append, 'something') - elem = Elem('something') + assert len(el) == 0 + with pytest.raises(InvalidElemError): + el.append('something') + elem = MetaElem('something') el.append(elem) - assert_equal(len(el), 1) - assert_equal(el[0], 'something') - assert_equal(el.get_elem(0), elem) - assert_equal([x for x in el], ['something']) + assert len(el) == 1 + assert el[0] == 'something' + assert el.get_elem(0) == elem + assert [x for x in el] == ['something'] # Test constructor - assert_raises(InvalidElemError, ElemList, ['something']) + with pytest.raises(InvalidElemError): + ElemList(['something']) el = ElemList([elem]) - assert_equal(len(el), 1) - assert_equal(el[0], 'something') - assert_equal(el.get_elem(0), elem) + assert len(el) == 1 + assert el[0] == 'something' + assert el.get_elem(0) == elem el2 = ElemList(el) - assert_equal(len(el2), 1) - assert_equal(el2[0], 'something') - assert_equal(el2.get_elem(0), elem) + assert len(el2) == 1 + assert el2[0] == 'something' + assert el2.get_elem(0) == elem def test_elemlist_slicing(): el = ElemList() - el[5:6] = [Elem('hello'), Elem('there'), Elem('world')] - assert_equal([x for x in el], ['hello', 'there', 'world']) - assert_true(isinstance(el[:2], ElemList)) - assert_equal([x for x in el[:2]], ['hello', 'there']) + el[5:6] = [MetaElem('hello'), MetaElem('there'), MetaElem('world')] + assert [x for x in el] == ['hello', 'there', 'world'] + assert isinstance(el[:2], ElemList) + assert [x for x in el[:2]] == ['hello', 'there'] def test_elemlist_add(): - res = ElemList([Elem('hello'), Elem('there')]) + ElemList([Elem('world')]) - assert_true(isinstance(res, ElemList)) - assert_equal([x for x in res], ['hello', 'there', 'world']) - res = ElemList([Elem('hello'), Elem('there')]) + [Elem('world')] - assert_true(isinstance(res, ElemList)) - assert_equal([x for x in res], ['hello', 'there', 'world']) - res = [Elem('hello'), Elem('there')] + ElemList([Elem('world')]) - assert_true(isinstance(res, ElemList)) - assert_equal([x for x in res], ['hello', 'there', 'world']) - res = ElemList([Elem('hello'), Elem('there')]) - res += [Elem('world')] - assert_equal([x for x in res], ['hello', 'there', 'world']) - res = ElemList([Elem('hello'), Elem('there')]) - res += ElemList([Elem('world')]) - assert_equal([x for x in res], ['hello', 'there', 'world']) \ No newline at end of file + res = ElemList([MetaElem('hello'), MetaElem('there')]) + ElemList([MetaElem('world')]) + assert isinstance(res, ElemList) + assert [x for x in res] == ['hello', 'there', 'world'] + res = ElemList([MetaElem('hello'), MetaElem('there')]) + [MetaElem('world')] + assert isinstance(res, ElemList) + assert [x for x in res] == ['hello', 'there', 'world'] + res = [MetaElem('hello'), MetaElem('there')] + ElemList([MetaElem('world')]) + assert isinstance(res, ElemList) + assert [x for x in res] == ['hello', 'there', 'world'] + res = ElemList([MetaElem('hello'), MetaElem('there')]) + res += [MetaElem('world')] + assert [x for x in res] == ['hello', 'there', 'world'] + res = ElemList([MetaElem('hello'), MetaElem('there')]) + res += ElemList([MetaElem('world')]) + assert [x for x in res] == ['hello', 'there', 'world']