[Scipy-svn] r4770 - in trunk/scipy/io/matlab: . tests

scipy-svn@scip... scipy-svn@scip...
Sat Oct 4 19:58:18 CDT 2008


Author: matthew.brett@gmail.com
Date: 2008-10-04 19:58:08 -0500 (Sat, 04 Oct 2008)
New Revision: 4770

Modified:
   trunk/scipy/io/matlab/mio.py
   trunk/scipy/io/matlab/mio4.py
   trunk/scipy/io/matlab/mio5.py
   trunk/scipy/io/matlab/miobase.py
   trunk/scipy/io/matlab/tests/test_mio.py
Log:
Centralize recognition of matfile type, apply patch for ticket #743 by Ray Jones, with thanks

Modified: trunk/scipy/io/matlab/mio.py
===================================================================
--- trunk/scipy/io/matlab/mio.py	2008-10-05 00:43:08 UTC (rev 4769)
+++ trunk/scipy/io/matlab/mio.py	2008-10-05 00:58:08 UTC (rev 4770)
@@ -6,7 +6,9 @@
 
 import os
 import sys
+import warnings
 
+from miobase import get_matfile_version
 from mio4 import MatFile4Reader, MatFile4Writer
 from mio5 import MatFile5Reader, MatFile5Writer
 
@@ -57,11 +59,16 @@
             raise IOError, 'Reader needs file name or open file-like object'
         byte_stream = file_name
 
-    MR = MatFile4Reader(byte_stream, **kwargs)
-    if MR.format_looks_right():
-        return MR
-    return MatFile5Reader(byte_stream, **kwargs)
-
+    mv = get_matfile_version(byte_stream)
+    if mv == '4':
+        return MatFile4Reader(byte_stream, **kwargs)
+    elif mv == '5':
+        return MatFile5Reader(byte_stream, **kwargs)
+    elif mv == '7':
+        raise NotImplementedError('Please use PyTables for matlab HDF files')
+    else:
+        raise TypeError('Did not recognize version %s' % mv)
+    
 def loadmat(file_name,  mdict=None, appendmat=True, basename='raw', **kwargs):
     ''' Load Matlab(tm) file
 
@@ -88,10 +95,17 @@
     matlab_compatible   - returns matrices as would be loaded by matlab
                           (implies squeeze_me=False, chars_as_strings=False,
                           mat_dtype=True)
+    struct_as_record    - whether to load matlab structs as numpy record arrays, or
+    			  as old-style numpy arrays with dtype=object.
+                          (warns if not set, and defaults to False.  non-recarrays 
+                          cannot be exported via savemat.)
 
     v4 (Level 1.0), v6 and v7.1 matfiles are supported.
 
     '''
+    if not kwargs.get('struct_as_record', False):
+        warnings.warn("loading matlab structures as arrays of dtype=object is deprecated",
+                      DeprecationWarning, stacklevel=2)
     MR = mat_reader_factory(file_name, appendmat, **kwargs)
     matfile_dict = MR.get_variables()
     if mdict is not None:
@@ -125,7 +139,7 @@
     if format == '4':
         MW = MatFile4Writer(file_stream)
     elif format == '5':
-        MW = MatFile5Writer(file_stream)
+        MW = MatFile5Writer(file_stream, unicode_strings=True)
     else:
         raise ValueError, 'Format should be 4 or 5'
     MW.put_variables(mdict)

Modified: trunk/scipy/io/matlab/mio4.py
===================================================================
--- trunk/scipy/io/matlab/mio4.py	2008-10-05 00:43:08 UTC (rev 4769)
+++ trunk/scipy/io/matlab/mio4.py	2008-10-05 00:58:08 UTC (rev 4770)
@@ -198,15 +198,6 @@
     def matrix_getter_factory(self):
         return self._array_reader.matrix_getter_factory()
 
-    def format_looks_right(self):
-        # Mat4 files have a zero somewhere in first 4 bytes
-        self.mat_stream.seek(0)
-        mopt_bytes = np.ndarray(shape=(4,),
-                                dtype=np.uint8,
-                                buffer = self.mat_stream.read(4))
-        self.mat_stream.seek(0)
-        return 0 in mopt_bytes
-
     def guess_byte_order(self):
         self.mat_stream.seek(0)
         mopt = self.read_dtype(np.dtype('i4'))

Modified: trunk/scipy/io/matlab/mio5.py
===================================================================
--- trunk/scipy/io/matlab/mio5.py	2008-10-05 00:43:08 UTC (rev 4769)
+++ trunk/scipy/io/matlab/mio5.py	2008-10-05 00:58:08 UTC (rev 4770)
@@ -56,6 +56,10 @@
 
 mxCELL_CLASS = 1
 mxSTRUCT_CLASS = 2
+# The March 2008 edition of "Matlab 7 MAT-File Format" says that
+# mxOBJECT_CLASS = 3, whereas matrix.h says that mxLOGICAL = 3.
+# Matlab 2008a appears to save logicals as type 9, so we assume that
+# the document is correct.  See type 18, below.
 mxOBJECT_CLASS = 3
 mxCHAR_CLASS = 4
 mxSPARSE_CLASS = 5
@@ -67,6 +71,14 @@
 mxUINT16_CLASS = 11
 mxINT32_CLASS = 12
 mxUINT32_CLASS = 13
+# The following are not in the March 2008 edition of "Matlab 7
+# MAT-File Format," but were guessed from matrix.h.
+mxINT64_CLASS = 14
+mxUINT64_CLASS = 15
+mxFUNCTION_CLASS = 16
+# Not doing anything with these at the moment.
+mxOPAQUE_CLASS = 17
+mxOBJECT_CLASS_FROM_MATRIX_H = 18
 
 mdtypes_template = {
     miINT8: 'i1',
@@ -102,6 +114,8 @@
     mxUINT16_CLASS: 'u2',
     mxINT32_CLASS: 'i4',
     mxUINT32_CLASS: 'u4',
+    mxINT64_CLASS: 'i8',
+    mxUINT64_CLASS: 'u8',
     mxSINGLE_CLASS: 'f4',
     mxDOUBLE_CLASS: 'f8',
     }
@@ -117,9 +131,11 @@
     'i1': miINT8,
     'i2': miINT16,
     'i4': miINT32,
+    'i8': miINT64,
     'u1': miUINT8,
+    'u2': miUINT16,
     'u4': miUINT32,
-    'u2': miUINT16,
+    'u8': miUINT64,
     'S1': miUINT8,
     'U1': miUTF16,
     }
@@ -132,8 +148,10 @@
     'c16': mxDOUBLE_CLASS,
     'f4': mxSINGLE_CLASS,
     'c8': mxSINGLE_CLASS,
+    'i8': mxINT64_CLASS,
     'i4': mxINT32_CLASS,
     'i2': mxINT16_CLASS,
+    'u8': mxUINT64_CLASS,
     'u2': mxUINT16_CLASS,
     'u1': mxUINT8_CLASS,
     'S1': mxUINT8_CLASS,
@@ -163,12 +181,10 @@
     mxUINT16_CLASS,
     mxINT32_CLASS,
     mxUINT32_CLASS,
+    mxINT64_CLASS,
+    mxUINT64_CLASS,
     )
 
-class mat_struct(object):
-    ''' Placeholder for holding read data from structs '''
-    pass
-
 class mat_obj(object):
     ''' Placeholder for holding read data from objects '''
     pass
@@ -180,13 +196,13 @@
     factory function
     '''
 
-    def __init__(self, mat_stream, dtypes, processor_func, codecs, class_dtypes):
+    def __init__(self, mat_stream, dtypes, processor_func, codecs, class_dtypes, struct_as_record):
         super(Mat5ArrayReader, self).__init__(mat_stream,
                                               dtypes,
-                                              processor_func,
-                                              )
+                                              processor_func)
         self.codecs = codecs
         self.class_dtypes = class_dtypes
+        self.struct_as_record = struct_as_record
 
     def read_element(self, copy=True):
         raw_tag = self.mat_stream.read(8)
@@ -228,7 +244,7 @@
                             buffer=raw_str)
             if copy:
                 el = el.copy()
-
+       
         return el
 
     def matrix_getter_factory(self):
@@ -274,9 +290,11 @@
         if mc == mxCELL_CLASS:
             return Mat5CellMatrixGetter(self, header)
         if mc == mxSTRUCT_CLASS:
-            return Mat5StructMatrixGetter(self, header)
+            return Mat5StructMatrixGetter(self, header, self.struct_as_record)
         if mc == mxOBJECT_CLASS:
             return Mat5ObjectMatrixGetter(self, header)
+        if mc == mxFUNCTION_CLASS:
+            return Mat5FunctionMatrixGetter(self, header)
         raise TypeError, 'No reader for class code %s' % mc
 
 
@@ -294,7 +312,8 @@
             array_reader.dtypes,
             array_reader.processor_func,
             array_reader.codecs,
-            array_reader.class_dtypes)
+            array_reader.class_dtypes,
+            array_reader.struct_as_record)
 
 
 class Mat5MatrixGetter(MatMatrixGetter):
@@ -322,6 +341,7 @@
         self.mat_stream = array_reader.mat_stream
         self.data_position = self.mat_stream.tell()
         self.header = {}
+        self.name = ''
         self.is_global = False
         self.mat_dtype = 'f8'
 
@@ -420,44 +440,92 @@
     def get_item(self):
         return self.read_element()
 
+class mat_struct(object):
+    ''' Placeholder for holding read data from structs '''
+    pass
 
-class Mat5StructMatrixGetter(Mat5CellMatrixGetter):
-    def __init__(self, *args, **kwargs):
-        super(Mat5StructMatrixGetter, self).__init__(*args, **kwargs)
-        self.obj_template = mat_struct()
+class Mat5StructMatrixGetter(Mat5MatrixGetter):
+    def __init__(self, array_reader, header, struct_as_record):
+        super(Mat5StructMatrixGetter, self).__init__(array_reader, header)
+        self.struct_as_record = struct_as_record
 
     def get_raw_array(self):
         namelength = self.read_element()[0]
-        # get field names
         names = self.read_element()
-        splitnames = [names[i:i+namelength] for i in \
-                      xrange(0,len(names),namelength)]
-        self.obj_template._fieldnames = [x.tostring().strip('\x00')
-                                        for x in splitnames]
-        return super(Mat5StructMatrixGetter, self).get_raw_array()
+        field_names = [names[i:i+namelength].tostring().strip('\x00')
+                       for i in xrange(0,len(names),namelength)]
+        tupdims = tuple(self.header['dims'][::-1])
+        length = np.product(tupdims)
+        result = np.empty(length, dtype=[(field_name, object) 
+                                         for field_name in field_names])
+        for i in range(length):
+            for field_name in field_names:
+                result[i][field_name] = self.read_element()
+        
+        if not self.struct_as_record:
+            # Backward compatibility with previous format
+            self.obj_template = mat_struct()
+            self.obj_template._fieldnames = field_names
+            newresult = np.empty(length, dtype=object)
+            for i in range(length):
+                item = pycopy(self.obj_template)
+                for name in field_names:
+                    item.__dict__[name] = result[i][name]
+                newresult[i] = item
+            result = newresult
+        
+        return result.reshape(tupdims).T
 
-    def get_item(self):
-        item = pycopy(self.obj_template)
-        for element in item._fieldnames:
-            item.__dict__[element]  = self.read_element()
-        return item
+class MatlabObject:
+    def __init__(self, classname, field_names):
+        self.__dict__['classname'] = classname
+        self.__dict__['mobj_recarray'] = np.empty((1,1), dtype=[(field_name, object) 
+                                            for field_name in field_names])
 
+    def __getattr__(self, name):
+        mobj_recarray = self.__dict__['mobj_recarray']
+        if name in mobj_recarray.dtype.fields:
+            return mobj_recarray[0,0][name]
+        else:
+            raise AttributeError, "no field named %s in MatlabObject"%(name)
 
-class Mat5ObjectMatrixGetter(Mat5StructMatrixGetter):
-    def __init__(self, *args, **kwargs):
-        super(Mat5StructMatrixGetter, self).__init__(*args, **kwargs)
-        self.obj_template = mat_obj()
+    def __setattr__(self, name, value):
+        if name in self.__dict__['mobj_recarray'].dtype.fields:
+            self.__dict__['mobj_recarray'][0,0][name] = value
+        else:
+            self.__dict__[name] = value
+        
 
-    def get_raw_array(self):
-        self.obj_template._classname = self.read_element().tostring()
-        return super(Mat5ObjectMatrixGetter, self).get_raw_array()
+class Mat5ObjectMatrixGetter(Mat5MatrixGetter):
+    def get_array(self):
+        '''Matlab ojects are essentially structs, with an extra field, the classname.'''
+        classname = self.read_element().tostring()
+        namelength = self.read_element()[0]
+        names = self.read_element()
+        field_names = [names[i:i+namelength].tostring().strip('\x00')
+                       for i in xrange(0,len(names),namelength)]
+        result = MatlabObject(classname, field_names)
 
+        for field_name in field_names:
+            result.__setattr__(field_name, self.read_element())
 
+        return result
+
+
+class MatlabFunctionMatrix:
+    ''' Opaque object representing an array of function handles. '''
+    def __init__(self, arr):
+        self.arr = arr
+
+class Mat5FunctionMatrixGetter(Mat5CellMatrixGetter):
+    def get_array(self):
+        return MatlabFunctionMatrix(self.get_raw_array())
+
+
 class MatFile5Reader(MatFileReader):
     ''' Reader for Mat 5 mat files
-
     Adds the following attribute to base class
-
+    
     uint16_codec       - char codec to use for uint16 char arrays
                           (defaults to system default codec)
    '''
@@ -469,6 +537,7 @@
                  squeeze_me=False,
                  chars_as_strings=True,
                  matlab_compatible=False,
+                 struct_as_record=False,
                  uint16_codec=None
                  ):
         self.codecs = {}
@@ -478,6 +547,7 @@
             None,
             None,
             None,
+            struct_as_record
             )
         super(MatFile5Reader, self).__init__(
             mat_stream,
@@ -534,6 +604,8 @@
         return self._array_reader.matrix_getter_factory()
 
     def guess_byte_order(self):
+        ''' Guess byte order.
+        Sets stream pointer to 0 '''
         self.mat_stream.seek(126)
         mi = self.mat_stream.read(2)
         self.mat_stream.seek(0)
@@ -549,16 +621,7 @@
         hdict['__version__'] = '%d.%d' % (v_major, v_minor)
         return hdict
 
-    def format_looks_right(self):
-        # Mat4 files have a zero somewhere in first 4 bytes
-        self.mat_stream.seek(0)
-        mopt_bytes = np.ndarray(shape=(4,),
-                                dtype=np.uint8,
-                                buffer = self.mat_stream.read(4))
-        self.mat_stream.seek(0)
-        return 0 not in mopt_bytes
 
-
 class Mat5MatrixWriter(MatStreamWriter):
 
     mat_tag = np.zeros((), mdtypes_template['tag_full'])
@@ -644,7 +707,6 @@
 
 
 class Mat5NumericWriter(Mat5MatrixWriter):
-
     def write(self):
         imagf = self.arr.dtype.kind == 'c'
         try:
@@ -684,10 +746,8 @@
 
 
 class Mat5SparseWriter(Mat5MatrixWriter):
-
     def write(self):
         ''' Sparse matrices are 2D
-
         '''
         A = self.arr.tocsc() # convert to sparse CSC format
         A.sort_indices()     # MATLAB expects sorted row indices
@@ -704,6 +764,80 @@
         self.update_matrix_tag()
 
 
+class Mat5CompositeWriter(Mat5MatrixWriter):
+    def __init__(self, file_stream, arr, name, is_global=False, unicode_strings=False):
+        super(Mat5CompositeWriter, self).__init__(file_stream, arr, name, is_global)
+        self.unicode_strings = unicode_strings
+        
+
+class Mat5CellWriter(Mat5CompositeWriter):
+    def write(self):
+        self.write_header(mclass=mxCELL_CLASS)
+        # loop over data, column major
+        A = np.atleast_2d(self.arr).flatten('F')
+        MWG = Mat5WriterGetter(self.file_stream, self.unicode_strings)
+        for el in A:
+            MW = MWG.matrix_writer_factory(el, '')
+            MW.write()
+        self.update_matrix_tag()
+
+class Mat5FunctionWriter(Mat5CompositeWriter):
+    def __init__(self, file_stream, arr, name, is_global=False, unicode_strings=False):
+        super(Mat5FunctionWriter, self).__init__(file_stream, arr.arr, name, is_global)
+
+    def write(self):
+        self.write_header(mclass=mxFUNCTION_CLASS)
+        # loop over data, column major
+        A = np.atleast_2d(self.arr).flatten('F')
+        MWG = Mat5WriterGetter(self.file_stream, self.unicode_strings)
+        for el in A:
+            MW = MWG.matrix_writer_factory(el, '')
+            MW.write()
+        self.update_matrix_tag()
+
+
+class Mat5StructWriter(Mat5CompositeWriter):
+    def write(self):
+        self.write_header(mclass=mxSTRUCT_CLASS)
+        
+        # write fieldnames
+        fieldnames = [f[0] for f in self.arr.dtype.descr]
+        self.write_element(np.array([32], dtype='i4'))
+        self.write_element(np.array(fieldnames, dtype='S32'), mdtype=miINT8)
+        
+        A = np.atleast_2d(self.arr).flatten('F')
+        MWG = Mat5WriterGetter(self.file_stream, self.unicode_strings)
+        for el in A:
+            for f in fieldnames:
+                MW = MWG.matrix_writer_factory(el[f], '')
+                MW.write()
+        self.update_matrix_tag()
+
+class Mat5ObjectWriter(Mat5CompositeWriter):
+    def __init__(self, file_stream, arr, name, is_global=False, unicode_strings=False):
+        super(Mat5ObjectWriter, self).__init__(file_stream, arr.__dict__['mobj_recarray'], name, is_global)
+        self.classname = arr.classname
+
+    def write(self):
+        self.write_header(mclass=mxOBJECT_CLASS)
+
+        # write classnames
+        self.write_element(np.array(self.classname, dtype='S'), mdtype=miINT8)
+
+        # write fieldnames
+        fieldnames = [f[0] for f in self.arr.dtype.descr]
+        self.write_element(np.array([32], dtype='i4'))
+        self.write_element(np.array(fieldnames, dtype='S32'), mdtype=miINT8)
+        
+        A = np.atleast_2d(self.arr).flatten('F')
+        MWG = Mat5WriterGetter(self.file_stream, self.unicode_strings)
+        for el in A:
+            for f in fieldnames:
+                MW = MWG.matrix_writer_factory(el[f], '')
+                MW.write()
+        self.update_matrix_tag()
+
+    
 class Mat5WriterGetter(object):
     ''' Wraps stream and options, provides methods for getting Writer objects '''
     def __init__(self, stream, unicode_strings):
@@ -722,15 +856,18 @@
         if have_sparse:
             if scipy.sparse.issparse(arr):
                 return Mat5SparseWriter(self.stream, arr, name, is_global)
+            
+        if isinstance(arr, MatlabFunctionMatrix):
+            return Mat5FunctionWriter(self.stream, arr, name, is_global, self.unicode_strings)
+        if isinstance(arr, MatlabObject):
+            return Mat5ObjectWriter(self.stream, arr, name, is_global, self.unicode_strings)
+        
         arr = np.array(arr)
         if arr.dtype.hasobject:
-            types, arr_type = self.classify_mobjects(arr)
-            if arr_type == 'c':
-                return Mat5CellWriter(self.stream, arr, name, is_global, types)
-            elif arr_type == 's':
-                return Mat5StructWriter(self.stream, arr, name, is_global)
-            elif arr_type == 'o':
-                return Mat5ObjectWriter(self.stream, arr, name, is_global)
+            if arr.dtype.fields == None:
+                return Mat5CellWriter(self.stream, arr, name, is_global, self.unicode_strings)
+            else:
+                return Mat5StructWriter(self.stream, arr, name, is_global, self.unicode_strings)
         if arr.dtype.kind in ('U', 'S'):
             if self.unicode_strings:
                 return Mat5UniCharWriter(self.stream, arr, name, is_global)
@@ -739,47 +876,6 @@
         else:
             return Mat5NumericWriter(self.stream, arr, name, is_global)
 
-    def classify_mobjects(self, objarr):
-        ''' Function to classify objects passed for writing
-        returns
-        types         - S1 array of same shape as objarr with codes for each object
-                        i  - invalid object
-                        a  - ndarray
-                        s  - matlab struct
-                        o  - matlab object
-        arr_type       - one of
-                        c  - cell array
-                        s  - struct array
-                        o  - object array
-        '''
-        n = objarr.size
-        types = np.empty((n,), dtype='S1')
-        types[:] = 'i'
-        type_set = set()
-        flato = objarr.flat
-        for i in range(n):
-            obj = flato[i]
-            if isinstance(obj, np.ndarray):
-                types[i] = 'a'
-                continue
-            try:
-                fns = tuple(obj._fieldnames)
-            except AttributeError:
-                continue
-            try:
-                cn = obj._classname
-            except AttributeError:
-                types[i] = 's'
-                type_set.add(fns)
-                continue
-            types[i] = 'o'
-            type_set.add((cn, fns))
-        arr_type = 'c'
-        if len(set(types))==1 and len(type_set) == 1:
-            arr_type = types[0]
-        return types.reshape(objarr.shape), arr_type
-
-
 class MatFile5Writer(MatFileWriter):
     ''' Class for writing mat5 files '''
     def __init__(self, file_stream,
@@ -815,6 +911,8 @@
 
     def put_variables(self, mdict):
         for name, var in mdict.items():
+            if name[0] == '_':
+                continue
             is_global = name in self.global_vars
             self.writer_getter.rewind()
             self.writer_getter.matrix_writer_factory(

Modified: trunk/scipy/io/matlab/miobase.py
===================================================================
--- trunk/scipy/io/matlab/miobase.py	2008-10-05 00:43:08 UTC (rev 4769)
+++ trunk/scipy/io/matlab/miobase.py	2008-10-05 00:58:08 UTC (rev 4770)
@@ -22,6 +22,38 @@
         res *= e
     return res
 
+def get_matfile_version(fileobj):
+    ''' Return '4', '5', or '7' depending on apparent mat file type
+    Inputs
+    fileobj       - file object implementing seek() and read()
+    Outputs
+    version_str   - one of (strings) 4, 5, or 7
+    
+    Has the side effect of setting the file read pointer to 0
+    '''
+    # Mat4 files have a zero somewhere in first 4 bytes
+    fileobj.seek(0)
+    mopt_bytes = np.ndarray(shape=(4,),
+                           dtype=np.uint8,
+                           buffer = fileobj.read(4))
+    if 0 in mopt_bytes:
+        fileobj.seek(0)
+        return '4'
+    # For 5 or 7 we need to read an integer in the header
+    # bytes 124 through 128 contain a version integer
+    # and an endian test string
+    fileobj.seek(124)
+    tst_str = fileobj.read(4)
+    fileobj.seek(0)
+    maj_ind = int(tst_str[2] == 'I')
+    verb = ord(tst_str[maj_ind])
+    if verb == 1:
+        return '5'
+    elif verb == 2:
+        return '7'
+    raise ValueError('Unknown mat file type, version %d' % verb)
+
+
 class ByteOrder(object):
     ''' Namespace for byte ordering '''
     little_endian = sys.byteorder == 'little'
@@ -50,7 +82,7 @@
     Attaches to initialized stream
 
     Base class for "getters" - which do store state of what they are
-    reading on itialization, and therefore need to be initialized
+    reading on initialization, and therefore need to be initialized
     before each read, and "readers" which do not store state, and only
     need to be initialized once on object creation
 
@@ -96,14 +128,13 @@
     matlab_compatible  - returns matrices as would be loaded by matlab
                          (implies squeeze_me=False, chars_as_strings=False
                          mat_dtype=True)
+    struct_as_record   - return strutures as numpy records (only from v5 files)
 
     To make this class functional, you will need to override the
     following methods:
 
     set_dtypes              - sets data types defs from byte order
     matrix_getter_factory   - gives object to fetch next matrix from stream
-    format_looks_right      - returns True if format looks correct for
-                              this file type (Mat4, Mat5)
     guess_byte_order        - guesses file byte order from file
     """
 
@@ -113,6 +144,7 @@
                  squeeze_me=False,
                  chars_as_strings=True,
                  matlab_compatible=False,
+                 struct_as_record=False
                  ):
         # Initialize stream
         self.mat_stream = mat_stream
@@ -177,7 +209,8 @@
                           'get/set order code')
 
     def set_dtypes(self):
-        assert False, 'Not implemented'
+        ''' Set dtype endianness. In this case we have no dtypes '''
+        pass
 
     def convert_dtypes(self, dtype_template):
         dtypes = dtype_template.copy()
@@ -187,16 +220,13 @@
 
     def matrix_getter_factory(self):
         assert False, 'Not implemented'
-
-    def format_looks_right(self):
-        "Return True if the format looks right for this object"
-        assert False, 'Not implemented'
-
+    
     def file_header(self):
         return {}
 
     def guess_byte_order(self):
-        assert 0, 'Not implemented'
+        ''' As we do not know what file type we have, assume native '''
+        return ByteOrder.native_code
 
     def get_processor_func(self):
         ''' Processing to apply to read matrices
@@ -227,7 +257,7 @@
                     str_arr = arr.reshape(
                         (small_product(n_dims),
                          dims[-1]))
-                    arr = np.empty(n_dims, dtype=object)
+                    arr = np.empty(n_dims, dtype='U%d' % dims[-1])
                     for i in range(0, n_dims[-1]):
                         arr[...,i] = self.chars_to_str(str_arr[i])
                 else: # return string

Modified: trunk/scipy/io/matlab/tests/test_mio.py
===================================================================
--- trunk/scipy/io/matlab/tests/test_mio.py	2008-10-05 00:43:08 UTC (rev 4769)
+++ trunk/scipy/io/matlab/tests/test_mio.py	2008-10-05 00:58:08 UTC (rev 4770)
@@ -6,11 +6,11 @@
 from tempfile import mkdtemp
 from numpy.testing import *
 from numpy import arange, array, pi, cos, exp, sin, sqrt, ndarray,  \
-     zeros, reshape, transpose
+     zeros, reshape, transpose, dtype, empty
 import scipy.sparse as SP
 
 from scipy.io.matlab.mio import loadmat, savemat
-from scipy.io.matlab.mio5 import mat_obj, mat_struct
+from scipy.io.matlab.mio5 import MatlabObject
 
 import shutil
 import gzip
@@ -30,7 +30,7 @@
             self._check_level(level_label, ev, actual[i])
         return
     # object, as container for matlab structs and objects
-    elif isinstance(expected, mat_struct) or isinstance(expected, mat_obj):
+    elif isinstance(expected, MatlabObject):
         assert isinstance(actual, typex), \
                "Different types %s and %s at %s" % (typex, typac, label)
         ex_fields = dir(expected)
@@ -62,7 +62,7 @@
 
 def _check_case(self, name, files, case):
     for file_name in files:
-        matdict = loadmat(file_name)
+        matdict = loadmat(file_name, struct_as_record=True)
         label = "test %s; file %s" % (name, file_name)
         for k, expected in case.items():
             k_label = "%s, variable %s" % (label, k)
@@ -164,10 +164,8 @@
      'expected': {'testsparsefloat': SP.csc_matrix(array([[-1+2j,0,2],[0,-3j,0]]))},
      },
     ]
-st = mat_struct()
-st.stringfield = u'Rats live on no evil star.'
-st.doublefield = array([sqrt(2),exp(1),pi])
-st.complexfield = (1+1j)*array([sqrt(2),exp(1),pi])
+st = array([(u'Rats live on no evil star.', array([sqrt(2),exp(1),pi]), (1+1j)*array([sqrt(2),exp(1),pi]))], 
+           dtype=[(n, object) for n in ['stringfield', 'doublefield', 'complexfield']])
 case_table5.append(
     {'name': 'struct',
      'expected': {'teststruct': st}
@@ -182,25 +180,24 @@
     {'name': 'cellnest',
      'expected': {'testcellnest': a},
      })
-st = mat_struct()
-st.one = array(1)
-st.two = mat_struct()
-st.two.three = u'number 3'
+st = empty((1,1), dtype=[(n, object) for n in ['one', 'two']])
+st[0,0]['one'] = array(1)
+st[0,0]['two'] = empty((1,1), dtype=[('three', object)])
+st[0,0]['two'][0,0]['three'] = u'number 3'
 case_table5.append(
     {'name': 'structnest',
      'expected': {'teststructnest': st}
      })
-a = array([mat_struct(), mat_struct()])
-a[0].one = array(1)
-a[0].two = array(2)
-a[1].one = u'number 1'
-a[1].two = u'number 2'
+a = empty((2,1), dtype=[(n, object) for n in ['one', 'two']])
+a[0,0]['one'] = array(1)
+a[0,0]['two'] = array(2)
+a[1,0]['one'] = u'number 1'
+a[1,0]['two'] = u'number 2'
 case_table5.append(
     {'name': 'structarr',
      'expected': {'teststructarr': a}
      })
-a = mat_obj()
-a._classname = 'inline'
+a = MatlabObject('inline', ['expr', 'args', 'isEmpty', 'numArgs', 'version'])
 a.expr = u'x'
 a.inputExpr = u' x = INLINE_INPUTS_{1};'
 a.args = u'x'
@@ -255,7 +252,7 @@
         mat_stream.close()
 
         mat_stream = gzip.open( fname,mode='rb')
-        actual = loadmat(mat_stream)
+        actual = loadmat(mat_stream, struct_as_record=True)
         mat_stream.close()
     finally:
         shutil.rmtree(tmpdir)



More information about the Scipy-svn mailing list