ext_transform_feedback: document missing mode in usage
[piglit.git] / framework / backends / __init__.py
bloba67bf0b6eba9a02d5b8419b083aea4cd6a9402ea
1 # coding=utf-8
2 # Copyright (c) 2014-2016, 2019 Intel Corporation
4 # Permission is hereby granted, free of charge, to any person obtaining a copy
5 # of this software and associated documentation files (the "Software"), to deal
6 # in the Software without restriction, including without limitation the rights
7 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 # copies of the Software, and to permit persons to whom the Software is
9 # furnished to do so, subject to the following conditions:
11 # The above copyright notice and this permission notice shall be included in
12 # all copies or substantial portions of the Software.
14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 # SOFTWARE.
22 """Provides a module like interface for backends.
24 This package provides an abstract interface for working with backends, which
25 implement various functions as provided in the Registry class, and then provide
26 a Registry instance as REGISTRY, which maps individual objects to objects that
27 piglit expects to use. This module then provides a thin abstraction layer so
28 that piglit is never aware of what backend it's using, it just asks for an
29 object and receives one.
31 Most consumers will want to import framework.backends and work directly with
32 the helper functions here. For some more detailed uses (test cases especially)
33 the modules themselves may be used.
35 When this module is loaded it will search through framework/backends for python
36 modules (those ending in .py), and attempt to import them. Then it will look
37 for an attribute REGISTRY in those modules, and if it as a
38 framework.register.Registry instance, it will add the name of that module (sans
39 .py) as a key, and the instance as a value to the BACKENDS dictionary. Each of
40 the helper functions in this module uses that dictionary to find the function
41 that a user actually wants.
43 """
45 import os
46 import importlib
48 from .register import Registry
49 from .compression import COMPRESSION_SUFFIXES
51 __all__ = [
52 'BACKENDS',
53 'BackendError',
54 'BackendNotImplementedError',
55 'get_backend',
56 'load',
57 'set_meta',
58 'write',
62 class BackendError(Exception):
63 pass
66 class BackendNotImplementedError(NotImplementedError):
67 pass
70 def _register():
71 """Register backends.
73 Walk through the list of backends and register them to a name in a
74 dictionary, so that they can be referenced from helper functions.
76 """
77 registry = {}
79 for module in os.listdir(os.path.dirname(os.path.abspath(__file__))):
80 module, extension = os.path.splitext(module)
81 if extension == '.py':
82 mod = importlib.import_module('framework.backends.{}'.format(module))
83 if hasattr(mod, 'REGISTRY') and isinstance(mod.REGISTRY, Registry):
84 registry[module] = mod.REGISTRY
86 return registry
89 BACKENDS = _register()
92 def get_backend(backend):
93 """Returns a BackendInstance based on the string passed.
95 If the backend isn't a known module, then a BackendError will be raised, it
96 is the responsibility of the caller to handle this error.
98 If the backend module exists, but there is not active implementation then a
99 BackendNotImplementedError will be raised, it is also the responsibility of
100 the caller to handle this error.
103 try:
104 inst = BACKENDS[backend].backend
105 except KeyError:
106 raise BackendError('Unknown backend: {}'.format(backend))
108 if inst is None:
109 raise BackendNotImplementedError(
110 'Backend for {} is not implemented'.format(backend))
112 return inst
115 def get_extension(file_path):
116 """Get the extension name to use when searching for a loader.
118 This function correctly handles compression suffixes, as long as they are
119 valid.
122 def _extension(file_path):
123 """Helper function to get the extension string."""
124 compression = 'none'
125 name, extension = os.path.splitext(file_path)
127 # If we hit a compressed suffix, get an additional suffix to test
128 # with.
129 # i.e: Use .json.gz rather that .gz
130 if extension in COMPRESSION_SUFFIXES:
131 compression = extension[1:] # Drop the leading '.'
132 # Remove any trailing '.', this fixes a bug where the filename
133 # is 'foo.json..xz, or similar
134 extension = os.path.splitext(name.rstrip('.'))[1]
136 return extension, compression
138 if not os.path.isdir(file_path):
139 return _extension(file_path)
140 else:
141 for file_ in os.listdir(file_path):
142 if file_.startswith('result') and not file_.endswith('.old'):
143 return _extension(file_)
145 tests = os.path.join(file_path, 'tests')
146 if os.path.exists(tests):
147 return _extension(os.listdir(tests)[0])
148 else:
149 # At this point we have failed to find any sort of backend, just
150 # except and die
151 raise BackendError("No backend found for any file in {}".format(
152 file_path))
155 def load(file_path):
156 """Wrapper for loading runs.
158 This function will attempt to determine how to load the file (based on file
159 extension), and then pass the file path into the appropriate loader, and
160 then return the TestrunResult instance.
164 extension, compression = get_extension(file_path)
166 for backend in BACKENDS.values():
167 if extension in backend.extensions:
168 loader = backend.load
170 if loader is None:
171 raise BackendNotImplementedError(
172 'Loader for {} is not implemented'.format(extension))
174 return loader(file_path, compression)
176 raise BackendError(
177 'No module supports file extension "{}"'.format(extension))
180 def write(results, file_path):
181 """Wrapper for writing runs.
183 This function will attempt to determine how to write a TestrunResult
184 instance into the file (based on file extension), passing the TestrunResult
185 instance into the appropriate writer.
187 Returns whether the backend will attempt to use compression when writing
188 the results file or not.
191 extension, _ = get_extension(file_path)
193 for backend in BACKENDS.values():
194 if extension in backend.extensions:
195 writer = backend.write
197 if writer is None:
198 raise BackendNotImplementedError(
199 'Writer for {} is not implemented'.format(extension))
201 return writer(results, file_path)
203 raise BackendError(
204 'No module supports file extension "{}"'.format(extension))
207 def set_meta(backend, result):
208 """Wrapper around meta that gets the right meta function."""
209 try:
210 BACKENDS[backend].meta(result)
211 except KeyError:
212 raise BackendError('No backend {}'.format(backend))
213 except TypeError as e:
214 # Since we initialize non-implemented backends as None, and None isn't
215 # callable then we'll get a TypeError, and we're looking for NoneType
216 # in the message. If we get that we really want a
217 # BackendNotImplementedError
218 if str(e) == "'NoneType' object is not callable":
219 raise BackendNotImplementedError(
220 'meta function for {} not implemented.'.format(backend))
221 else:
222 # Otherwise re-raise the error
223 raise