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

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 278ddee was 278ddee, checked in by krzywon, 7 years ago

Merge branch 'master' into ticket-876

# Conflicts:
# src/sas/sascalc/dataloader/readers/ascii_reader.py
# src/sas/sascalc/dataloader/readers/xml_reader.py

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