Index: src/sas/sascalc/dataloader/__init__.py
===================================================================
--- src/sas/sascalc/dataloader/__init__.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/dataloader/__init__.py	(revision 574adc767a9e86744546f8edd6a23dc4f81914ad)
@@ -1,3 +1,3 @@
-from data_info import *
-from manipulations import *
-from readers import *
+from .data_info import *
+from .manipulations import *
+from .readers import *
Index: src/sas/sascalc/dataloader/data_info.py
===================================================================
--- src/sas/sascalc/dataloader/data_info.py	(revision deaa0c6b324db1003ba4999149364cd82185294e)
+++ src/sas/sascalc/dataloader/data_info.py	(revision deaa0c6b324db1003ba4999149364cd82185294e)
@@ -716,5 +716,5 @@
                 self.y_unit = '1/cm'
         except: # the data is not recognized/supported, and the user is notified
-            raise(TypeError, 'data not recognized, check documentation for supported 1D data formats')
+            raise TypeError('data not recognized, check documentation for supported 1D data formats')
 
     def __str__(self):
@@ -796,5 +796,5 @@
                 len(self.y) != len(other.y):
                 msg = "Unable to perform operation: data length are not equal"
-                raise ValueError, msg
+                raise ValueError(msg)
             # Here we could also extrapolate between data points
             TOLERANCE = 0.01
@@ -802,5 +802,5 @@
                 if math.fabs((self.x[i] - other.x[i])/self.x[i]) > TOLERANCE:
                     msg = "Incompatible data sets: x-values do not match"
-                    raise ValueError, msg
+                    raise ValueError(msg)
 
             # Check that the other data set has errors, otherwise
@@ -876,5 +876,5 @@
         if not isinstance(other, Data1D):
             msg = "Unable to perform operation: different types of data set"
-            raise ValueError, msg
+            raise ValueError(msg)
         return True
 
@@ -948,5 +948,5 @@
 
         if len(self.detector) > 0:
-            raise RuntimeError, "Data2D: Detector bank already filled at init"
+            raise RuntimeError("Data2D: Detector bank already filled at init")
 
     def __str__(self):
@@ -1020,12 +1020,12 @@
                 len(self.qy_data) != len(other.qy_data):
                 msg = "Unable to perform operation: data length are not equal"
-                raise ValueError, msg
+                raise ValueError(msg)
             for ind in range(len(self.data)):
                 if math.fabs((self.qx_data[ind] - other.qx_data[ind])/self.qx_data[ind]) > TOLERANCE:
                     msg = "Incompatible data sets: qx-values do not match: %s %s" % (self.qx_data[ind], other.qx_data[ind])
-                    raise ValueError, msg
+                    raise ValueError(msg)
                 if math.fabs((self.qy_data[ind] - other.qy_data[ind])/self.qy_data[ind]) > TOLERANCE:
                     msg = "Incompatible data sets: qy-values do not match: %s %s" % (self.qy_data[ind], other.qy_data[ind])
-                    raise ValueError, msg
+                    raise ValueError(msg)
 
             # Check that the scales match
@@ -1108,5 +1108,5 @@
         if not isinstance(other, Data2D):
             msg = "Unable to perform operation: different types of data set"
-            raise ValueError, msg
+            raise ValueError(msg)
         return True
 
Index: src/sas/sascalc/dataloader/file_reader_base_class.py
===================================================================
--- src/sas/sascalc/dataloader/file_reader_base_class.py	(revision deaa0c6b324db1003ba4999149364cd82185294e)
+++ src/sas/sascalc/dataloader/file_reader_base_class.py	(revision deaa0c6b324db1003ba4999149364cd82185294e)
@@ -6,15 +6,23 @@
 
 import os
+import sys
 import re
 import logging
+from abc import abstractmethod
+
 import numpy as np
-from abc import abstractmethod
-from loader_exceptions import NoKnownLoaderException, FileContentsException,\
+from .loader_exceptions import NoKnownLoaderException, FileContentsException,\
     DataReaderException, DefaultReaderException
-from data_info import Data1D, Data2D, DataInfo, plottable_1D, plottable_2D,\
+from .data_info import Data1D, Data2D, DataInfo, plottable_1D, plottable_2D,\
     combine_data_info_with_plottable
 
 logger = logging.getLogger(__name__)
 
+if sys.version_info[0] < 3:
+    def decode(s):
+        return s
+else:
+    def decode(s):
+        return s.decode() if isinstance(s, bytes) else s
 
 class FileReader(object):
@@ -78,4 +86,26 @@
         # Return a list of parsed entries that data_loader can manage
         return self.output
+
+    def nextline(self):
+        """
+        Returns the next line in the file as a string.
+        """
+        #return self.f_open.readline()
+        return decode(self.f_open.readline())
+
+    def nextlines(self):
+        """
+        Returns the next line in the file as a string.
+        """
+        for line in self.f_open:
+            #yield line
+            yield decode(line)
+
+    def readall(self):
+        """
+        Returns the entire file as a string.
+        """
+        #return self.f_open.read()
+        return decode(self.f_open.read())
 
     def handle_error_message(self, msg):
Index: src/sas/sascalc/dataloader/loader.py
===================================================================
--- src/sas/sascalc/dataloader/loader.py	(revision dcb91cf83e474bdbe1c788d87690ca5f5e7841cb)
+++ src/sas/sascalc/dataloader/loader.py	(revision dc8d1c2838582489b1d85751d7e27f7348c8685c)
@@ -26,12 +26,14 @@
 import time
 from zipfile import ZipFile
+
 from sas.sascalc.data_util.registry import ExtensionRegistry
+
 # Default readers are defined in the readers sub-module
-import readers
-from loader_exceptions import NoKnownLoaderException, FileContentsException,\
+from . import readers
+from .loader_exceptions import NoKnownLoaderException, FileContentsException,\
     DefaultReaderException
-from readers import ascii_reader
-from readers import cansas_reader
-from readers import cansas_reader_HDF5
+from .readers import ascii_reader
+from .readers import cansas_reader
+from .readers import cansas_reader_HDF5
 
 logger = logging.getLogger(__name__)
@@ -73,4 +75,5 @@
         try:
             return super(Registry, self).load(path, format=format)
+        #except Exception: raise  # for debugging, don't use fallback loader
         except NoKnownLoaderException as nkl_e:
             pass  # Try the ASCII reader
@@ -327,5 +330,5 @@
         extlist = [ext for ext in self.extensions() if path.endswith(ext)]
         # Sort matching extensions by decreasing order of length
-        extlist.sort(lambda a, b: len(a) < len(b))
+        extlist.sort(key=len)
         # Combine loaders for matching extensions into one big list
         writers = []
@@ -341,5 +344,5 @@
         # Raise an error if there are no matching extensions
         if len(writers) == 0:
-            raise ValueError, "Unknown file type for " + path
+            raise ValueError("Unknown file type for " + path)
         # All done
         return writers
@@ -360,5 +363,5 @@
             try:
                 return fn(path, data)
-            except:
+            except Exception:
                 pass  # give other loaders a chance to succeed
         # If we get here it is because all loaders failed
Index: src/sas/sascalc/dataloader/manipulations.py
===================================================================
--- src/sas/sascalc/dataloader/manipulations.py	(revision 324e0bf8b73f2240c3c9fc343111f7c06cd3bca8)
+++ src/sas/sascalc/dataloader/manipulations.py	(revision 574adc767a9e86744546f8edd6a23dc4f81914ad)
@@ -26,5 +26,5 @@
 
 #from data_info import plottable_2D
-from data_info import Data1D
+from .data_info import Data1D
 
 
Index: src/sas/sascalc/dataloader/readers/__init__.py
===================================================================
--- src/sas/sascalc/dataloader/readers/__init__.py	(revision 488f3a5d892488fd37274dc8ae6ce49780434f37)
+++ src/sas/sascalc/dataloader/readers/__init__.py	(revision 574adc767a9e86744546f8edd6a23dc4f81914ad)
@@ -1,2 +1,2 @@
 # Method to associate extensions to default readers
-from associations import read_associations
+from .associations import read_associations
Index: src/sas/sascalc/dataloader/readers/abs_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/abs_reader.py	(revision ffb64746799c3fad4edf35cf95a741843e6ed5dc)
+++ src/sas/sascalc/dataloader/readers/abs_reader.py	(revision ffb64746799c3fad4edf35cf95a741843e6ed5dc)
@@ -11,10 +11,11 @@
 
 import logging
+
 import numpy as np
-from sas.sascalc.dataloader.file_reader_base_class import FileReader
-from sas.sascalc.dataloader.data_info import DataInfo, plottable_1D, Data1D,\
-    Detector
-from sas.sascalc.dataloader.loader_exceptions import FileContentsException,\
-    DefaultReaderException
+
+from sas.sascalc.data_util.nxsunit import Converter
+from ..file_reader_base_class import FileReader
+from ..data_info import DataInfo, plottable_1D, Data1D, Detector
+from ..loader_exceptions import FileContentsException, DefaultReaderException
 
 logger = logging.getLogger(__name__)
@@ -31,20 +32,15 @@
     # List of allowed extensions
     ext = ['.abs']
-    
+
     def get_file_contents(self):
-        """ 
+        """
         Get the contents of the file
-        
+
         :raise RuntimeError: when the file can't be opened
         :raise ValueError: when the length of the data vectors are inconsistent
         """
-        buff = self.f_open.read()
+        buff = self.readall()
         filepath = self.f_open.name
         lines = buff.splitlines()
-        self.has_converter = True
-        try:
-            from sas.sascalc.data_util.nxsunit import Converter
-        except:
-            self.has_converter = False
         self.output = []
         self.current_datainfo = DataInfo()
@@ -75,6 +71,5 @@
                 try:
                     value = float(line_toks[1])
-                    if self.has_converter and \
-                            self.current_datainfo.source.wavelength_unit != 'A':
+                    if self.current_datainfo.source.wavelength_unit != 'A':
                         conv = Converter('A')
                         self.current_datainfo.source.wavelength = conv(value,
@@ -89,5 +84,5 @@
                 try:
                     value = float(line_toks[3])
-                    if self.has_converter and detector.distance_unit != 'm':
+                    if detector.distance_unit != 'm':
                         conv = Converter('m')
                         detector.distance = conv(value,
@@ -95,5 +90,5 @@
                     else:
                         detector.distance = value
-                except:
+                except Exception:
                     msg = "ABSReader cannot read SDD from %s" % filepath
                     self.current_datainfo.errors.append(msg)
@@ -109,7 +104,12 @@
                 # Sample thickness in mm
                 try:
-                    value = float(line_toks[5][:-1])
-                    if self.has_converter and \
-                            self.current_datainfo.sample.thickness_unit != 'cm':
+                    # ABS writer adds 'C' with no space to the end of the
+                    # thickness column.  Remove it if it is there before
+                    # converting the thickness.
+                    if line_toks[5][:-1] not in '012345679.':
+                        value = float(line_toks[5][:-1])
+                    else:
+                        value = float(line_toks[5])
+                    if self.current_datainfo.sample.thickness_unit != 'cm':
                         conv = Converter('cm')
                         self.current_datainfo.sample.thickness = conv(value,
@@ -134,5 +134,5 @@
 
                 # Bin size
-                if self.has_converter and detector.pixel_size_unit != 'mm':
+                if detector.pixel_size_unit != 'mm':
                     conv = Converter('mm')
                     detector.pixel_size.x = conv(5.08,
@@ -146,10 +146,10 @@
                 # Store beam center in distance units
                 # Det 640 x 640 mm
-                if self.has_converter and detector.beam_center_unit != 'mm':
+                if detector.beam_center_unit != 'mm':
                     conv = Converter('mm')
                     detector.beam_center.x = conv(center_x * 5.08,
                                      units=detector.beam_center_unit)
                     detector.beam_center.y = conv(center_y * 5.08,
-                                    units=detector.beam_center_unit)
+                                     units=detector.beam_center_unit)
                 else:
                     detector.beam_center.x = center_x * 5.08
Index: src/sas/sascalc/dataloader/readers/anton_paar_saxs_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/anton_paar_saxs_reader.py	(revision fafe52a82bebe8d8a20bfb820388c964aa3c4b7b)
+++ src/sas/sascalc/dataloader/readers/anton_paar_saxs_reader.py	(revision a5bd87a4eae90ea636d8e5a5ddb6cd6370e029b3)
@@ -63,5 +63,5 @@
         ## Reinitialize the class when loading a new data file to reset all class variables
         self.reset_state()
-        buff = self.f_open.read()
+        buff = self.readall()
         self.raw_data = buff.splitlines()
         self.read_data()
Index: src/sas/sascalc/dataloader/readers/ascii_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/ascii_reader.py	(revision 7b07fbe306fb8a4fe61838f12a869394e2e47c0a)
+++ src/sas/sascalc/dataloader/readers/ascii_reader.py	(revision 7b07fbe306fb8a4fe61838f12a869394e2e47c0a)
@@ -45,5 +45,5 @@
         """
 
-        buff = self.f_open.read()
+        buff = self.readall()
         filepath = self.f_open.name
         lines = buff.splitlines()
@@ -130,5 +130,5 @@
                 # Reset # of lines of data candidates
                 candidate_lines = 0
-        
+
         if not is_data:
             self.set_all_to_none()
Index: src/sas/sascalc/dataloader/readers/associations.py
===================================================================
--- src/sas/sascalc/dataloader/readers/associations.py	(revision ce8c7bd4870bc1b63b94231ed8f227410e8faee1)
+++ src/sas/sascalc/dataloader/readers/associations.py	(revision 574adc767a9e86744546f8edd6a23dc4f81914ad)
@@ -40,5 +40,5 @@
     """
     # For each FileType entry, get the associated reader and extension
-    for ext, reader in settings.iteritems():
+    for ext, reader in settings.items():
         if reader is not None and ext is not None:
             # Associate the extension with a particular reader
@@ -47,9 +47,9 @@
             # and remove the extra line below.
             try:
-                exec "import %s" % reader
-                exec "loader.associate_file_type('%s', %s)" % (ext.lower(),
-                                                                reader)
-                exec "loader.associate_file_type('%s', %s)" % (ext.upper(),
-                                                                reader)
+                exec("from . import %s" % reader)
+                exec("loader.associate_file_type('%s', %s)"
+                     % (ext.lower(), reader))
+                exec("loader.associate_file_type('%s', %s)"
+                     % (ext.upper(), reader))
             except:
                 msg = "read_associations: skipping association"
Index: src/sas/sascalc/dataloader/readers/cansas_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/cansas_reader.py	(revision 62160509fbefa6093a58ec5116a94fa7a2a8999a)
+++ src/sas/sascalc/dataloader/readers/cansas_reader.py	(revision 62160509fbefa6093a58ec5116a94fa7a2a8999a)
@@ -1,20 +1,9 @@
 import logging
-import numpy as np
 import os
 import sys
 import datetime
 import inspect
-# For saving individual sections of data
-from sas.sascalc.dataloader.data_info import Data1D, Data2D, DataInfo, \
-    plottable_1D, plottable_2D
-from sas.sascalc.dataloader.data_info import Collimation, TransmissionSpectrum, \
-    Detector, Process, Aperture
-from sas.sascalc.dataloader.data_info import \
-    combine_data_info_with_plottable as combine_data
-import sas.sascalc.dataloader.readers.xml_reader as xml_reader
-from sas.sascalc.dataloader.readers.xml_reader import XMLreader
-from sas.sascalc.dataloader.readers.cansas_constants import CansasConstants, CurrentLevel
-from sas.sascalc.dataloader.loader_exceptions import FileContentsException, \
-    DefaultReaderException, DataReaderException
+
+import numpy as np
 
 # The following 2 imports *ARE* used. Do not remove either.
@@ -23,4 +12,16 @@
 
 from lxml import etree
+
+from sas.sascalc.data_util.nxsunit import Converter
+
+# For saving individual sections of data
+from ..data_info import Data1D, Data2D, DataInfo, plottable_1D, plottable_2D, \
+    Collimation, TransmissionSpectrum, Detector, Process, Aperture, \
+    combine_data_info_with_plottable as combine_data
+from ..loader_exceptions import FileContentsException, DefaultReaderException, \
+    DataReaderException
+from . import xml_reader
+from .xml_reader import XMLreader
+from .cansas_constants import CansasConstants, CurrentLevel
 
 logger = logging.getLogger(__name__)
@@ -33,9 +34,4 @@
 INVALID_XML = "\n\nThe loaded xml file, {0} does not fully meet the CanSAS v1.x specification. SasView loaded " + \
               "as much of the data as possible.\n\n"
-HAS_CONVERTER = True
-try:
-    from sas.sascalc.data_util.nxsunit import Converter
-except ImportError:
-    HAS_CONVERTER = False
 
 CONSTANTS = CansasConstants()
@@ -152,5 +148,5 @@
                 raise fc_exc
         except Exception as e: # Convert all other exceptions to FileContentsExceptions
-            raise FileContentsException(e.message)
+            raise FileContentsException(str(e))
         finally:
             if not self.f_open.closed:
@@ -622,15 +618,13 @@
                 else:
                     save_in = "current_datainfo"
-                exec "default_unit = self.{0}.{1}".format(save_in, unitname)
-                if local_unit and default_unit and local_unit.lower() != default_unit.lower() \
-                        and local_unit.lower() != "none":
-                    if HAS_CONVERTER == True:
-                        # Check local units - bad units raise KeyError
-                        data_conv_q = Converter(local_unit)
-                        value_unit = default_unit
-                        node_value = data_conv_q(node_value, units=default_unit)
-                    else:
-                        value_unit = local_unit
-                        err_msg = "Unit converter is not available.\n"
+                default_unit = getattrchain(self, '.'.join((save_in, unitname)))
+                if (local_unit and default_unit
+                        and local_unit.lower() != default_unit.lower()
+                        and local_unit.lower() != "none"):
+                    # Check local units - bad units raise KeyError
+                    #print("loading", tagname, node_value, local_unit, default_unit)
+                    data_conv_q = Converter(local_unit)
+                    value_unit = default_unit
+                    node_value = data_conv_q(node_value, units=default_unit)
                 else:
                     value_unit = local_unit
@@ -644,5 +638,5 @@
                     err_msg += "expecting [{0}]".format(default_unit)
                 value_unit = local_unit
-            except:
+            except Exception:
                 err_msg = "CanSAS reader: unknown error converting "
                 err_msg += "\"{0}\" unit [{1}]"
@@ -676,5 +670,5 @@
         doc, _ = self._to_xml_doc(datainfo)
         # Write the file
-        file_ref = open(filename, 'w')
+        file_ref = open(filename, 'wb')
         if self.encoding is None:
             self.encoding = "UTF-8"
@@ -867,7 +861,7 @@
         point = self.create_element("Idata")
         node.append(point)
-        qx = ','.join([str(datainfo.qx_data[i]) for i in xrange(len(datainfo.qx_data))])
-        qy = ','.join([str(datainfo.qy_data[i]) for i in xrange(len(datainfo.qy_data))])
-        intensity = ','.join([str(datainfo.data[i]) for i in xrange(len(datainfo.data))])
+        qx = ','.join(str(v) for v in datainfo.qx_data)
+        qy = ','.join(str(v) for v in datainfo.qy_data)
+        intensity = ','.join(str(v) for v in datainfo.data)
 
         self.write_node(point, "Qx", qx,
@@ -878,22 +872,17 @@
                         {'unit': datainfo._zunit})
         if datainfo.err_data is not None:
-            err = ','.join([str(datainfo.err_data[i]) for i in
-                            xrange(len(datainfo.err_data))])
+            err = ','.join(str(v) for v in datainfo.err_data)
             self.write_node(point, "Idev", err,
                             {'unit': datainfo._zunit})
         if datainfo.dqy_data is not None:
-            dqy = ','.join([str(datainfo.dqy_data[i]) for i in
-                            xrange(len(datainfo.dqy_data))])
+            dqy = ','.join(str(v) for v in datainfo.dqy_data)
             self.write_node(point, "Qydev", dqy,
                             {'unit': datainfo._yunit})
         if datainfo.dqx_data is not None:
-            dqx = ','.join([str(datainfo.dqx_data[i]) for i in
-                            xrange(len(datainfo.dqx_data))])
+            dqx = ','.join(str(v) for v in datainfo.dqx_data)
             self.write_node(point, "Qxdev", dqx,
                             {'unit': datainfo._xunit})
         if datainfo.mask is not None:
-            mask = ','.join(
-                ["1" if datainfo.mask[i] else "0"
-                 for i in xrange(len(datainfo.mask))])
+            mask = ','.join("1" if v else "0" for v in datainfo.mask)
             self.write_node(point, "Mask", mask)
 
@@ -1239,5 +1228,5 @@
         try:
             value = float(entry.text)
-        except:
+        except ValueError:
             value = None
 
@@ -1248,35 +1237,24 @@
             if units is not None:
                 toks = variable.split('.')
-                local_unit = None
-                exec "local_unit = storage.%s_unit" % toks[0]
+                # TODO: why split() when accessing unit, but not when setting value?
+                local_unit = getattr(storage, toks[0]+"_unit")
                 if local_unit is not None and units.lower() != local_unit.lower():
-                    if HAS_CONVERTER == True:
-                        try:
-                            conv = Converter(units)
-                            exec "storage.%s = %g" % \
-                                (variable, conv(value, units=local_unit))
-                        except:
-                            _, exc_value, _ = sys.exc_info()
-                            err_mess = "CanSAS reader: could not convert"
-                            err_mess += " %s unit [%s]; expecting [%s]\n  %s" \
-                                % (variable, units, local_unit, exc_value)
-                            self.errors.add(err_mess)
-                            if optional:
-                                logger.info(err_mess)
-                            else:
-                                raise ValueError, err_mess
-                    else:
-                        err_mess = "CanSAS reader: unrecognized %s unit [%s];"\
-                        % (variable, units)
-                        err_mess += " expecting [%s]" % local_unit
+                    try:
+                        conv = Converter(units)
+                        setattrchain(storage, variable, conv(value, units=local_unit))
+                    except Exception:
+                        _, exc_value, _ = sys.exc_info()
+                        err_mess = "CanSAS reader: could not convert"
+                        err_mess += " %s unit [%s]; expecting [%s]\n  %s" \
+                            % (variable, units, local_unit, exc_value)
                         self.errors.add(err_mess)
                         if optional:
                             logger.info(err_mess)
                         else:
-                            raise ValueError, err_mess
+                            raise ValueError(err_mess)
                 else:
-                    exec "storage.%s = value" % variable
+                    setattrchain(storage, variable, value)
             else:
-                exec "storage.%s = value" % variable
+                setattrchain(storage, variable, value)
 
     # DO NOT REMOVE - used in saving and loading panel states.
@@ -1298,5 +1276,5 @@
         entry = get_content(location, node)
         if entry is not None and entry.text is not None:
-            exec "storage.%s = entry.text.strip()" % variable
+            setattrchain(storage, variable, entry.text.strip())
 
 # DO NOT REMOVE Called by outside packages:
@@ -1341,2 +1319,20 @@
         return True
     return False
+
+def getattrchain(obj, chain, default=None):
+    """Like getattr, but the attr may contain multiple parts separated by '.'"""
+    for part in chain.split('.'):
+        if hasattr(obj, part):
+            obj = getattr(obj, part, None)
+        else:
+            return default
+    return obj
+
+def setattrchain(obj, chain, value):
+    """Like setattr, but the attr may contain multiple parts separated by '.'"""
+    parts = list(chain.split('.'))
+    for part in parts[-1]:
+        obj = getattr(obj, part, None)
+        if obj is None:
+            raise ValueError("missing parent object "+part)
+    setattr(obj, value)
Index: src/sas/sascalc/dataloader/readers/cansas_reader_HDF5.py
===================================================================
--- src/sas/sascalc/dataloader/readers/cansas_reader_HDF5.py	(revision c9ecd1b562e38798001341ea1382b347f0cc1af9)
+++ src/sas/sascalc/dataloader/readers/cansas_reader_HDF5.py	(revision 7b50f14eb38e44aca64b1a233e5b996b05f5dc0c)
@@ -9,11 +9,13 @@
 import sys
 
-from sas.sascalc.dataloader.data_info import plottable_1D, plottable_2D,\
+from ..data_info import plottable_1D, plottable_2D,\
     Data1D, Data2D, DataInfo, Process, Aperture, Collimation, \
     TransmissionSpectrum, Detector
-from sas.sascalc.dataloader.data_info import combine_data_info_with_plottable
-from sas.sascalc.dataloader.loader_exceptions import FileContentsException, DefaultReaderException
-from sas.sascalc.dataloader.file_reader_base_class import FileReader
-
+from ..data_info import combine_data_info_with_plottable
+from ..loader_exceptions import FileContentsException, DefaultReaderException
+from ..file_reader_base_class import FileReader, decode
+
+def h5attr(node, key, default=None):
+    return decode(node.attrs.get(key, default))
 
 class Reader(FileReader):
@@ -130,8 +132,7 @@
             # Get all information for the current key
             value = data.get(key)
-            if value.attrs.get(u'canSAS_class') is not None:
-                class_name = value.attrs.get(u'canSAS_class')
-            else:
-                class_name = value.attrs.get(u'NX_class')
+            class_name = h5attr(value, u'canSAS_class')
+            if class_name is None:
+                class_name = h5attr(value, u'NX_class')
             if class_name is not None:
                 class_prog = re.compile(class_name)
@@ -225,4 +226,6 @@
 
                 for data_point in data_set:
+                    if data_point.dtype.char == 'S':
+                        data_point = decode(bytes(data_point))
                     # Top Level Meta Data
                     if key == u'definition':
@@ -231,8 +234,8 @@
                         self.current_datainfo.run.append(data_point)
                         try:
-                            run_name = value.attrs['name']
+                            run_name = h5attr(value, 'name')
                             run_dict = {data_point: run_name}
                             self.current_datainfo.run_name = run_dict
-                        except:
+                        except Exception:
                             pass
                     elif key == u'title':
@@ -576,7 +579,7 @@
         :return: unit for the value passed to the method
         """
-        unit = value.attrs.get(u'units')
+        unit = h5attr(value, u'units')
         if unit is None:
-            unit = value.attrs.get(u'unit')
+            unit = h5attr(value, u'unit')
         # Convert the unit formats
         if unit == "1/A":
Index: src/sas/sascalc/dataloader/readers/danse_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/danse_reader.py	(revision a78a02fe90f1c962f523301685c143a27f11ba7d)
+++ src/sas/sascalc/dataloader/readers/danse_reader.py	(revision af3e9f590874286ad2be3a1e5be489d605e8a9a0)
@@ -14,10 +14,12 @@
 import math
 import os
+import logging
+
 import numpy as np
-import logging
-from sas.sascalc.dataloader.data_info import plottable_2D, DataInfo, Detector
-from sas.sascalc.dataloader.manipulations import reader2D_converter
-from sas.sascalc.dataloader.file_reader_base_class import FileReader
-from sas.sascalc.dataloader.loader_exceptions import FileContentsException, DataReaderException
+
+from ..data_info import plottable_2D, DataInfo, Detector
+from ..manipulations import reader2D_converter
+from ..file_reader_base_class import FileReader
+from ..loader_exceptions import FileContentsException, DataReaderException
 
 logger = logging.getLogger(__name__)
@@ -78,5 +80,5 @@
         data_start_line = 1
         while read_on:
-            line = self.f_open.readline()
+            line = self.nextline()
             data_start_line += 1
             if line.find("DATA:") >= 0:
@@ -112,5 +114,5 @@
             raise FileContentsException(msg)
 
-        for line_num, data_str in enumerate(self.f_open.readlines()):
+        for line_num, data_str in enumerate(self.nextlines()):
             toks = data_str.split()
             try:
Index: src/sas/sascalc/dataloader/readers/red2d_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/red2d_reader.py	(revision 2f85af7145254a68c54575abc23e44599ebef7ae)
+++ src/sas/sascalc/dataloader/readers/red2d_reader.py	(revision c8321cfca1fe64cbabb0c8b781cf5aecc57c211b)
@@ -10,16 +10,14 @@
 ######################################################################
 import os
+import math
+import time
+
 import numpy as np
-import math
-from sas.sascalc.dataloader.data_info import plottable_2D, DataInfo, Detector
-from sas.sascalc.dataloader.file_reader_base_class import FileReader
-from sas.sascalc.dataloader.loader_exceptions import FileContentsException
-
-# Look for unit converter
-has_converter = True
-try:
-    from sas.sascalc.data_util.nxsunit import Converter
-except:
-    has_converter = False
+
+from sas.sascalc.data_util.nxsunit import Converter
+
+from ..data_info import plottable_2D, DataInfo, Detector
+from ..file_reader_base_class import FileReader
+from ..loader_exceptions import FileContentsException
 
 
@@ -31,5 +29,5 @@
     try:
         return float(x_point)
-    except:
+    except Exception:
         return 0
 
@@ -51,5 +49,4 @@
         :param data: data2D
         """
-        import time
         # Write the file
         try:
@@ -72,5 +69,5 @@
     def get_file_contents(self):
         # Read file
-        buf = self.f_open.read()
+        buf = self.readall()
         self.f_open.close()
         # Instantiate data object
@@ -119,31 +116,25 @@
                 try:
                     wavelength = float(line_toks[1])
-                    # Units
-                    if has_converter == True and \
-                    self.current_datainfo.source.wavelength_unit != 'A':
+                    # Wavelength is stored in angstroms; convert if necessary
+                    if self.current_datainfo.source.wavelength_unit != 'A':
                         conv = Converter('A')
                         wavelength = conv(wavelength,
                                           units=self.current_datainfo.source.wavelength_unit)
-                except:
-                    #Not required
-                    pass
-                # Distance in mm
+                except Exception:
+                    pass  # Not required
                 try:
                     distance = float(line_toks[3])
-                    # Units
-                    if has_converter == True and self.current_datainfo.detector[0].distance_unit != 'm':
+                    # Distance is stored in meters; convert if necessary
+                    if self.current_datainfo.detector[0].distance_unit != 'm':
                         conv = Converter('m')
                         distance = conv(distance,
                             units=self.current_datainfo.detector[0].distance_unit)
-                except:
-                    #Not required
-                    pass
-
-                # Distance in meters
+                except Exception:
+                    pass  # Not required
+
                 try:
                     transmission = float(line_toks[4])
-                except:
-                    #Not required
-                    pass
+                except Exception:
+                    pass  # Not required
 
             if line.count("LAMBDA") > 0:
@@ -170,5 +161,5 @@
 
             ## Read and get data.
-            if data_started == True:
+            if data_started:
                 line_toks = line.split()
                 if len(line_toks) == 0:
@@ -178,4 +169,5 @@
                 col_num = len(line_toks)
                 break
+
         # Make numpy array to remove header lines using index
         lines_array = np.array(lines)
@@ -203,5 +195,5 @@
         # Change it(string) into float
         #data_list = map(float,data_list)
-        data_list1 = map(check_point, data_list)
+        data_list1 = list(map(check_point, data_list))
 
         # numpy array form
@@ -211,5 +203,5 @@
         try:
             data_point = data_array.reshape(row_num, col_num).transpose()
-        except:
+        except Exception:
             msg = "red2d_reader can't read this file: Incorrect number of data points provided."
             raise FileContentsException(msg)
@@ -325,7 +317,7 @@
 
         # Units of axes
-        self.current_dataset.xaxis("\\rm{Q_{x}}", 'A^{-1}')
-        self.current_dataset.yaxis("\\rm{Q_{y}}", 'A^{-1}')
-        self.current_dataset.zaxis("\\rm{Intensity}", "cm^{-1}")
+        self.current_dataset.xaxis(r"\rm{Q_{x}}", 'A^{-1}')
+        self.current_dataset.yaxis(r"\rm{Q_{y}}", 'A^{-1}')
+        self.current_dataset.zaxis(r"\rm{Intensity}", "cm^{-1}")
 
         # Store loading process information
Index: src/sas/sascalc/dataloader/readers/sesans_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/sesans_reader.py	(revision be434489a51d7ec8b03d82a7e2d53cb8baa37c83)
+++ src/sas/sascalc/dataloader/readers/sesans_reader.py	(revision 849094a5f7b0f78510cbd819f7325b0940f60cdb)
@@ -6,9 +6,11 @@
     Jurrian Bakker
 """
+import os
+
 import numpy as np
-import os
-from sas.sascalc.dataloader.file_reader_base_class import FileReader
-from sas.sascalc.dataloader.data_info import plottable_1D, DataInfo
-from sas.sascalc.dataloader.loader_exceptions import FileContentsException, DataReaderException
+
+from ..file_reader_base_class import FileReader
+from ..data_info import plottable_1D, DataInfo
+from ..loader_exceptions import FileContentsException, DataReaderException
 
 # Check whether we have a converter available
@@ -42,5 +44,5 @@
         self.output = []
 
-        line = self.f_open.readline()
+        line = self.nextline()
         params = {}
         while not line.startswith("BEGIN_DATA"):
@@ -48,5 +50,5 @@
             if len(terms) >= 2:
                 params[terms[0]] = " ".join(terms[1:])
-            line = self.f_open.readline()
+            line = self.nextline()
         self.params = params
 
@@ -68,5 +70,5 @@
                                "handled by other software.")
 
-        headers = self.f_open.readline().split()
+        headers = self.nextline().split()
 
         self._insist_header(headers, "SpinEchoLength")
Index: src/sas/sascalc/dataloader/readers/tiff_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/tiff_reader.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
+++ src/sas/sascalc/dataloader/readers/tiff_reader.py	(revision 574adc767a9e86744546f8edd6a23dc4f81914ad)
@@ -2,5 +2,5 @@
 #This software was developed by the University of Tennessee as part of the
 #Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
-#project funded by the US National Science Foundation. 
+#project funded by the US National Science Foundation.
 #See the license text in license.txt
 #copyright 2008, University of Tennessee
@@ -31,9 +31,9 @@
     ## Extension
     ext = ['.tif', '.tiff']
-        
+
     def read(self, filename=None):
         """
         Open and read the data in a file
-        
+
         :param file: path of the file
         """
@@ -44,15 +44,15 @@
         except:
             msg = "tiff_reader: could not load file. Missing Image module."
-            raise RuntimeError, msg
-        
+            raise RuntimeError(msg)
+
         # Instantiate data object
         output = Data2D()
         output.filename = os.path.basename(filename)
-            
+
         # Read in the image
         try:
             im = Image.open(filename)
         except:
-            raise  RuntimeError, "cannot open %s"%(filename)
+            raise  RuntimeError("cannot open %s"%(filename))
         data = im.getdata()
 
@@ -61,5 +61,5 @@
         output.err_data = np.zeros([im.size[0], im.size[1]])
         output.mask = np.ones([im.size[0], im.size[1]], dtype=bool)
-        
+
         # Initialize
         x_vals = []
@@ -69,5 +69,5 @@
         for i_x in range(im.size[0]):
             x_vals.append(i_x)
-            
+
         itot = 0
         for i_y in range(im.size[1]):
@@ -80,5 +80,5 @@
                 logger.error("tiff_reader: had to skip a non-float point")
                 continue
-            
+
             # Get bin number
             if math.fmod(itot, im.size[0]) == 0:
@@ -87,9 +87,9 @@
             else:
                 i_x += 1
-                
+
             output.data[im.size[1] - 1 - i_y][i_x] = value
-            
+
             itot += 1
-                
+
         output.xbins = im.size[0]
         output.ybins = im.size[1]
@@ -102,5 +102,5 @@
         output.ymin = 0
         output.ymax = im.size[0] - 1
-        
+
         # Store loading process information
         output.meta_data['loader'] = self.type_name
Index: src/sas/sascalc/dataloader/readers/xml_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/xml_reader.py	(revision fafe52a82bebe8d8a20bfb820388c964aa3c4b7b)
+++ src/sas/sascalc/dataloader/readers/xml_reader.py	(revision 7b50f14eb38e44aca64b1a233e5b996b05f5dc0c)
@@ -16,7 +16,9 @@
 
 import logging
+
 from lxml import etree
 from lxml.builder import E
-from sas.sascalc.dataloader.file_reader_base_class import FileReader
+
+from ..file_reader_base_class import FileReader, decode
 
 logger = logging.getLogger(__name__)
@@ -151,6 +153,6 @@
         Converts an etree element into a string
         """
-        return etree.tostring(elem, pretty_print=pretty_print, \
-                              encoding=encoding)
+        return decode(etree.tostring(elem, pretty_print=pretty_print,
+                                     encoding=encoding))
 
     def break_processing_instructions(self, string, dic):
