1 # Copyright (c) 2014-2016 Intel Corporation
3 # Permission is hereby granted, free of charge, to any person obtaining a copy
4 # of this software and associated documentation files (the "Software"), to deal
5 # in the Software without restriction, including without limitation the rights
6 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 # copies of the Software, and to permit persons to whom the Software is
8 # furnished to do so, subject to the following conditions:
10 # The above copyright notice and this permission notice shall be included in
11 # all copies or substantial portions of the Software.
13 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 """Provides a module like interface for backends.
23 This package provides an abstract interface for working with backends, which
24 implement various functions as provided in the Registry class, and then provide
25 a Registry instance as REGISTRY, which maps individual objects to objects that
26 piglit expects to use. This module then provides a thin abstraction layer so
27 that piglit is never aware of what backend it's using, it just asks for an
28 object and receives one.
30 Most consumers will want to import framework.backends and work directly with
31 the helper functions here. For some more detailed uses (test cases especially)
32 the modules themselves may be used.
34 When this module is loaded it will search through framework/backends for python
35 modules (those ending in .py), and attempt to import them. Then it will look
36 for an attribute REGISTRY in those modules, and if it as a
37 framework.register.Registry instance, it will add the name of that module (sans
38 .py) as a key, and the instance as a value to the BACKENDS dictionary. Each of
39 the helper functions in this module uses that dictionary to find the function
40 that a user actually wants.
44 from __future__
import (
45 absolute_import
, division
, print_function
, unicode_literals
52 from .register
import Registry
53 from .compression
import COMPRESSION_SUFFIXES
58 'BackendNotImplementedError',
65 class BackendError(Exception):
69 class BackendNotImplementedError(NotImplementedError):
76 Walk through the list of backends and register them to a name in a
77 dictionary, so that they can be referenced from helper functions.
82 for module
in os
.listdir(os
.path
.dirname(os
.path
.abspath(__file__
))):
83 module
, extension
= os
.path
.splitext(module
)
84 if extension
== '.py':
85 mod
= importlib
.import_module('framework.backends.{}'.format(module
))
86 if hasattr(mod
, 'REGISTRY') and isinstance(mod
.REGISTRY
, Registry
):
87 registry
[module
] = mod
.REGISTRY
92 BACKENDS
= _register()
95 def get_backend(backend
):
96 """Returns a BackendInstance based on the string passed.
98 If the backend isn't a known module, then a BackendError will be raised, it
99 is the responsibility of the caller to handle this error.
101 If the backend module exists, but there is not active implementation then a
102 BackendNotImplementedError will be raised, it is also the responsibility of
103 the caller to handle this error.
107 inst
= BACKENDS
[backend
].backend
109 raise BackendError('Unknown backend: {}'.format(backend
))
112 raise BackendNotImplementedError(
113 'Backend for {} is not implemented'.format(backend
))
119 """Wrapper for loading runs.
121 This function will attempt to determine how to load the file (based on file
122 extension), and then pass the file path into the appropriate loader, and
123 then return the TestrunResult instance.
126 def get_extension(file_path
):
127 """Get the extension name to use when searching for a loader.
129 This function correctly handles compression suffixes, as long as they
133 def _extension(file_path
):
134 """Helper function to get the extension string."""
136 name
, extension
= os
.path
.splitext(file_path
)
138 # If we hit a compressed suffix, get an additional suffix to test
140 # i.e: Use .json.gz rather that .gz
141 if extension
in COMPRESSION_SUFFIXES
:
142 compression
= extension
[1:] # Drop the leading '.'
143 # Remove any trailing '.', this fixes a bug where the filename
144 # is 'foo.json..xz, or similar
145 extension
= os
.path
.splitext(name
.rstrip('.'))[1]
147 return extension
, compression
149 if not os
.path
.isdir(file_path
):
150 return _extension(file_path
)
152 for file_
in os
.listdir(file_path
):
153 if file_
.startswith('result') and not file_
.endswith('.old'):
154 return _extension(file_
)
156 tests
= os
.path
.join(file_path
, 'tests')
157 if os
.path
.exists(tests
):
158 return _extension(os
.listdir(tests
)[0])
160 # At this point we have failed to find any sort of backend, just
162 raise BackendError("No backend found for any file in {}".format(
165 extension
, compression
= get_extension(file_path
)
167 for backend
in six
.itervalues(BACKENDS
):
168 if extension
in backend
.extensions
:
169 loader
= backend
.load
172 raise BackendNotImplementedError(
173 'Loader for {} is not implemented'.format(extension
))
175 return loader(file_path
, compression
)
178 'No module supports file extensions "{}"'.format(extension
))
181 def set_meta(backend
, result
):
182 """Wrapper around meta that gets the right meta function."""
184 BACKENDS
[backend
].meta(result
)
186 raise BackendError('No backend {}'.format(backend
))
187 except TypeError as e
:
188 # Since we initialize non-implemented backends as None, and None isn't
189 # callable then we'll get a TypeError, and we're looking for NoneType
190 # in the message. If we get that we really want a
191 # BackendNotImplementedError
192 if str(e
) == "'NoneType' object is not callable":
193 raise BackendNotImplementedError(
194 'meta function for {} not implemented.'.format(backend
))
196 # Otherwise re-raise the error