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

Last change on this file since 3d6c010 was b699768, checked in by Piotr Rozyczko <piotr.rozyczko@…>, 9 years ago

Initial commit of the refactored SasCalc? module.

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