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

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 83ee7258 was 8dec7e7, checked in by lewis, 7 years ago

Raise DefaultReaderException? in NXcanSAS reader and handle correctly

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