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

Last change on this file since b9d74f3 was b9d74f3, checked in by andyfaff, 8 years ago

MAINT: use raise Exception() not raise Exception

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