source: sasview/src/sas/sascalc/dataloader/loader.py @ da8bb53

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since da8bb53 was da8bb53, checked in by krzywon, 7 years ago

Added a 4th data loader exception for generic readers that cannot open file.

  • Property mode set to 100644
File size: 14.5 KB
RevLine 
[4749514]1"""
2    File handler to support different file extensions.
[270c882b]3    Uses reflectometer registry utility.
[f06d7fc]4
[4749514]5    The default readers are found in the 'readers' sub-module
6    and registered by default at initialization time.
[f06d7fc]7
[4749514]8    To add a new default reader, one must register it in
9    the register_readers method found in readers/__init__.py.
[f06d7fc]10
[4749514]11    A utility method (find_plugins) is available to inspect
12    a directory (for instance, a user plug-in directory) and
13    look for new readers/writers.
14"""
15#####################################################################
16#This software was developed by the University of Tennessee as part of the
17#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
18#project funded by the US National Science Foundation.
19#See the license text in license.txt
20#copyright 2008, University of Tennessee
21######################################################################
22
23import os
24import sys
25import logging
26import time
[da8bb53]27import mimetypes
[4749514]28from zipfile import ZipFile
[b699768]29from sas.sascalc.data_util.registry import ExtensionRegistry
[4749514]30# Default readers are defined in the readers sub-module
31import readers
[da8bb53]32from loader_exceptions import NoKnownLoaderException, FileContentsException,\
33    DefaultReaderException
[4749514]34from readers import ascii_reader
35from readers import cansas_reader
[d0764bf]36from readers import cansas_reader_HDF5
[4749514]37
[270c882b]38
[463e7ffc]39logger = logging.getLogger(__name__)
[c155a16]40
[4749514]41class Registry(ExtensionRegistry):
42    """
43    Registry class for file format extensions.
44    Readers and writers are supported.
45    """
46    def __init__(self):
47        super(Registry, self).__init__()
[f06d7fc]48
[270c882b]49        # Writers
[4749514]50        self.writers = {}
[f06d7fc]51
[270c882b]52        # List of wildcards
[4749514]53        self.wildcards = ['All (*.*)|*.*']
[f06d7fc]54
[270c882b]55        # Creation time, for testing
[4749514]56        self._created = time.time()
[f06d7fc]57
[4749514]58        # Register default readers
59        readers.read_associations(self)
60
61    def load(self, path, format=None):
62        """
63        Call the loader for the file type of path.
64
65        :param path: file path
66        :param format: explicit extension, to force the use
67            of a particular reader
68
[b9b612a]69        Defaults to the ascii (multi-column), cansas XML, and cansas NeXuS
70        readers if no reader was registered for the file's extension.
[4749514]71        """
72        try:
73            return super(Registry, self).load(path, format=format)
[270c882b]74        except NoKnownLoaderException as e:
75            pass  # try the ASCII reader
[b9b612a]76        try:
77            ascii_loader = ascii_reader.Reader()
78            return ascii_loader.read(path)
[da8bb53]79        except DefaultReaderException:
80            pass  # Loader specific error to try the cansas XML reader
[b9b612a]81        try:
82            cansas_loader = cansas_reader.Reader()
83            return cansas_loader.read(path)
[da8bb53]84        except DefaultReaderException:
85            pass  # Loader specific error to try the cansas NeXuS reader
[270c882b]86        except FileContentsException:
[da8bb53]87            # TODO: Handle errors properly
88            pass
89        except Exception as csr:
90            # TODO: Modify cansas reader to throw DefaultReaderException
91            pass
[b9b612a]92        try:
93            cansas_nexus_loader = cansas_reader_HDF5.Reader()
94            return cansas_nexus_loader.read(path)
[da8bb53]95        except DefaultReaderException:
[7f75a3f]96            logging.errors("No default loader can load the data")
[b9b612a]97            # No known reader available. Give up and throw an error
98            msg = "\n\tUnknown data format: %s.\n\tThe file is not a " % path
[7f75a3f]99            msg += "known format that can be loaded by SasView.\n"
100            msg += "Traceback:\n%s" % e.message
101            raise NoKnownLoaderException, msg
[da8bb53]102        except FileContentsException:
103            # TODO: Handle error(s) properly
104            pass
[f06d7fc]105
[4749514]106    def find_plugins(self, dir):
107        """
108        Find readers in a given directory. This method
109        can be used to inspect user plug-in directories to
110        find new readers/writers.
[f06d7fc]111
[4749514]112        :param dir: directory to search into
113        :return: number of readers found
114        """
115        readers_found = 0
116        temp_path = os.path.abspath(dir)
117        if not os.path.isdir(temp_path):
118            temp_path = os.path.join(os.getcwd(), dir)
119        if not os.path.isdir(temp_path):
120            temp_path = os.path.join(os.path.dirname(__file__), dir)
121        if not os.path.isdir(temp_path):
122            temp_path = os.path.join(os.path.dirname(sys.path[0]), dir)
[f06d7fc]123
[4749514]124        dir = temp_path
125        # Check whether the directory exists
126        if not os.path.isdir(dir):
127            msg = "DataLoader couldn't locate DataLoader plugin folder."
128            msg += """ "%s" does not exist""" % dir
[c155a16]129            logger.warning(msg)
[4749514]130            return readers_found
[f06d7fc]131
[4749514]132        for item in os.listdir(dir):
133            full_path = os.path.join(dir, item)
134            if os.path.isfile(full_path):
[f06d7fc]135
[4749514]136                # Process python files
137                if item.endswith('.py'):
138                    toks = os.path.splitext(os.path.basename(item))
139                    try:
140                        sys.path.insert(0, os.path.abspath(dir))
141                        module = __import__(toks[0], globals(), locals())
142                        if self._identify_plugin(module):
143                            readers_found += 1
144                    except:
145                        msg = "Loader: Error importing "
146                        msg += "%s\n  %s" % (item, sys.exc_value)
[c155a16]147                        logger.error(msg)
[f06d7fc]148
[4749514]149                # Process zip files
150                elif item.endswith('.zip'):
151                    try:
152                        # Find the modules in the zip file
153                        zfile = ZipFile(item)
154                        nlist = zfile.namelist()
[f06d7fc]155
[4749514]156                        sys.path.insert(0, item)
157                        for mfile in nlist:
158                            try:
159                                # Change OS path to python path
160                                fullname = mfile.replace('/', '.')
161                                fullname = os.path.splitext(fullname)[0]
162                                module = __import__(fullname, globals(),
[f06d7fc]163                                                    locals(), [""])
[4749514]164                                if self._identify_plugin(module):
165                                    readers_found += 1
166                            except:
167                                msg = "Loader: Error importing"
168                                msg += " %s\n  %s" % (mfile, sys.exc_value)
[c155a16]169                                logger.error(msg)
[f06d7fc]170
[4749514]171                    except:
172                        msg = "Loader: Error importing "
173                        msg += " %s\n  %s" % (item, sys.exc_value)
[c155a16]174                        logger.error(msg)
[f06d7fc]175
[4749514]176        return readers_found
[f06d7fc]177
[4749514]178    def associate_file_type(self, ext, module):
179        """
180        Look into a module to find whether it contains a
181        Reader class. If so, APPEND it to readers and (potentially)
182        to the list of writers for the given extension
[f06d7fc]183
[4749514]184        :param ext: file extension [string]
185        :param module: module object
186        """
187        reader_found = False
[f06d7fc]188
[4749514]189        if hasattr(module, "Reader"):
190            try:
191                # Find supported extensions
192                loader = module.Reader()
193                if ext not in self.loaders:
194                    self.loaders[ext] = []
195                # Append the new reader to the list
196                self.loaders[ext].append(loader.read)
197
198                reader_found = True
[f06d7fc]199
[4749514]200                # Keep track of wildcards
201                type_name = module.__name__
202                if hasattr(loader, 'type_name'):
203                    type_name = loader.type_name
[f06d7fc]204
[4749514]205                wcard = "%s files (*%s)|*%s" % (type_name, ext.lower(),
[f06d7fc]206                                                ext.lower())
[4749514]207                if wcard not in self.wildcards:
208                    self.wildcards.append(wcard)
[f06d7fc]209
[4749514]210                # Check whether writing is supported
211                if hasattr(loader, 'write'):
212                    if ext not in self.writers:
213                        self.writers[ext] = []
214                    # Append the new writer to the list
215                    self.writers[ext].append(loader.write)
[f06d7fc]216
[4749514]217            except:
218                msg = "Loader: Error accessing"
219                msg += " Reader in %s\n  %s" % (module.__name__, sys.exc_value)
[c155a16]220                logger.error(msg)
[4749514]221        return reader_found
222
223    def associate_file_reader(self, ext, loader):
224        """
225        Append a reader object to readers
[f06d7fc]226
[4749514]227        :param ext: file extension [string]
228        :param module: reader object
229        """
230        reader_found = False
[f06d7fc]231
[4749514]232        try:
233            # Find supported extensions
234            if ext not in self.loaders:
235                self.loaders[ext] = []
236            # Append the new reader to the list
237            self.loaders[ext].append(loader.read)
238
239            reader_found = True
[f06d7fc]240
[4749514]241            # Keep track of wildcards
242            if hasattr(loader, 'type_name'):
243                type_name = loader.type_name
[f06d7fc]244
[4749514]245                wcard = "%s files (*%s)|*%s" % (type_name, ext.lower(),
246                                                ext.lower())
247                if wcard not in self.wildcards:
248                    self.wildcards.append(wcard)
[f06d7fc]249
[4749514]250        except:
251            msg = "Loader: Error accessing Reader "
252            msg += "in %s\n  %s" % (loader.__name__, sys.exc_value)
[c155a16]253            logger.error(msg)
[4749514]254        return reader_found
255
256    def _identify_plugin(self, module):
257        """
[f06d7fc]258        Look into a module to find whether it contains a
[4749514]259        Reader class. If so, add it to readers and (potentially)
260        to the list of writers.
261        :param module: module object
[f06d7fc]262
[4749514]263        """
264        reader_found = False
[f06d7fc]265
[4749514]266        if hasattr(module, "Reader"):
267            try:
268                # Find supported extensions
269                loader = module.Reader()
270                for ext in loader.ext:
271                    if ext not in self.loaders:
272                        self.loaders[ext] = []
273                    # When finding a reader at run time,
274                    # treat this reader as the new default
275                    self.loaders[ext].insert(0, loader.read)
276
277                    reader_found = True
[f06d7fc]278
[4749514]279                    # Keep track of wildcards
280                    type_name = module.__name__
281                    if hasattr(loader, 'type_name'):
282                        type_name = loader.type_name
283                    wcard = "%s files (*%s)|*%s" % (type_name, ext.lower(),
[f06d7fc]284                                                    ext.lower())
[4749514]285                    if wcard not in self.wildcards:
286                        self.wildcards.append(wcard)
[f06d7fc]287
[4749514]288                # Check whether writing is supported
289                if hasattr(loader, 'write'):
290                    for ext in loader.ext:
291                        if ext not in self.writers:
292                            self.writers[ext] = []
293                        self.writers[ext].insert(0, loader.write)
[f06d7fc]294
[4749514]295            except:
296                msg = "Loader: Error accessing Reader"
297                msg += " in %s\n  %s" % (module.__name__, sys.exc_value)
[c155a16]298                logger.error(msg)
[4749514]299        return reader_found
300
301    def lookup_writers(self, path):
302        """
303        :return: the loader associated with the file type of path.
304        :Raises ValueError: if file type is not known.
305        """
306        # Find matching extensions
307        extlist = [ext for ext in self.extensions() if path.endswith(ext)]
308        # Sort matching extensions by decreasing order of length
309        extlist.sort(lambda a, b: len(a) < len(b))
310        # Combine loaders for matching extensions into one big list
311        writers = []
312        for L in [self.writers[ext] for ext in extlist]:
313            writers.extend(L)
314        # Remove duplicates if they exist
315        if len(writers) != len(set(writers)):
316            result = []
317            for L in writers:
318                if L not in result:
319                    result.append(L)
320            writers = L
321        # Raise an error if there are no matching extensions
322        if len(writers) == 0:
323            raise ValueError, "Unknown file type for " + path
324        # All done
325        return writers
326
327    def save(self, path, data, format=None):
328        """
329        Call the writer for the file type of path.
330
331        Raises ValueError if no writer is available.
332        Raises KeyError if format is not available.
333        May raise a writer-defined exception if writer fails.
334        """
335        if format is None:
336            writers = self.lookup_writers(path)
337        else:
338            writers = self.writers[format]
339        for fn in writers:
340            try:
341                return fn(path, data)
342            except:
343                pass  # give other loaders a chance to succeed
344        # If we get here it is because all loaders failed
345        raise  # reraises last exception
346
[f06d7fc]347
[4749514]348class Loader(object):
349    """
350    Utility class to use the Registry as a singleton.
351    """
352    ## Registry instance
353    __registry = Registry()
[f06d7fc]354
[4749514]355    def associate_file_type(self, ext, module):
356        """
357        Look into a module to find whether it contains a
358        Reader class. If so, append it to readers and (potentially)
359        to the list of writers for the given extension
[f06d7fc]360
[4749514]361        :param ext: file extension [string]
362        :param module: module object
363        """
364        return self.__registry.associate_file_type(ext, module)
365
366    def associate_file_reader(self, ext, loader):
367        """
368        Append a reader object to readers
[f06d7fc]369
[4749514]370        :param ext: file extension [string]
371        :param module: reader object
372        """
373        return self.__registry.associate_file_reader(ext, loader)
374
375    def load(self, file, format=None):
376        """
377        Load a file
[f06d7fc]378
[4749514]379        :param file: file name (path)
380        :param format: specified format to use (optional)
381        :return: DataInfo object
382        """
383        return self.__registry.load(file, format)
[f06d7fc]384
[4749514]385    def save(self, file, data, format):
386        """
387        Save a DataInfo object to file
388        :param file: file name (path)
389        :param data: DataInfo object
[f06d7fc]390        :param format: format to write the data in
[4749514]391        """
392        return self.__registry.save(file, data, format)
[f06d7fc]393
[4749514]394    def _get_registry_creation_time(self):
395        """
396        Internal method used to test the uniqueness
397        of the registry object
398        """
399        return self.__registry._created
[f06d7fc]400
401    def find_plugins(self, directory):
[4749514]402        """
403        Find plugins in a given directory
[f06d7fc]404
[4749514]405        :param dir: directory to look into to find new readers/writers
406        """
[f06d7fc]407        return self.__registry.find_plugins(directory)
408
[4749514]409    def get_wildcards(self):
[f06d7fc]410        """
411        Return the list of wildcards
412        """
[4749514]413        return self.__registry.wildcards
Note: See TracBrowser for help on using the repository browser.