Index: src/sas/sascalc/dataloader/readers/abs_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/abs_reader.py	(revision 1efbc190a388190d54fbdbcf384110dd991977fe)
+++ src/sas/sascalc/dataloader/readers/abs_reader.py	(revision b8080e1affadb04559f0597dfddc5506d2044e9d)
@@ -29,7 +29,7 @@
     type_name = "IGOR 1D"
     # Wildcards
-    type = ["IGOR 1D files (*.abs)|*.abs"]
+    type = ["IGOR 1D files (*.abs)|*.abs", "IGOR 1D USANS files (*.cor)|*.cor"]
     # List of allowed extensions
-    ext = ['.abs']
+    ext = ['.abs', '.cor']
 
     def get_file_contents(self):
@@ -46,5 +46,4 @@
         self.current_datainfo = DataInfo()
         self.current_datainfo.filename = filepath
-        self.reset_data_list(len(lines))
         detector = Detector()
         data_line = 0
@@ -172,5 +171,5 @@
 
                 try:
-                    _x = float(toks[0])
+                    _x = float(toks[4])
                     _y = float(toks[1])
                     _dy = float(toks[2])
@@ -188,5 +187,13 @@
                     self.current_dataset.y[data_line] = _y
                     self.current_dataset.dy[data_line] = _dy
-                    self.current_dataset.dx[data_line] = _dx
+                    if _dx > 0:
+                        self.current_dataset.dx[data_line] = _dx
+                    else:
+                        if data_line == 0:
+                            self.current_dataset.dx = None
+                            self.current_dataset.dxl = np.zeros(len(lines))
+                            self.current_dataset.dxw = np.zeros(len(lines))
+                        self.current_dataset.dxl[data_line] = abs(_dx)
+                        self.current_dataset.dxw[data_line] = 0
                     data_line += 1
 
@@ -197,7 +204,10 @@
                     pass
 
+            # SANS Data:
             # The 6 columns are | Q (1/A) | I(Q) (1/cm) | std. dev.
             # I(Q) (1/cm) | sigmaQ | meanQ | ShadowFactor|
-            if line.count("The 6 columns") > 0:
+            # USANS Data:
+            # EMP LEVEL: <value> ; BKG LEVEL: <value>
+            if line.startswith("The 6 columns") or line.startswith("EMP LEVEL"):
                 is_data_started = True
 
Index: src/sas/sascalc/dataloader/readers/associations.py
===================================================================
--- src/sas/sascalc/dataloader/readers/associations.py	(revision 574adc767a9e86744546f8edd6a23dc4f81914ad)
+++ src/sas/sascalc/dataloader/readers/associations.py	(revision b8080e1affadb04559f0597dfddc5506d2044e9d)
@@ -26,4 +26,5 @@
     ".dat": "red2d_reader",
     ".abs": "abs_reader",
+    ".cor": "abs_reader",
     ".sans": "danse_reader",
     ".pdh": "anton_paar_saxs_reader"
Index: src/sas/sascalc/dataloader/readers/cansas_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/cansas_reader.py	(revision 2b538cdefcdc3d89de8c018fe7f225f875614c5a)
+++ src/sas/sascalc/dataloader/readers/cansas_reader.py	(revision b8080e1affadb04559f0597dfddc5506d2044e9d)
@@ -68,7 +68,5 @@
         data files do not appear a second time
         """
-        self.current_datainfo = None
-        self.current_dataset = None
-        self.current_data1d = None
+        super(Reader, self).reset_state()
         self.data = []
         self.process = Process()
@@ -79,5 +77,4 @@
         self.names = []
         self.cansas_defaults = {}
-        self.output = []
         self.ns_list = None
         self.logging = []
@@ -85,5 +82,5 @@
 
     def read(self, xml_file, schema_path="", invalid=True):
-        if schema_path != "" or invalid != True:
+        if schema_path != "" or not invalid:
             # read has been called from self.get_file_contents because xml file doens't conform to schema
             _, self.extension = os.path.splitext(os.path.basename(xml_file))
@@ -945,5 +942,5 @@
             pos, "z", datainfo.sample.position.z,
             {"unit": datainfo.sample.position_unit})
-        if written == True:
+        if written:
             self.append(pos, sample)
 
@@ -958,5 +955,5 @@
             ori, "yaw", datainfo.sample.orientation.z,
             {"unit": datainfo.sample.orientation_unit})
-        if written == True:
+        if written:
             self.append(ori, sample)
 
@@ -1005,5 +1002,5 @@
             size, "z", datainfo.source.beam_size.z,
             {"unit": datainfo.source.beam_size_unit})
-        if written == True:
+        if written:
             self.append(size, source)
 
@@ -1061,5 +1058,5 @@
                     size, "z", aperture.size.z,
                     {"unit": aperture.size_unit})
-                if written == True:
+                if written:
                     self.append(size, apert)
 
@@ -1084,5 +1081,5 @@
             written = written | self.write_node(det, "SDD", item.distance,
                                                 {"unit": item.distance_unit})
-            if written == True:
+            if written:
                 self.append(det, instr)
 
@@ -1094,5 +1091,5 @@
             written = written | self.write_node(off, "z", item.offset.z,
                                                 {"unit": item.offset_unit})
-            if written == True:
+            if written:
                 self.append(off, det)
 
@@ -1106,5 +1103,5 @@
                                                 item.orientation.z,
                                                 {"unit": item.orientation_unit})
-            if written == True:
+            if written:
                 self.append(ori, det)
 
@@ -1118,5 +1115,5 @@
                                                 item.beam_center.z,
                                                 {"unit": item.beam_center_unit})
-            if written == True:
+            if written:
                 self.append(center, det)
 
@@ -1128,5 +1125,5 @@
             written = written | self.write_node(pix, "z", item.pixel_size.z,
                                                 {"unit": item.pixel_size_unit})
-            if written == True:
+            if written:
                 self.append(pix, det)
             self.write_node(det, "slit_length", item.slit_length,
Index: src/sas/sascalc/dataloader/readers/cansas_reader_HDF5.py
===================================================================
--- src/sas/sascalc/dataloader/readers/cansas_reader_HDF5.py	(revision 2b538cdefcdc3d89de8c018fe7f225f875614c5a)
+++ src/sas/sascalc/dataloader/readers/cansas_reader_HDF5.py	(revision b8080e1affadb04559f0597dfddc5506d2044e9d)
@@ -9,11 +9,15 @@
 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
-
-
-class Reader():
+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):
     """
     A class for reading in CanSAS v2.0 data files. The existing iteration opens
@@ -25,4 +29,5 @@
     Any number of SASdata sets may be present in a SASentry and the data within
     can be either 1D I(Q) or 2D I(Qx, Qy).
+
     Also supports reading NXcanSAS formatted HDF5 files
 
@@ -39,8 +44,4 @@
     # Raw file contents to be processed
     raw_data = None
-    # Data info currently being read in
-    current_datainfo = None
-    # SASdata set currently being read in
-    current_dataset = None
     # List of plottable1D objects that should be linked to the current_datainfo
     data1d = None
@@ -55,8 +56,6 @@
     # Flag to bypass extension check
     allow_all = True
-    # List of files to return
-    output = None
-
-    def read(self, filename):
+
+    def get_file_contents(self):
         """
         This is the general read method that all SasView data_loaders must have.
@@ -66,5 +65,9 @@
         """
         # Reinitialize when loading a new data file to reset all class variables
-        self.reset_class_variables()
+        self.reset_state()
+
+        filename = self.f_open.name
+        self.f_open.close() # IO handled by h5py
+
         # Check that the file exists
         if os.path.isfile(filename):
@@ -74,20 +77,33 @@
             if extension in self.ext or self.allow_all:
                 # Load the data file
-                self.raw_data = h5py.File(filename, 'r')
-                # Read in all child elements of top level SASroot
-                self.read_children(self.raw_data, [])
-                # Add the last data set to the list of outputs
-                self.add_data_set()
-                # Close the data file
-                self.raw_data.close()
-        # Return data set(s)
-        return self.output
-
-    def reset_class_variables(self):
+                try:
+                    self.raw_data = h5py.File(filename, 'r')
+                except Exception as e:
+                    if extension not in self.ext:
+                        msg = "CanSAS2.0 HDF5 Reader could not load file {}".format(basename + extension)
+                        raise DefaultReaderException(msg)
+                    raise FileContentsException(e.message)
+                try:
+                    # Read in all child elements of top level SASroot
+                    self.read_children(self.raw_data, [])
+                    # Add the last data set to the list of outputs
+                    self.add_data_set()
+                except Exception as exc:
+                    raise FileContentsException(exc.message)
+                finally:
+                    # Close the data file
+                    self.raw_data.close()
+
+                for dataset in self.output:
+                    if isinstance(dataset, Data1D):
+                        if dataset.x.size < 5:
+                            self.output = []
+                            raise FileContentsException("Fewer than 5 data points found.")
+
+    def reset_state(self):
         """
         Create the reader object and define initial states for class variables
         """
-        self.current_datainfo = None
-        self.current_dataset = None
+        super(Reader, self).reset_state()
         self.data1d = []
         self.data2d = []
@@ -95,5 +111,4 @@
         self.errors = set()
         self.logging = []
-        self.output = []
         self.parent_class = u''
         self.detector = Detector()
@@ -115,8 +130,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)
@@ -125,4 +139,5 @@
 
             if isinstance(value, h5py.Group):
+                # Set parent class before recursion
                 self.parent_class = class_name
                 parent_list.append(key)
@@ -135,4 +150,6 @@
                 # Recursion step to access data within the group
                 self.read_children(value, parent_list)
+                # Reset parent class when returning from recursive method
+                self.parent_class = class_name
                 self.add_intermediate()
                 parent_list.remove(key)
@@ -165,4 +182,13 @@
                         self.current_dataset.x = data_set.flatten()
                     continue
+                elif key == u'Qdev':
+                    self.current_dataset.dx = data_set.flatten()
+                    continue
+                elif key == u'dQw':
+                    self.current_dataset.dxw = data_set.flatten()
+                    continue
+                elif key == u'dQl':
+                    self.current_dataset.dxl = data_set.flatten()
+                    continue
                 elif key == u'Qy':
                     self.current_dataset.yaxis("Q_y", unit)
@@ -198,4 +224,9 @@
 
                 for data_point in data_set:
+                    if isinstance(data_point, np.ndarray):
+                        if data_point.dtype.char == 'S':
+                            data_point = decode(bytes(data_point))
+                    else:
+                        data_point = decode(data_point)
                     # Top Level Meta Data
                     if key == u'definition':
@@ -203,4 +234,10 @@
                     elif key == u'run':
                         self.current_datainfo.run.append(data_point)
+                        try:
+                            run_name = h5attr(value, 'name')
+                            run_dict = {data_point: run_name}
+                            self.current_datainfo.run_name = run_dict
+                        except Exception:
+                            pass
                     elif key == u'title':
                         self.current_datainfo.title = data_point
@@ -411,5 +448,4 @@
         Data1D and Data2D objects
         """
-
         # Type cast data arrays to float64
         if len(self.current_datainfo.trans_spectrum) > 0:
@@ -435,20 +471,4 @@
         # Type cast data arrays to float64 and find min/max as appropriate
         for dataset in self.data2d:
-            dataset.data = dataset.data.astype(np.float64)
-            dataset.err_data = dataset.err_data.astype(np.float64)
-            if dataset.qx_data is not None:
-                dataset.xmin = np.min(dataset.qx_data)
-                dataset.xmax = np.max(dataset.qx_data)
-                dataset.qx_data = dataset.qx_data.astype(np.float64)
-            if dataset.dqx_data is not None:
-                dataset.dqx_data = dataset.dqx_data.astype(np.float64)
-            if dataset.qy_data is not None:
-                dataset.ymin = np.min(dataset.qy_data)
-                dataset.ymax = np.max(dataset.qy_data)
-                dataset.qy_data = dataset.qy_data.astype(np.float64)
-            if dataset.dqy_data is not None:
-                dataset.dqy_data = dataset.dqy_data.astype(np.float64)
-            if dataset.q_data is not None:
-                dataset.q_data = dataset.q_data.astype(np.float64)
             zeros = np.ones(dataset.data.size, dtype=bool)
             try:
@@ -473,29 +493,10 @@
                 dataset.x_bins = dataset.qx_data[:n_cols]
                 dataset.data = dataset.data.flatten()
-
-            final_dataset = combine_data_info_with_plottable(
-                dataset, self.current_datainfo)
-            self.output.append(final_dataset)
+            self.current_dataset = dataset
+            self.send_to_output()
 
         for dataset in self.data1d:
-            if dataset.x is not None:
-                dataset.x = dataset.x.astype(np.float64)
-                dataset.xmin = np.min(dataset.x)
-                dataset.xmax = np.max(dataset.x)
-            if dataset.y is not None:
-                dataset.y = dataset.y.astype(np.float64)
-                dataset.ymin = np.min(dataset.y)
-                dataset.ymax = np.max(dataset.y)
-            if dataset.dx is not None:
-                dataset.dx = dataset.dx.astype(np.float64)
-            if dataset.dxl is not None:
-                dataset.dxl = dataset.dxl.astype(np.float64)
-            if dataset.dxw is not None:
-                dataset.dxw = dataset.dxw.astype(np.float64)
-            if dataset.dy is not None:
-                dataset.dy = dataset.dy.astype(np.float64)
-            final_dataset = combine_data_info_with_plottable(
-                dataset, self.current_datainfo)
-            self.output.append(final_dataset)
+            self.current_dataset = dataset
+            self.send_to_output()
 
     def add_data_set(self, key=""):
@@ -579,7 +580,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 cee5c78929349ae10afc88cb8b95e0c907f1826a)
+++ src/sas/sascalc/dataloader/readers/danse_reader.py	(revision b8080e1affadb04559f0597dfddc5506d2044e9d)
@@ -157,5 +157,5 @@
         # Store all data
         # Store wavelength
-        if has_converter == True and self.current_datainfo.source.wavelength_unit != 'A':
+        if has_converter and self.current_datainfo.source.wavelength_unit != 'A':
             conv = Converter('A')
             wavelength = conv(wavelength,
@@ -164,5 +164,5 @@
 
         # Store distance
-        if has_converter == True and detector.distance_unit != 'm':
+        if has_converter and detector.distance_unit != 'm':
             conv = Converter('m')
             distance = conv(distance, units=detector.distance_unit)
@@ -170,5 +170,5 @@
 
         # Store pixel size
-        if has_converter == True and detector.pixel_size_unit != 'mm':
+        if has_converter and detector.pixel_size_unit != 'mm':
             conv = Converter('mm')
             pixel = conv(pixel, units=detector.pixel_size_unit)
@@ -191,5 +191,5 @@
         x_vals = np.tile(x_vals, (size_y, 1)).flatten()
         y_vals = np.tile(y_vals, (size_x, 1)).T.flatten()
-        if (np.all(self.current_dataset.err_data is None)
+        if (np.all(self.current_dataset.err_data == None)
                 or np.any(self.current_dataset.err_data <= 0)):
             new_err_data = np.sqrt(np.abs(self.current_dataset.data))
Index: src/sas/sascalc/dataloader/readers/sesans_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/sesans_reader.py	(revision 849094a5f7b0f78510cbd819f7325b0940f60cdb)
+++ src/sas/sascalc/dataloader/readers/sesans_reader.py	(revision b8080e1affadb04559f0597dfddc5506d2044e9d)
@@ -12,5 +12,5 @@
 from ..file_reader_base_class import FileReader
 from ..data_info import plottable_1D, DataInfo
-from ..loader_exceptions import FileContentsException, DataReaderException
+from ..loader_exceptions import FileContentsException
 
 # Check whether we have a converter available
@@ -18,5 +18,5 @@
 try:
     from sas.sascalc.data_util.nxsunit import Converter
-except:
+except ImportError:
     has_converter = False
 _ZERO = 1e-16
@@ -46,5 +46,5 @@
         line = self.nextline()
         params = {}
-        while not line.startswith("BEGIN_DATA"):
+        while line and not line.startswith("BEGIN_DATA"):
             terms = line.split()
             if len(terms) >= 2:
@@ -63,10 +63,11 @@
             raise FileContentsException("Wavelength has no units")
         if params["SpinEchoLength_unit"] != params["Wavelength_unit"]:
-            raise FileContentsException("The spin echo data has rudely used "
-                               "different units for the spin echo length "
-                               "and the wavelength.  While sasview could "
-                               "handle this instance, it is a violation "
-                               "of the file format and will not be "
-                               "handled by other software.")
+            raise FileContentsException(
+                "The spin echo data has rudely used "
+                "different units for the spin echo length "
+                "and the wavelength.  While sasview could "
+                "handle this instance, it is a violation "
+                "of the file format and will not be "
+                "handled by other software.")
 
         headers = self.nextline().split()
@@ -86,5 +87,5 @@
 
         if not data.size:
-            raise FileContentsException("{} is empty".format(path))
+            raise FileContentsException("{} is empty".format(self.filepath))
         x = data[:, headers.index("SpinEchoLength")]
         if "SpinEchoLength_error" in headers:
