source: sasview/DataLoader/loader.py @ e5df560

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.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since e5df560 was 4e367f6, checked in by ajj, 14 years ago

Fixing error in plugin path finding.

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