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
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.
48 from .register
import Registry
49 from .compression
import COMPRESSION_SUFFIXES
54 'BackendNotImplementedError',
62 class BackendError(Exception):
66 class BackendNotImplementedError(NotImplementedError):
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.
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
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.
104 inst
= BACKENDS
[backend
].backend
106 raise BackendError('Unknown backend: {}'.format(backend
))
109 raise BackendNotImplementedError(
110 'Backend for {} is not implemented'.format(backend
))
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
122 def _extension(file_path
):
123 """Helper function to get the extension string."""
125 name
, extension
= os
.path
.splitext(file_path
)
127 # If we hit a compressed suffix, get an additional suffix to test
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
)
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])
149 # At this point we have failed to find any sort of backend, just
151 raise BackendError("No backend found for any file in {}".format(
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
171 raise BackendNotImplementedError(
172 'Loader for {} is not implemented'.format(extension
))
174 return loader(file_path
, compression
)
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
198 raise BackendNotImplementedError(
199 'Writer for {} is not implemented'.format(extension
))
201 return writer(results
, file_path
)
204 'No module supports file extension "{}"'.format(extension
))
207 def set_meta(backend
, result
):
208 """Wrapper around meta that gets the right meta function."""
210 BACKENDS
[backend
].meta(result
)
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
))
222 # Otherwise re-raise the error