From 7853fa91bf3776ad1e6d58558619de40386141a0 Mon Sep 17 00:00:00 2001 From: Ben Cipollini Date: Thu, 22 Oct 2015 20:47:34 -0500 Subject: [PATCH 01/17] xmlutils.py => xmlbasedimages.py --- nibabel/gifti/gifti.py | 2 +- nibabel/{xmlutils.py => xmlbasedimages.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename nibabel/{xmlutils.py => xmlbasedimages.py} (100%) diff --git a/nibabel/gifti/gifti.py b/nibabel/gifti/gifti.py index c3199064b5..d399175c36 100644 --- a/nibabel/gifti/gifti.py +++ b/nibabel/gifti/gifti.py @@ -12,7 +12,7 @@ import numpy as np -from .. import xmlutils as xml +from .. import xmlbasedimages as xml from ..filebasedimages import FileBasedImage from ..nifti1 import data_type_codes, xform_codes, intent_codes from .util import (array_index_order_codes, gifti_encoding_codes, diff --git a/nibabel/xmlutils.py b/nibabel/xmlbasedimages.py similarity index 100% rename from nibabel/xmlutils.py rename to nibabel/xmlbasedimages.py From 20d8a386968503621db3e4f90b1855a0b57db655 Mon Sep 17 00:00:00 2001 From: Ben Cipollini Date: Thu, 22 Oct 2015 21:06:27 -0500 Subject: [PATCH 02/17] Base GiftiImage on XmlBasedImage --- nibabel/gifti/gifti.py | 7 +++---- nibabel/xmlbasedimages.py | 10 ++++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/nibabel/gifti/gifti.py b/nibabel/gifti/gifti.py index d399175c36..1cb393ad23 100644 --- a/nibabel/gifti/gifti.py +++ b/nibabel/gifti/gifti.py @@ -13,7 +13,6 @@ import numpy as np from .. import xmlbasedimages as xml -from ..filebasedimages import FileBasedImage from ..nifti1 import data_type_codes, xform_codes, intent_codes from .util import (array_index_order_codes, gifti_encoding_codes, gifti_endian_codes, KIND2FMT) @@ -386,14 +385,14 @@ def metadata(self): return self.meta.metadata -class GiftiImage(FileBasedImage, xml.XmlSerializable): +class GiftiImage(xml.XmlBasedImage): valid_exts = ('.gii',) files_types = (('image', '.gii'),) def __init__(self, header=None, extra=None, file_map=None, meta=None, labeltable=None, darrays=None, version="1.0"): - FileBasedImage.__init__(self, header=header, extra=extra, - file_map=file_map) + super(GiftiImage, self).__init__(header=header, extra=extra, + file_map=file_map) if darrays is None: darrays = [] diff --git a/nibabel/xmlbasedimages.py b/nibabel/xmlbasedimages.py index fa23466006..0f7d2cc0fa 100644 --- a/nibabel/xmlbasedimages.py +++ b/nibabel/xmlbasedimages.py @@ -11,6 +11,8 @@ """ from xml.etree.ElementTree import Element, SubElement, tostring +from .filebasedimages import FileBasedHeader, FileBasedImage + class XmlSerializable(object): """ Basic interface for serializing an object to xml""" @@ -23,3 +25,11 @@ def to_xml(self, enc='utf-8'): """ Output should be an xml string with the given encoding. (default: utf-8)""" return tostring(self._to_xml_element(), enc) + + +class XmlBasedHeader(FileBasedHeader, XmlSerializable): + pass + + +class XmlBasedImage(FileBasedImage, XmlSerializable): + pass From 957f7997b333142d14a115f1ae04e5757a886183 Mon Sep 17 00:00:00 2001 From: Ben Cipollini Date: Fri, 23 Oct 2015 09:31:52 -0700 Subject: [PATCH 03/17] Use gifti's to_file_map as XmlBasedImage's default implementation. --- nibabel/gifti/gifti.py | 75 ++++++++++++++------------------------- nibabel/xmlbasedimages.py | 17 ++++++++- 2 files changed, 43 insertions(+), 49 deletions(-) diff --git a/nibabel/gifti/gifti.py b/nibabel/gifti/gifti.py index 1cb393ad23..cb4f713cea 100644 --- a/nibabel/gifti/gifti.py +++ b/nibabel/gifti/gifti.py @@ -386,6 +386,33 @@ def metadata(self): class GiftiImage(xml.XmlBasedImage): + """ + The Gifti spec suggests using the following suffixes to your + filename when saving each specific type of data: + + .gii + Generic GIFTI File + .coord.gii + Coordinates + .func.gii + Functional + .label.gii + Labels + .rgba.gii + RGB or RGBA + .shape.gii + Shape + .surf.gii + Surface + .tensor.gii + Tensors + .time.gii + Time Series + .topo.gii + Topology + + The Gifti file is stored in endian convention of the current machine. + """ valid_exts = ('.gii',) files_types = (('image', '.gii'),) @@ -550,51 +577,3 @@ def from_file_map(klass, file_map): from .parse_gifti_fast import parse_gifti_file return parse_gifti_file( fptr=file_map['image'].get_prepare_fileobj('rb')) - - def to_file_map(self, file_map=None): - """ Save the current image to the specified file_map - - Parameters - ---------- - file_map : string - - Returns - ------- - None - - Notes - ----- - We write all files with utf-8 encoding, and specify this at the top of - the XML file with the ``encoding`` attribute. - - The Gifti spec suggests using the following suffixes to your - filename when saving each specific type of data: - - .gii - Generic GIFTI File - .coord.gii - Coordinates - .func.gii - Functional - .label.gii - Labels - .rgba.gii - RGB or RGBA - .shape.gii - Shape - .surf.gii - Surface - .tensor.gii - Tensors - .time.gii - Time Series - .topo.gii - Topology - - The Gifti file is stored in endian convention of the current machine. - """ - # Our giftis are always utf-8 encoded - see GiftiImage.to_xml - if file_map is None: - file_map = self.file_map - f = file_map['image'].get_prepare_fileobj('wb') - f.write(self.to_xml()) diff --git a/nibabel/xmlbasedimages.py b/nibabel/xmlbasedimages.py index 0f7d2cc0fa..fbe89658c1 100644 --- a/nibabel/xmlbasedimages.py +++ b/nibabel/xmlbasedimages.py @@ -32,4 +32,19 @@ class XmlBasedHeader(FileBasedHeader, XmlSerializable): class XmlBasedImage(FileBasedImage, XmlSerializable): - pass + + def to_file_map(self, file_map=None): + """ Save the current image to the specified file_map + + Parameters + ---------- + file_map : string + + Returns + ------- + None + """ + if file_map is None: + file_map = self.file_map + f = file_map['image'].get_prepare_fileobj('wb') + f.write(self.to_xml()) From abcf9254ca81089d3fb1fd18063c9c2d4fcec30f Mon Sep 17 00:00:00 2001 From: Ben Cipollini Date: Fri, 23 Oct 2015 10:16:00 -0700 Subject: [PATCH 04/17] Create a new XmlImageParser abstract class, which must be implemented and attached to each XmlBasedImage subclass. --- nibabel/xmlbasedimages.py | 70 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/nibabel/xmlbasedimages.py b/nibabel/xmlbasedimages.py index fbe89658c1..4ae4e5e97b 100644 --- a/nibabel/xmlbasedimages.py +++ b/nibabel/xmlbasedimages.py @@ -10,6 +10,7 @@ Thin layer around xml.etree.ElementTree, to abstract nibabel xml support. """ from xml.etree.ElementTree import Element, SubElement, tostring +from xml.parsers.expat import ParserCreate, ExpatError from .filebasedimages import FileBasedHeader, FileBasedImage @@ -31,7 +32,61 @@ class XmlBasedHeader(FileBasedHeader, XmlSerializable): pass +class XmlImageParser(object): + """ Parse XML image""" + + HANDLER_NAMES = ['StartElementHandler', + 'EndElementHandler', + 'CharacterDataHandler'] + + def __init__(self, encoding=None): + self.encoding = encoding + self.img = None + + def parse(self, string=None, fname=None, fptr=None): + """ + Parameters + ---------- + string : str + string containing xml document + + fname : str + file name of an xml document. + + fptr : file pointer + open file pointer to an xml document + + Returns + ------- + img : XmlBasedImage + """ + if int(fname is not None) + int(fptr is not None) + int(fname is not None) != 1: + raise ValueError('Exactly one of fptr, fname, string must be specified.') + + if string is not None: + fptr = StringIO(string) + elif fname is not None: + fptr = open(fname, 'r') + + parser = ParserCreate() # from xml package + for name in self.HANDLER_NAMES: + setattr(parser, name, getattr(self, name)) + parser.ParseFile(fptr) + + return self.img + + def StartElementHandler(self, name, attrs): + raise NotImplementedError + + def EndElementHandler(self, name): + raise NotImplementedError + + def CharacterDataHandler(self, data): + raise NotImplementedError + + class XmlBasedImage(FileBasedImage, XmlSerializable): + parser = XmlImageParser def to_file_map(self, file_map=None): """ Save the current image to the specified file_map @@ -48,3 +103,18 @@ def to_file_map(self, file_map=None): file_map = self.file_map f = file_map['image'].get_prepare_fileobj('wb') f.write(self.to_xml()) + + @classmethod + def from_file_map(klass, file_map): + """ Load a Gifti image from a file_map + + Parameters + file_map : string + + Returns + ------- + img : GiftiImage + Returns a GiftiImage + """ + return parser.parse( + fptr=file_map['image'].get_prepare_fileobj('rb')) From f13bbecffd1232020e2211eb0b1c5eb8264f3215 Mon Sep 17 00:00:00 2001 From: Ben Cipollini Date: Fri, 23 Oct 2015 10:16:55 -0700 Subject: [PATCH 05/17] Implement GiftiImageParser, stamp on GiftiImage --- nibabel/gifti/gifti.py | 19 ++------- nibabel/gifti/parse_gifti_fast.py | 65 ++----------------------------- 2 files changed, 6 insertions(+), 78 deletions(-) diff --git a/nibabel/gifti/gifti.py b/nibabel/gifti/gifti.py index cb4f713cea..35a08be398 100644 --- a/nibabel/gifti/gifti.py +++ b/nibabel/gifti/gifti.py @@ -420,6 +420,9 @@ def __init__(self, header=None, extra=None, file_map=None, meta=None, labeltable=None, darrays=None, version="1.0"): super(GiftiImage, self).__init__(header=header, extra=extra, file_map=file_map) + # placed here temporarily for git diff purposes + from .parse_gifti_fast import GiftiImageParser + GiftiImage.parser = GiftiImageParser if darrays is None: darrays = [] @@ -561,19 +564,3 @@ def to_xml(self, enc='utf-8'): return b""" """ + xml.XmlSerializable.to_xml(self, enc) - - @classmethod - def from_file_map(klass, file_map): - """ Load a Gifti image from a file_map - - Parameters - file_map : string - - Returns - ------- - img : GiftiImage - Returns a GiftiImage - """ - from .parse_gifti_fast import parse_gifti_file - return parse_gifti_file( - fptr=file_map['image'].get_prepare_fileobj('rb')) diff --git a/nibabel/gifti/parse_gifti_fast.py b/nibabel/gifti/parse_gifti_fast.py index aa3995b6b7..e4bb48278c 100644 --- a/nibabel/gifti/parse_gifti_fast.py +++ b/nibabel/gifti/parse_gifti_fast.py @@ -13,7 +13,6 @@ import warnings import zlib from ..externals.six import StringIO -from xml.parsers.expat import ParserCreate, ExpatError import numpy as np @@ -23,7 +22,7 @@ GiftiCoordSystem) from .util import (array_index_order_codes, gifti_encoding_codes, gifti_endian_codes) - +from ..xmlbasedimages import XmlImageParser DEBUG_PRINT = False @@ -71,14 +70,11 @@ def read_data_block(encoding, endian, ordering, datatype, shape, data): return newarr -class Outputter(object): +class GiftiImageParser(XmlImageParser): def __init__(self): - self.initialize() + super(GiftiImageParser, self).__init__() - def initialize(self): - """ Initialize outputter - """ # finite state machine stack self.fsm_state = [] @@ -95,7 +91,6 @@ def initialize(self): # where to write CDATA: self.write_to = None - self.img = None # Collecting char buffer fragments self._char_blocks = None @@ -316,57 +311,3 @@ def flush_chardata(self): def pending_data(self): " True if there is character data pending for processing " return not self._char_blocks is None - - -def parse_gifti_file(fname=None, fptr=None, buffer_size=None): - """ Parse gifti file named `fname`, return image - - Parameters - ---------- - fname : str - filename of gifti file - buffer_size: None or int, optional - size of read buffer. None gives default of 35000000 unless on python < - 2.6, in which case it is read only in the parser. In that case values - other than None cause a ValueError on execution - - Returns - ------- - img : gifti image - """ - assert (fname is not None) + (fptr is not None) == 1, "Specify only fname or fptr, not both" - - if fptr is None: - with open(fname, 'rb') as datasource: - return parse_gifti_file(fptr=datasource, buffer_size=buffer_size) - else: - datasource = fptr - - if buffer_size is None: - buffer_sz_val = 35000000 - else: - buffer_sz_val = buffer_size - - parser = ParserCreate() - parser.buffer_text = True - try: - parser.buffer_size = buffer_sz_val - except AttributeError: - if not buffer_size is None: - raise ValueError('Cannot set buffer size for parser') - HANDLER_NAMES = ['StartElementHandler', - 'EndElementHandler', - 'CharacterDataHandler'] - out = Outputter() - for name in HANDLER_NAMES: - setattr(parser, name, getattr(out, name)) - try: - parser.ParseFile(datasource) - except ExpatError: - print('An expat error occured while parsing the Gifti file.') - - # Reality check for pending data - assert out.pending_data is False - # update filename - out.img.filename = fname - return out.img From 3cdbe63434eaed619c3f5bd1b423b5bf09eb99f2 Mon Sep 17 00:00:00 2001 From: Ben Cipollini Date: Fri, 23 Oct 2015 10:17:20 -0700 Subject: [PATCH 06/17] Fix: GiftiImage should use get_/set_filename --- nibabel/gifti/gifti.py | 2 +- nibabel/xmlbasedimages.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/nibabel/gifti/gifti.py b/nibabel/gifti/gifti.py index 35a08be398..ee7bc63cb2 100644 --- a/nibabel/gifti/gifti.py +++ b/nibabel/gifti/gifti.py @@ -530,7 +530,7 @@ def getArraysFromIntent(self, intent): def print_summary(self): print('----start----') - print('Source filename: ', self.filename) + print('Source filename: ', self.get_filename()) print('Number of data arrays: ', self.numDA) print('Version: ', self.version) if self.meta is not None: diff --git a/nibabel/xmlbasedimages.py b/nibabel/xmlbasedimages.py index 4ae4e5e97b..bed1d6b167 100644 --- a/nibabel/xmlbasedimages.py +++ b/nibabel/xmlbasedimages.py @@ -116,5 +116,7 @@ def from_file_map(klass, file_map): img : GiftiImage Returns a GiftiImage """ - return parser.parse( + img = klass.parser().parse( fptr=file_map['image'].get_prepare_fileobj('rb')) + img.set_filename(file_map['image'].filename) + return img From 51adb8a1305472ca296be5278f90a4490ba69cde Mon Sep 17 00:00:00 2001 From: Ben Cipollini Date: Fri, 23 Oct 2015 10:36:33 -0700 Subject: [PATCH 07/17] Properly deprecate Outputter, parse_gifti_file --- nibabel/gifti/parse_gifti_fast.py | 42 ++++++++++++++++++++++++++++++- nibabel/xmlbasedimages.py | 7 ++++-- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/nibabel/gifti/parse_gifti_fast.py b/nibabel/gifti/parse_gifti_fast.py index e4bb48278c..469132b741 100644 --- a/nibabel/gifti/parse_gifti_fast.py +++ b/nibabel/gifti/parse_gifti_fast.py @@ -16,12 +16,12 @@ import numpy as np -from ..nifti1 import data_type_codes, xform_codes, intent_codes from .gifti import (GiftiMetaData, GiftiImage, GiftiLabel, GiftiLabelTable, GiftiNVPairs, GiftiDataArray, GiftiCoordSystem) from .util import (array_index_order_codes, gifti_encoding_codes, gifti_endian_codes) +from ..nifti1 import data_type_codes, xform_codes, intent_codes from ..xmlbasedimages import XmlImageParser DEBUG_PRINT = False @@ -94,6 +94,7 @@ def __init__(self): # Collecting char buffer fragments self._char_blocks = None + self.buffer_size = None def StartElementHandler(self, name, attrs): self.flush_chardata() @@ -311,3 +312,42 @@ def flush_chardata(self): def pending_data(self): " True if there is character data pending for processing " return not self._char_blocks is None + + def _create_parser(self): + parser = super(GiftiImageParser, self)._create_parser() + if self.buffer_size is not None: + parser.buffer_text = True + parser.buffer_size = self.buffer_size + return parser + + def parse(self, string=None, fname=None, fptr=None, buffer_size=None): + """ Parse gifti file named `fname`, return image + + Parameters + ---------- + fname : str + filename of gifti file + buffer_size: None or int, optional + size of read buffer. None gives default of 35000000 unless on python < + 2.6, in which case it is read only in the parser. In that case values + other than None cause a ValueError on execution + + Returns + ------- + img : gifti image + """ + self.buffer_size = buffer_size + return super(GiftiImageParser, self).parse(string=string, fname=fname, + fptr=fptr) + + +class Outputter(GiftiImageParser): + @np.deprecate_with_doc("Use GiftiImageParser instead.") + def __init__(self, *args, **kwargs): + super(Outputter, self).__init__(*args, **kwargs) + self.img = None + + +@np.deprecate_with_doc("Use GiftiImageParser.parse() instead.") +def parse_gifti_file(fname=None, fptr=None, buffer_size=None): + GiftiImageParser().parse(fname=fname, fptr=fptr, buffer_size=buffer_size) diff --git a/nibabel/xmlbasedimages.py b/nibabel/xmlbasedimages.py index bed1d6b167..ffbffaa3d1 100644 --- a/nibabel/xmlbasedimages.py +++ b/nibabel/xmlbasedimages.py @@ -10,7 +10,7 @@ Thin layer around xml.etree.ElementTree, to abstract nibabel xml support. """ from xml.etree.ElementTree import Element, SubElement, tostring -from xml.parsers.expat import ParserCreate, ExpatError +from xml.parsers.expat import ParserCreate from .filebasedimages import FileBasedHeader, FileBasedImage @@ -43,6 +43,9 @@ def __init__(self, encoding=None): self.encoding = encoding self.img = None + def _create_parser(self): + return ParserCreate() # from xml package + def parse(self, string=None, fname=None, fptr=None): """ Parameters @@ -68,7 +71,7 @@ def parse(self, string=None, fname=None, fptr=None): elif fname is not None: fptr = open(fname, 'r') - parser = ParserCreate() # from xml package + parser = self._create_parser() for name in self.HANDLER_NAMES: setattr(parser, name, getattr(self, name)) parser.ParseFile(fptr) From 853508e64246801f1479d0f7a1839674a2867d8c Mon Sep 17 00:00:00 2001 From: Ben Cipollini Date: Fri, 23 Oct 2015 18:36:35 -0700 Subject: [PATCH 08/17] BF: re-implement buffer size; makes parsing faster! --- nibabel/gifti/parse_gifti_fast.py | 33 +++-------------------------- nibabel/xmlbasedimages.py | 35 ++++++++++++++++++++++++------- 2 files changed, 31 insertions(+), 37 deletions(-) diff --git a/nibabel/gifti/parse_gifti_fast.py b/nibabel/gifti/parse_gifti_fast.py index 469132b741..d8eadbf481 100644 --- a/nibabel/gifti/parse_gifti_fast.py +++ b/nibabel/gifti/parse_gifti_fast.py @@ -72,8 +72,9 @@ def read_data_block(encoding, endian, ordering, datatype, shape, data): class GiftiImageParser(XmlImageParser): - def __init__(self): - super(GiftiImageParser, self).__init__() + def __init__(self, encoding=None, buffer_size=35000000): + super(GiftiImageParser, self).__init__(encoding=encoding, + buffer_size=buffer_size) # finite state machine stack self.fsm_state = [] @@ -94,7 +95,6 @@ def __init__(self): # Collecting char buffer fragments self._char_blocks = None - self.buffer_size = None def StartElementHandler(self, name, attrs): self.flush_chardata() @@ -313,33 +313,6 @@ def pending_data(self): " True if there is character data pending for processing " return not self._char_blocks is None - def _create_parser(self): - parser = super(GiftiImageParser, self)._create_parser() - if self.buffer_size is not None: - parser.buffer_text = True - parser.buffer_size = self.buffer_size - return parser - - def parse(self, string=None, fname=None, fptr=None, buffer_size=None): - """ Parse gifti file named `fname`, return image - - Parameters - ---------- - fname : str - filename of gifti file - buffer_size: None or int, optional - size of read buffer. None gives default of 35000000 unless on python < - 2.6, in which case it is read only in the parser. In that case values - other than None cause a ValueError on execution - - Returns - ------- - img : gifti image - """ - self.buffer_size = buffer_size - return super(GiftiImageParser, self).parse(string=string, fname=fname, - fptr=fptr) - class Outputter(GiftiImageParser): @np.deprecate_with_doc("Use GiftiImageParser instead.") diff --git a/nibabel/xmlbasedimages.py b/nibabel/xmlbasedimages.py index ffbffaa3d1..7183d144c9 100644 --- a/nibabel/xmlbasedimages.py +++ b/nibabel/xmlbasedimages.py @@ -29,24 +29,32 @@ def to_xml(self, enc='utf-8'): class XmlBasedHeader(FileBasedHeader, XmlSerializable): + """ Basic wrapper around FileBasedHeader and XmlSerializable.""" pass class XmlImageParser(object): - """ Parse XML image""" + """ Base class for defining how to parse xml-based images.""" HANDLER_NAMES = ['StartElementHandler', 'EndElementHandler', 'CharacterDataHandler'] - def __init__(self, encoding=None): + def __init__(self, encoding=None, buffer_size=35000000): self.encoding = encoding + self.buffer_size = buffer_size self.img = None def _create_parser(self): - return ParserCreate() # from xml package + """Internal function that allows subclasses to mess + with the underlying parser, if desired.""" - def parse(self, string=None, fname=None, fptr=None): + parser = ParserCreate(encoding=self.encoding) # from xml package + parser.buffer_text = True + parser.buffer_size = self.buffer_size + return parser + + def parse(self, string=None, fname=None, fptr=None, buffer_size=None): """ Parameters ---------- @@ -59,6 +67,11 @@ def parse(self, string=None, fname=None, fptr=None): fptr : file pointer open file pointer to an xml document + buffer_size: None or int, optional + size of read buffer. None gives default of 35000000 unless on python < + 2.6, in which case it is read only in the parser. In that case values + other than None cause a ValueError on execution + Returns ------- img : XmlBasedImage @@ -76,6 +89,10 @@ def parse(self, string=None, fname=None, fptr=None): setattr(parser, name, getattr(self, name)) parser.ParseFile(fptr) + if fname is not None: + fptr.close() + self.img.set_filename(fname) + return self.img def StartElementHandler(self, name, attrs): @@ -108,7 +125,7 @@ def to_file_map(self, file_map=None): f.write(self.to_xml()) @classmethod - def from_file_map(klass, file_map): + def from_file_map(klass, file_map, buffer_size=35000000): """ Load a Gifti image from a file_map Parameters @@ -119,7 +136,11 @@ def from_file_map(klass, file_map): img : GiftiImage Returns a GiftiImage """ - img = klass.parser().parse( + img = klass.parser(buffer_size=buffer_size).parse( fptr=file_map['image'].get_prepare_fileobj('rb')) - img.set_filename(file_map['image'].filename) return img + + @classmethod + def from_filename(klass, filename, buffer_size=35000000): + file_map = klass.filespec_to_file_map(filename) + return klass.from_file_map(file_map, buffer_size=buffer_size) From ceb6f72d269767fda142dfcd20ed308b447d3e5f Mon Sep 17 00:00:00 2001 From: Ben Cipollini Date: Mon, 26 Oct 2015 14:28:29 -0700 Subject: [PATCH 09/17] TST: Add parse_gifti_fast deprecation tests (and fix old deprecation tests) --- nibabel/gifti/tests/test_parse_gifti_fast.py | 21 ++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/nibabel/gifti/tests/test_parse_gifti_fast.py b/nibabel/gifti/tests/test_parse_gifti_fast.py index f2470c707b..22504048b4 100644 --- a/nibabel/gifti/tests/test_parse_gifti_fast.py +++ b/nibabel/gifti/tests/test_parse_gifti_fast.py @@ -16,6 +16,7 @@ import nibabel.gifti as gi from nibabel.gifti.util import gifti_endian_codes +from nibabel.gifti.parse_gifti_fast import Outputter, parse_gifti_file from nibabel.loadsave import load, save from nibabel.nifti1 import xform_codes from nibabel.tmpdirs import InTemporaryDirectory @@ -286,12 +287,14 @@ def test_labeltable_deprecations(): # Test deprecation with clear_and_catch_warnings() as w: - warnings.filterwarnings('once', category=DeprecationWarning) + warnings.filterwarnings('always', category=DeprecationWarning) assert_equal(lt, img.get_labeltable()) + assert_equal(len(w), 1) with clear_and_catch_warnings() as w: - warnings.filterwarnings('once', category=DeprecationWarning) + warnings.filterwarnings('always', category=DeprecationWarning) img.set_labeltable(lt) + assert_equal(len(w), 1) assert_equal(lt, img.labeltable) @@ -313,3 +316,17 @@ def test_parse_dataarrays(): load(fn) assert_equal(len(w), 1) assert_equal(img.numDA, 0) + +def test_parse_deprecated(): + + + # Test deprecation + with clear_and_catch_warnings() as w: + warnings.filterwarnings('always', category=DeprecationWarning) + op = Outputter() + assert_equal(len(w), 1) + + with clear_and_catch_warnings() as w: + warnings.filterwarnings('always', category=DeprecationWarning) + assert_raises(ValueError, parse_gifti_file) + assert_equal(len(w), 1) From 6f8d65bc73e8c42f2322dc3ef2c7dc19ceabec45 Mon Sep 17 00:00:00 2001 From: Ben Cipollini Date: Mon, 26 Oct 2015 14:38:18 -0700 Subject: [PATCH 10/17] Use verbose=1 instead of global DEBUG_PRINT flag --- nibabel/gifti/parse_gifti_fast.py | 6 ++---- nibabel/xmlbasedimages.py | 3 ++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/nibabel/gifti/parse_gifti_fast.py b/nibabel/gifti/parse_gifti_fast.py index d8eadbf481..205450ad05 100644 --- a/nibabel/gifti/parse_gifti_fast.py +++ b/nibabel/gifti/parse_gifti_fast.py @@ -24,8 +24,6 @@ from ..nifti1 import data_type_codes, xform_codes, intent_codes from ..xmlbasedimages import XmlImageParser -DEBUG_PRINT = False - def read_data_block(encoding, endian, ordering, datatype, shape, data): """ Tries to unzip, decode, parse the funny string data """ @@ -98,7 +96,7 @@ def __init__(self, encoding=None, buffer_size=35000000): def StartElementHandler(self, name, attrs): self.flush_chardata() - if DEBUG_PRINT: + if self.verbose > 0: print('Start element:\n\t', repr(name), attrs) if name == 'GIFTI': # create gifti image @@ -200,7 +198,7 @@ def StartElementHandler(self, name, attrs): def EndElementHandler(self, name): self.flush_chardata() - if DEBUG_PRINT: + if self.verbose > 0: print('End element:\n\t', repr(name)) if name == 'GIFTI': if hasattr(self, 'expected_numDA') and self.expected_numDA != self.img.numDA: diff --git a/nibabel/xmlbasedimages.py b/nibabel/xmlbasedimages.py index 7183d144c9..2079954cb9 100644 --- a/nibabel/xmlbasedimages.py +++ b/nibabel/xmlbasedimages.py @@ -40,9 +40,10 @@ class XmlImageParser(object): 'EndElementHandler', 'CharacterDataHandler'] - def __init__(self, encoding=None, buffer_size=35000000): + def __init__(self, encoding=None, buffer_size=35000000, verbose=0): self.encoding = encoding self.buffer_size = buffer_size + self.verbose = verbose self.img = None def _create_parser(self): From 2ac9a9fb70006f400c335b81356f262e25c36626 Mon Sep 17 00:00:00 2001 From: Ben Cipollini Date: Mon, 26 Oct 2015 14:38:31 -0700 Subject: [PATCH 11/17] STY: linting parse_gifti_fast --- nibabel/gifti/parse_gifti_fast.py | 75 +++++++++++++++++++------------ 1 file changed, 46 insertions(+), 29 deletions(-) diff --git a/nibabel/gifti/parse_gifti_fast.py b/nibabel/gifti/parse_gifti_fast.py index 205450ad05..075c97c724 100644 --- a/nibabel/gifti/parse_gifti_fast.py +++ b/nibabel/gifti/parse_gifti_fast.py @@ -34,8 +34,8 @@ def read_data_block(encoding, endian, ordering, datatype, shape, data): c = StringIO(data) da = np.loadtxt(c) da = da.astype(data_type_codes.type[datatype]) - # independent of the endianness - return da + return da # independent of the endianness + elif enclabel == 'B64BIN': # GIFTI_ENCODING_B64BIN dec = base64.b64decode(data.encode('ascii')) @@ -44,6 +44,7 @@ def read_data_block(encoding, endian, ordering, datatype, shape, data): newarr = np.fromstring(dec, dtype=dt) if len(newarr.shape) != len(sh): newarr = newarr.reshape(sh, order=ord) + elif enclabel == 'B64GZ': # GIFTI_ENCODING_B64GZ # convert to bytes array for python 3.2 @@ -55,24 +56,28 @@ def read_data_block(encoding, endian, ordering, datatype, shape, data): newarr = np.fromstring(zdec, dtype=dt) if len(newarr.shape) != len(sh): newarr = newarr.reshape(sh, order=ord) + elif enclabel == 'External': # GIFTI_ENCODING_EXTBIN raise NotImplementedError("In what format are the external files?") + else: return 0 + # check if we need to byteswap required_byteorder = gifti_endian_codes.byteorder[endian] if (required_byteorder in ('big', 'little') and - required_byteorder != sys.byteorder): + required_byteorder != sys.byteorder): newarr = newarr.byteswap() return newarr class GiftiImageParser(XmlImageParser): - def __init__(self, encoding=None, buffer_size=35000000): + def __init__(self, encoding=None, buffer_size=35000000, verbose=0): super(GiftiImageParser, self).__init__(encoding=encoding, - buffer_size=buffer_size) + buffer_size=buffer_size, + verbose=verbose) # finite state machine stack self.fsm_state = [] @@ -98,6 +103,7 @@ def StartElementHandler(self, name, attrs): self.flush_chardata() if self.verbose > 0: print('Start element:\n\t', repr(name), attrs) + if name == 'GIFTI': # create gifti image self.img = GiftiImage() @@ -105,33 +111,35 @@ def StartElementHandler(self, name, attrs): self.img.version = attrs['Version'] if 'NumberOfDataArrays' in attrs: self.expected_numDA = int(attrs['NumberOfDataArrays']) - self.fsm_state.append('GIFTI') + elif name == 'MetaData': self.fsm_state.append('MetaData') - # if this metadata tag is first, create self.img.meta if len(self.fsm_state) == 2: self.meta_global = GiftiMetaData() else: # otherwise, create darray.meta self.meta_da = GiftiMetaData() + elif name == 'MD': self.nvpair = GiftiNVPairs() self.fsm_state.append('MD') + elif name == 'Name': if self.nvpair is None: raise ExpatError - else: - self.write_to = 'Name' + self.write_to = 'Name' + elif name == 'Value': if self.nvpair is None: raise ExpatError - else: - self.write_to = 'Value' + self.write_to = 'Value' + elif name == 'LabelTable': self.lata = GiftiLabelTable() self.fsm_state.append('LabelTable') + elif name == 'Label': self.label = GiftiLabel() if "Index" in attrs: @@ -147,6 +155,7 @@ def StartElementHandler(self, name, attrs): if "Alpha" in attrs: self.label.alpha = float(attrs["Alpha"]) self.write_to = 'Label' + elif name == 'DataArray': self.da = GiftiDataArray() if "Intent" in attrs: @@ -174,25 +183,27 @@ def StartElementHandler(self, name, attrs): self.da.ext_offset = attrs["ExternalFileOffset"] self.img.darrays.append(self.da) self.fsm_state.append('DataArray') + elif name == 'CoordinateSystemTransformMatrix': self.coordsys = GiftiCoordSystem() self.img.darrays[-1].coordsys = self.coordsys self.fsm_state.append('CoordinateSystemTransformMatrix') + elif name == 'DataSpace': if self.coordsys is None: raise ExpatError - else: - self.write_to = 'DataSpace' + self.write_to = 'DataSpace' + elif name == 'TransformedSpace': if self.coordsys is None: raise ExpatError - else: - self.write_to = 'TransformedSpace' + self.write_to = 'TransformedSpace' + elif name == 'MatrixData': if self.coordsys is None: raise ExpatError - else: - self.write_to = 'MatrixData' + self.write_to = 'MatrixData' + elif name == 'Data': self.write_to = 'Data' @@ -200,6 +211,7 @@ def EndElementHandler(self, name): self.flush_chardata() if self.verbose > 0: print('End element:\n\t', repr(name)) + if name == 'GIFTI': if hasattr(self, 'expected_numDA') and self.expected_numDA != self.img.numDA: warnings.warn("Actual # of data arrays does not match " @@ -208,6 +220,7 @@ def EndElementHandler(self, name): # remove last element of the list self.fsm_state.pop() # assert len(self.fsm_state) == 0 + elif name == 'MetaData': self.fsm_state.pop() if len(self.fsm_state) == 1: @@ -218,6 +231,7 @@ def EndElementHandler(self, name): else: self.img.darrays[-1].meta = self.meta_da self.meta_da = None + elif name == 'MD': self.fsm_state.pop() if self.meta_global is not None and self.meta_da is None: @@ -226,28 +240,24 @@ def EndElementHandler(self, name): self.meta_da.data.append(self.nvpair) # remove reference self.nvpair = None + elif name == 'LabelTable': self.fsm_state.pop() # add labeltable self.img.labeltable = self.lata self.lata = None + elif name == 'DataArray': self.fsm_state.pop() + elif name == 'CoordinateSystemTransformMatrix': self.fsm_state.pop() self.coordsys = None - elif name == 'DataSpace': - self.write_to = None - elif name == 'TransformedSpace': - self.write_to = None - elif name == 'MatrixData': - self.write_to = None - elif name == 'Name': - self.write_to = None - elif name == 'Value': - self.write_to = None - elif name == 'Data': + + elif name in ['DataSpace', 'TransformedSpace', 'MatrixData', + 'Name', 'Value', 'Data']: self.write_to = None + elif name == 'Label': self.lata.labels.append(self.label) self.label = None @@ -277,24 +287,30 @@ def flush_chardata(self): data = ''.join(self._char_blocks) # Reset the char collector self._char_blocks = None + # Process data if self.write_to == 'Name': data = data.strip() self.nvpair.name = data + elif self.write_to == 'Value': data = data.strip() self.nvpair.value = data + elif self.write_to == 'DataSpace': data = data.strip() self.coordsys.dataspace = xform_codes.code[data] + elif self.write_to == 'TransformedSpace': data = data.strip() self.coordsys.xformspace = xform_codes.code[data] + elif self.write_to == 'MatrixData': # conversion to numpy array c = StringIO(data) self.coordsys.xform = np.loadtxt(c) c.close() + elif self.write_to == 'Data': da_tmp = self.img.darrays[-1] da_tmp.data = read_data_block(da_tmp.encoding, da_tmp.endian, @@ -303,13 +319,14 @@ def flush_chardata(self): # update the endianness according to the # current machine setting self.endian = gifti_endian_codes.code[sys.byteorder] + elif self.write_to == 'Label': self.label.label = data.strip() @property def pending_data(self): " True if there is character data pending for processing " - return not self._char_blocks is None + return self._char_blocks is not None class Outputter(GiftiImageParser): From f5fa1a4eb3f6dddd8d8dfa20221008636db5be4f Mon Sep 17 00:00:00 2001 From: Ben Cipollini Date: Mon, 26 Oct 2015 14:59:57 -0700 Subject: [PATCH 12/17] STY, DOC: linting, documentation. --- nibabel/gifti/gifti.py | 6 +-- nibabel/gifti/parse_gifti_fast.py | 2 +- nibabel/gifti/tests/test_parse_gifti_fast.py | 2 +- nibabel/xmlbasedimages.py | 39 ++++++++++++++++---- 4 files changed, 36 insertions(+), 13 deletions(-) diff --git a/nibabel/gifti/gifti.py b/nibabel/gifti/gifti.py index ee7bc63cb2..1139ec3584 100644 --- a/nibabel/gifti/gifti.py +++ b/nibabel/gifti/gifti.py @@ -28,7 +28,7 @@ class GiftiMetaData(xml.XmlSerializable): the list self.data """ def __init__(self, nvpair=None): self.data = [] - if not nvpair is None: + if nvpair is not None: self.data.append(nvpair) @classmethod @@ -295,7 +295,7 @@ def from_array(klass, cda.intent = intent_codes.code[intent] cda.encoding = gifti_encoding_codes.code[encoding] cda.endian = gifti_endian_codes.code[endian] - if not coordsys is None: + if coordsys is not None: cda.coordsys = coordsys cda.ind_ord = array_index_order_codes.code[ordering] cda.meta = GiftiMetaData.from_dict(meta) @@ -370,7 +370,7 @@ def print_summary(self): print('Endian: ', gifti_endian_codes.specs[self.endian]) print('ExternalFileName: ', self.ext_fname) print('ExternalFileOffset: ', self.ext_offset) - if not self.coordsys is None: + if self.coordsys is not None: print('----') print('Coordinate System:') print(self.coordsys.print_summary()) diff --git a/nibabel/gifti/parse_gifti_fast.py b/nibabel/gifti/parse_gifti_fast.py index 075c97c724..7b26fdf6fc 100644 --- a/nibabel/gifti/parse_gifti_fast.py +++ b/nibabel/gifti/parse_gifti_fast.py @@ -338,4 +338,4 @@ def __init__(self, *args, **kwargs): @np.deprecate_with_doc("Use GiftiImageParser.parse() instead.") def parse_gifti_file(fname=None, fptr=None, buffer_size=None): - GiftiImageParser().parse(fname=fname, fptr=fptr, buffer_size=buffer_size) + GiftiImageParser(buffer_size=buffer_size).parse(fname=fname, fptr=fptr) diff --git a/nibabel/gifti/tests/test_parse_gifti_fast.py b/nibabel/gifti/tests/test_parse_gifti_fast.py index 22504048b4..729b3ab650 100644 --- a/nibabel/gifti/tests/test_parse_gifti_fast.py +++ b/nibabel/gifti/tests/test_parse_gifti_fast.py @@ -317,8 +317,8 @@ def test_parse_dataarrays(): assert_equal(len(w), 1) assert_equal(img.numDA, 0) -def test_parse_deprecated(): +def test_parse_deprecated(): # Test deprecation with clear_and_catch_warnings() as w: diff --git a/nibabel/xmlbasedimages.py b/nibabel/xmlbasedimages.py index 2079954cb9..565737e8d8 100644 --- a/nibabel/xmlbasedimages.py +++ b/nibabel/xmlbasedimages.py @@ -34,14 +34,33 @@ class XmlBasedHeader(FileBasedHeader, XmlSerializable): class XmlImageParser(object): - """ Base class for defining how to parse xml-based images.""" + """ Base class for defining how to parse xml-based images. + + Image-specific parsers should define: + StartElementHandler + EndElementHandler + CharacterDataHandler + """ HANDLER_NAMES = ['StartElementHandler', 'EndElementHandler', 'CharacterDataHandler'] def __init__(self, encoding=None, buffer_size=35000000, verbose=0): - self.encoding = encoding + """ + Parameters + ---------- + encoding : str + string containing xml document + + buffer_size: None or int, optional + size of read buffer. None uses default buffer_size + from xml.parsers.expat. + + verbose : int, optional + amount of output during parsing (0=silent, by default). + """ + self.encoding=encoding self.buffer_size = buffer_size self.verbose = verbose self.img = None @@ -55,7 +74,7 @@ def _create_parser(self): parser.buffer_size = self.buffer_size return parser - def parse(self, string=None, fname=None, fptr=None, buffer_size=None): + def parse(self, string=None, fname=None, fptr=None): """ Parameters ---------- @@ -68,11 +87,6 @@ def parse(self, string=None, fname=None, fptr=None, buffer_size=None): fptr : file pointer open file pointer to an xml document - buffer_size: None or int, optional - size of read buffer. None gives default of 35000000 unless on python < - 2.6, in which case it is read only in the parser. In that case values - other than None cause a ValueError on execution - Returns ------- img : XmlBasedImage @@ -107,7 +121,16 @@ def CharacterDataHandler(self, data): class XmlBasedImage(FileBasedImage, XmlSerializable): + """Basic convenience wrapper around FileBasedImage and XmlSerializable. + + Properties + ---------- + parser : XmlImageParser + class name of the XML parser associated with this image type. + """ + parser = XmlImageParser + header = XmlBasedHeader def to_file_map(self, file_map=None): """ Save the current image to the specified file_map From c3dfb07431eeb24f84bac87c88a0c3460bf53a44 Mon Sep 17 00:00:00 2001 From: Ben Cipollini Date: Mon, 26 Oct 2015 15:00:10 -0700 Subject: [PATCH 13/17] Allow buffer_size=None, to avoid setting explicitly. --- nibabel/gifti/tests/test_parse_gifti_fast.py | 6 ++++++ nibabel/xmlbasedimages.py | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/nibabel/gifti/tests/test_parse_gifti_fast.py b/nibabel/gifti/tests/test_parse_gifti_fast.py index 729b3ab650..51938e4c86 100644 --- a/nibabel/gifti/tests/test_parse_gifti_fast.py +++ b/nibabel/gifti/tests/test_parse_gifti_fast.py @@ -330,3 +330,9 @@ def test_parse_deprecated(): warnings.filterwarnings('always', category=DeprecationWarning) assert_raises(ValueError, parse_gifti_file) assert_equal(len(w), 1) + + +def test_parse_with_buffersize(): + for buff_sz in [None, 1, 2**12]: + img2 = load(DATA_FILE2, buffer_size=buff_sz) + assert_equal(img2.darrays[0].data.shape, (143479, 1)) diff --git a/nibabel/xmlbasedimages.py b/nibabel/xmlbasedimages.py index 565737e8d8..dca19c931f 100644 --- a/nibabel/xmlbasedimages.py +++ b/nibabel/xmlbasedimages.py @@ -71,7 +71,8 @@ def _create_parser(self): parser = ParserCreate(encoding=self.encoding) # from xml package parser.buffer_text = True - parser.buffer_size = self.buffer_size + if self.buffer_size is not None: + parser.buffer_size = self.buffer_size return parser def parse(self, string=None, fname=None, fptr=None): From 10b63811296c1b5ed5436a7f7141d29eaaab8b47 Mon Sep 17 00:00:00 2001 From: Ben Cipollini Date: Tue, 27 Oct 2015 07:48:40 -0700 Subject: [PATCH 14/17] FIX: xmlbasedimages --- nibabel/xmlbasedimages.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nibabel/xmlbasedimages.py b/nibabel/xmlbasedimages.py index dca19c931f..2d8d2c172c 100644 --- a/nibabel/xmlbasedimages.py +++ b/nibabel/xmlbasedimages.py @@ -9,6 +9,8 @@ """ Thin layer around xml.etree.ElementTree, to abstract nibabel xml support. """ + +from io import BytesIO from xml.etree.ElementTree import Element, SubElement, tostring from xml.parsers.expat import ParserCreate @@ -92,11 +94,11 @@ def parse(self, string=None, fname=None, fptr=None): ------- img : XmlBasedImage """ - if int(fname is not None) + int(fptr is not None) + int(fname is not None) != 1: + if int(string is not None) + int(fptr is not None) + int(fname is not None) != 1: raise ValueError('Exactly one of fptr, fname, string must be specified.') if string is not None: - fptr = StringIO(string) + fptr = BytesIO(string) elif fname is not None: fptr = open(fname, 'r') From a17f26f87d654df4ace4f7d0ebf790e8a3ec03b0 Mon Sep 17 00:00:00 2001 From: Ben Cipollini Date: Wed, 4 Nov 2015 14:44:09 -0800 Subject: [PATCH 15/17] Remove XmlBasedImage, xmlbasedimages.py=>xmlutils.py --- nibabel/gifti/gifti.py | 44 +++++++++++++- nibabel/gifti/parse_gifti_fast.py | 7 ++- nibabel/{xmlbasedimages.py => xmlutils.py} | 69 ++-------------------- 3 files changed, 50 insertions(+), 70 deletions(-) rename nibabel/{xmlbasedimages.py => xmlutils.py} (65%) diff --git a/nibabel/gifti/gifti.py b/nibabel/gifti/gifti.py index 1139ec3584..60b6aae455 100644 --- a/nibabel/gifti/gifti.py +++ b/nibabel/gifti/gifti.py @@ -12,7 +12,8 @@ import numpy as np -from .. import xmlbasedimages as xml +from .. import xmlutils as xml +from ..filebasedimages import FileBasedImage from ..nifti1 import data_type_codes, xform_codes, intent_codes from .util import (array_index_order_codes, gifti_encoding_codes, gifti_endian_codes, KIND2FMT) @@ -385,7 +386,7 @@ def metadata(self): return self.meta.metadata -class GiftiImage(xml.XmlBasedImage): +class GiftiImage(xml.XmlSerializable, FileBasedImage): """ The Gifti spec suggests using the following suffixes to your filename when saving each specific type of data: @@ -564,3 +565,42 @@ def to_xml(self, enc='utf-8'): return b""" """ + xml.XmlSerializable.to_xml(self, enc) + + def to_file_map(self, file_map=None): + """ Save the current image to the specified file_map + + Parameters + ---------- + file_map : string + + Returns + ------- + None + """ + if file_map is None: + file_map = self.file_map + f = file_map['image'].get_prepare_fileobj('wb') + f.write(self.to_xml()) + + @classmethod + def from_file_map(klass, file_map, buffer_size=35000000): + """ Load a Gifti image from a file_map + + Parameters + file_map : string + + Returns + ------- + img : GiftiImage + Returns a GiftiImage + """ + parser = klass.parser(buffer_size=buffer_size) + parser.parse(fptr=file_map['image'].get_prepare_fileobj('rb')) + img = parser.img + return img + + @classmethod + def from_filename(klass, filename, buffer_size=35000000): + file_map = klass.filespec_to_file_map(filename) + img = klass.from_file_map(file_map, buffer_size=buffer_size) + return img diff --git a/nibabel/gifti/parse_gifti_fast.py b/nibabel/gifti/parse_gifti_fast.py index 7b26fdf6fc..a74b8afdef 100644 --- a/nibabel/gifti/parse_gifti_fast.py +++ b/nibabel/gifti/parse_gifti_fast.py @@ -22,7 +22,7 @@ from .util import (array_index_order_codes, gifti_encoding_codes, gifti_endian_codes) from ..nifti1 import data_type_codes, xform_codes, intent_codes -from ..xmlbasedimages import XmlImageParser +from ..xmlutils import XmlParser def read_data_block(encoding, endian, ordering, datatype, shape, data): @@ -72,12 +72,14 @@ def read_data_block(encoding, endian, ordering, datatype, shape, data): return newarr -class GiftiImageParser(XmlImageParser): +class GiftiImageParser(XmlParser): def __init__(self, encoding=None, buffer_size=35000000, verbose=0): super(GiftiImageParser, self).__init__(encoding=encoding, buffer_size=buffer_size, verbose=verbose) + # output + self.img = None # finite state machine stack self.fsm_state = [] @@ -333,7 +335,6 @@ class Outputter(GiftiImageParser): @np.deprecate_with_doc("Use GiftiImageParser instead.") def __init__(self, *args, **kwargs): super(Outputter, self).__init__(*args, **kwargs) - self.img = None @np.deprecate_with_doc("Use GiftiImageParser.parse() instead.") diff --git a/nibabel/xmlbasedimages.py b/nibabel/xmlutils.py similarity index 65% rename from nibabel/xmlbasedimages.py rename to nibabel/xmlutils.py index 2d8d2c172c..11c41c230e 100644 --- a/nibabel/xmlbasedimages.py +++ b/nibabel/xmlutils.py @@ -35,8 +35,8 @@ class XmlBasedHeader(FileBasedHeader, XmlSerializable): pass -class XmlImageParser(object): - """ Base class for defining how to parse xml-based images. +class XmlParser(object): + """ Base class for defining how to parse xml-based image snippets. Image-specific parsers should define: StartElementHandler @@ -62,10 +62,9 @@ def __init__(self, encoding=None, buffer_size=35000000, verbose=0): verbose : int, optional amount of output during parsing (0=silent, by default). """ - self.encoding=encoding + self.encoding = encoding self.buffer_size = buffer_size self.verbose = verbose - self.img = None def _create_parser(self): """Internal function that allows subclasses to mess @@ -88,11 +87,7 @@ def parse(self, string=None, fname=None, fptr=None): file name of an xml document. fptr : file pointer - open file pointer to an xml document - - Returns - ------- - img : XmlBasedImage + open file pointer to an xml documents """ if int(string is not None) + int(fptr is not None) + int(fname is not None) != 1: raise ValueError('Exactly one of fptr, fname, string must be specified.') @@ -107,12 +102,6 @@ def parse(self, string=None, fname=None, fptr=None): setattr(parser, name, getattr(self, name)) parser.ParseFile(fptr) - if fname is not None: - fptr.close() - self.img.set_filename(fname) - - return self.img - def StartElementHandler(self, name, attrs): raise NotImplementedError @@ -121,53 +110,3 @@ def EndElementHandler(self, name): def CharacterDataHandler(self, data): raise NotImplementedError - - -class XmlBasedImage(FileBasedImage, XmlSerializable): - """Basic convenience wrapper around FileBasedImage and XmlSerializable. - - Properties - ---------- - parser : XmlImageParser - class name of the XML parser associated with this image type. - """ - - parser = XmlImageParser - header = XmlBasedHeader - - def to_file_map(self, file_map=None): - """ Save the current image to the specified file_map - - Parameters - ---------- - file_map : string - - Returns - ------- - None - """ - if file_map is None: - file_map = self.file_map - f = file_map['image'].get_prepare_fileobj('wb') - f.write(self.to_xml()) - - @classmethod - def from_file_map(klass, file_map, buffer_size=35000000): - """ Load a Gifti image from a file_map - - Parameters - file_map : string - - Returns - ------- - img : GiftiImage - Returns a GiftiImage - """ - img = klass.parser(buffer_size=buffer_size).parse( - fptr=file_map['image'].get_prepare_fileobj('rb')) - return img - - @classmethod - def from_filename(klass, filename, buffer_size=35000000): - file_map = klass.filespec_to_file_map(filename) - return klass.from_file_map(file_map, buffer_size=buffer_size) From 7410d89643a5e5a0a26fb14df00c38e0a8be5e12 Mon Sep 17 00:00:00 2001 From: Ben Cipollini Date: Wed, 4 Nov 2015 14:44:26 -0800 Subject: [PATCH 16/17] Deprecate (not delete) Outputter.initialize. --- nibabel/gifti/parse_gifti_fast.py | 3 +++ nibabel/gifti/tests/test_parse_gifti_fast.py | 1 + 2 files changed, 4 insertions(+) diff --git a/nibabel/gifti/parse_gifti_fast.py b/nibabel/gifti/parse_gifti_fast.py index a74b8afdef..86492c3253 100644 --- a/nibabel/gifti/parse_gifti_fast.py +++ b/nibabel/gifti/parse_gifti_fast.py @@ -336,6 +336,9 @@ class Outputter(GiftiImageParser): def __init__(self, *args, **kwargs): super(Outputter, self).__init__(*args, **kwargs) + def initialize(self, *args, **kwargs): + self.__init__(*args, **kwargs) + @np.deprecate_with_doc("Use GiftiImageParser.parse() instead.") def parse_gifti_file(fname=None, fptr=None, buffer_size=None): diff --git a/nibabel/gifti/tests/test_parse_gifti_fast.py b/nibabel/gifti/tests/test_parse_gifti_fast.py index 51938e4c86..e9511b649b 100644 --- a/nibabel/gifti/tests/test_parse_gifti_fast.py +++ b/nibabel/gifti/tests/test_parse_gifti_fast.py @@ -325,6 +325,7 @@ def test_parse_deprecated(): warnings.filterwarnings('always', category=DeprecationWarning) op = Outputter() assert_equal(len(w), 1) + op.initialize() # smoke test--no error. with clear_and_catch_warnings() as w: warnings.filterwarnings('always', category=DeprecationWarning) From 5c850da0b1775ca873b5159a49e78c73062d96ff Mon Sep 17 00:00:00 2001 From: Ben Cipollini Date: Thu, 5 Nov 2015 04:29:04 -0800 Subject: [PATCH 17/17] Match previous interface for Outputter, add back minimal docstring. --- nibabel/gifti/parse_gifti_fast.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/nibabel/gifti/parse_gifti_fast.py b/nibabel/gifti/parse_gifti_fast.py index 86492c3253..5a5b92856a 100644 --- a/nibabel/gifti/parse_gifti_fast.py +++ b/nibabel/gifti/parse_gifti_fast.py @@ -333,11 +333,13 @@ def pending_data(self): class Outputter(GiftiImageParser): @np.deprecate_with_doc("Use GiftiImageParser instead.") - def __init__(self, *args, **kwargs): - super(Outputter, self).__init__(*args, **kwargs) + def __init__(self): + super(Outputter, self).__init__() - def initialize(self, *args, **kwargs): - self.__init__(*args, **kwargs) + def initialize(self): + """ Initialize outputter + """ + self.__init__() @np.deprecate_with_doc("Use GiftiImageParser.parse() instead.")