source: sasview/src/sas/sascalc/data_util/registry.py @ b699768

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 b699768 was b699768, checked in by Piotr Rozyczko <piotr.rozyczko@…>, 8 years ago

Initial commit of the refactored SasCalc? module.

  • Property mode set to 100644
File size: 5.8 KB
Line 
1# This program is public domain
2"""
3File extension registry.
4
5This provides routines for opening files based on extension,
6and registers the built-in file extensions.
7"""
8
9import os.path
10
11class ExtensionRegistry(object):
12    """
13    Associate a file loader with an extension.
14
15    Note that there may be multiple loaders for the same extension.
16
17    Example: ::
18
19        registry = ExtensionRegistry()
20
21        # Add an association by setting an element
22        registry['.zip'] = unzip
23       
24        # Multiple extensions for one loader
25        registry['.tgz'] = untar
26        registry['.tar.gz'] = untar
27
28        # Generic extensions to use after trying more specific extensions;
29        # these will be checked after the more specific extensions fail.
30        registry['.gz'] = gunzip
31
32        # Multiple loaders for one extension
33        registry['.cx'] = cx1
34        registry['.cx'] = cx2
35        registry['.cx'] = cx3
36
37        # Show registered extensions
38        print registry.extensions()
39       
40        # Can also register a format name for explicit control from caller
41        registry['cx3'] = cx3
42        print registry.formats()
43
44        # Retrieve loaders for a file name
45        registry.lookup('hello.cx') -> [cx3,cx2,cx1]
46
47        # Run loader on a filename
48        registry.load('hello.cx') ->
49            try:
50                return cx3('hello.cx')
51            except:
52                try:
53                    return cx2('hello.cx')
54                except:
55                    return cx1('hello.cx')
56
57        # Load in a specific format ignoring extension
58        registry.load('hello.cx',format='cx3') ->
59            return cx3('hello.cx')
60    """
61    def __init__(self, **kw):
62        self.loaders = {}
63    def __setitem__(self, ext, loader):
64        if ext not in self.loaders:
65            self.loaders[ext] = []
66        self.loaders[ext].insert(0,loader)
67    def __getitem__(self, ext):
68        return self.loaders[ext]
69    def __contains__(self, ext):
70        return ext in self.loaders
71    def formats(self):
72        """
73        Return a sorted list of the registered formats.
74        """
75        names = [a for a in self.loaders.keys() if not a.startswith('.')]
76        names.sort()
77        return names
78    def extensions(self):
79        """
80        Return a sorted list of registered extensions.
81        """
82        exts = [a for a in self.loaders.keys() if a.startswith('.')]
83        exts.sort()
84        return exts
85    def lookup(self, path):
86        """
87        Return the loader associated with the file type of path.
88       
89        Raises ValueError if file type is not known.
90        """       
91        # Find matching extensions
92        extlist = [ext for ext in self.extensions() if path.endswith(ext)]
93        # Sort matching extensions by decreasing order of length
94        extlist.sort(lambda a,b: len(a)<len(b))
95        # Combine loaders for matching extensions into one big list
96        loaders = []
97        for L in [self.loaders[ext] for ext in extlist]:
98            loaders.extend(L)
99        # Remove duplicates if they exist
100        if len(loaders) != len(set(loaders)):
101            result = []
102            for L in loaders:
103                if L not in result: result.append(L)
104            loaders = L
105        # Raise an error if there are no matching extensions
106        if len(loaders) == 0:
107            raise ValueError, "Unknown file type for "+path
108        # All done
109        return loaders
110    def load(self, path, format=None):
111        """
112        Call the loader for the file type of path.
113
114        Raises ValueError if no loader is available.
115        Raises KeyError if format is not available.
116        May raise a loader-defined exception if loader fails.       
117        """
118        if format is None:
119            loaders = self.lookup(path)
120        else:
121            loaders = self.loaders[format]
122        for fn in loaders:
123            try:
124                return fn(path)
125            except:
126                pass # give other loaders a chance to succeed
127        # If we get here it is because all loaders failed
128        raise # reraises last exception
129
130def test():
131    reg = ExtensionRegistry()
132    class CxError(Exception): pass
133    def cx(file): return 'cx'
134    def new_cx(file): return 'new_cx'
135    def fail_cx(file): raise CxError
136    def cat(file): return 'cat'
137    def gunzip(file): return 'gunzip'
138    reg['.cx'] = cx
139    reg['.cx1'] = cx
140    reg['.cx'] = new_cx
141    reg['.gz'] = gunzip
142    reg['.cx.gz'] = new_cx
143    reg['.cx1.gz'] = fail_cx
144    reg['.cx1'] = fail_cx
145    reg['.cx2'] = fail_cx
146    reg['new_cx'] = new_cx
147
148    # Two loaders associated with .cx
149    assert reg.lookup('hello.cx') == [new_cx,cx]
150    # Make sure the last loader applies first
151    assert reg.load('hello.cx') == 'new_cx'
152    # Make sure the next loader applies if the first fails
153    assert reg.load('hello.cx1') == 'cx'
154    # Make sure the format override works
155    assert reg.load('hello.cx1',format='.cx.gz') == 'new_cx'
156    # Make sure the format override works
157    assert reg.load('hello.cx1',format='new_cx') == 'new_cx'
158    # Make sure the case of all loaders failing is correct
159    try:  reg.load('hello.cx2')
160    except CxError: pass # correct failure
161    else: raise AssertError,"Incorrect error on load failure"
162    # Make sure the case of no loaders fails correctly
163    try: reg.load('hello.missing')
164    except ValueError,msg:
165        assert str(msg)=="Unknown file type for hello.missing",'Message: <%s>'%(msg)
166    else: raise AssertError,"No error raised for missing extension"
167    assert reg.formats() == ['new_cx']
168    assert reg.extensions() == ['.cx','.cx.gz','.cx1','.cx1.gz','.cx2','.gz']
169    # make sure that it supports multiple '.' in filename
170    assert reg.load('hello.extra.cx1') == 'cx'
171    assert reg.load('hello.gz') == 'gunzip'
172    assert reg.load('hello.cx1.gz') == 'gunzip' # Since .cx1.gz fails
173
174if __name__ == "__main__": test()
Note: See TracBrowser for help on using the repository browser.