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

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 3a81cd9 was 3a81cd9, checked in by lewis, 7 years ago

Ensure error messages are displayed correctly

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