[Piglit] [RFC 4/9] framework backends: treat backends more like plugins
Dylan Baker
baker.dylan.c at gmail.com
Mon Apr 6 14:30:14 PDT 2015
This allows backends to register classes and functions using a simple
Registry object, and then uses the existing helper functions to get
those objects for use, without needing to know the details.
---
framework/backends/__init__.py | 98 +++++++++++++++++++++----
framework/backends/json.py | 11 ++-
framework/backends/junit.py | 8 ++
framework/backends/{__init__.py => register.py} | 22 +-----
framework/programs/run.py | 7 +-
5 files changed, 107 insertions(+), 39 deletions(-)
copy framework/backends/{__init__.py => register.py} (66%)
diff --git a/framework/backends/__init__.py b/framework/backends/__init__.py
index eee088e..c1a3a0b 100644
--- a/framework/backends/__init__.py
+++ b/framework/backends/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2014 Intel Corporation
+# Copyright (c) 2014, 2015 Intel Corporation
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -18,22 +18,90 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
-""" Import public backend classes into one place """
+"""Provides a module like interface for backends.
-from . import json, junit
+This package provides an abstract interface for working with backends, which
+implement various functions as provided in the Registry class, and then provide
+a Registry instance as REGISTRY, which maps individual objects to objects that
+piglit expects to use. This module then provides a thin abstraction layer so
+that piglit is never aware of what backend it's using, it just asks for an
+object and receives one.
-# A list of available backends
-BACKENDS = ['json', 'junit']
+Most consumers will want to import framework.backends and work directly with
+the helper functions here. For some more detailed uses (test cases expecially)
+the modules themselves may be used.
+
+When this module is loaded it will search through framework/backends for python
+modules (those ending in .py), and attempt to import them. Then it will look
+for an attribute REGISTRY in those modules, and if it as a
+framework.register.Registry instance, it will add the name of that module (sans
+.py) as a key, and the instance as a value to the BACKENDS dictionary. Each of
+the helper functions in this module uses that dictionary to find the function
+that a user actually wants.
+
+"""
+
+import os
+import importlib
+
+from .register import Registry
+
+__all__ = [
+ 'BACKENDS',
+ 'BackendError',
+ 'BackendNotImplementedError',
+ 'get_backend',
+]
+
+
+class BackendError(Exception):
+ pass
+
+
+class BackendNotImplementedError(NotImplementedError):
+ pass
+
+
+def _register():
+ """Register backends.
+
+ Walk through the list of backends and register them to a name in a
+ dictionary, so that they can be referenced from helper functions.
+
+ """
+ registry = {}
+
+ for module in os.listdir(os.path.dirname(os.path.abspath(__file__))):
+ module, extension = os.path.splitext(module)
+ if extension == '.py':
+ mod = importlib.import_module('framework.backends.{}'.format(module))
+ if hasattr(mod, 'REGISTRY') and isinstance(mod.REGISTRY, Registry):
+ registry[module] = mod.REGISTRY
+
+ return registry
+
+
+BACKENDS = _register()
def get_backend(backend):
- """ Returns a BackendInstance based on the string passed """
- backends = {
- 'json': json.JSONBackend,
- 'junit': junit.JUnitBackend,
- }
-
- # Be sure that we're exporting the same list of backends that we actually
- # have available
- assert backends.keys() == BACKENDS
- return backends[backend]
+ """Returns a BackendInstance based on the string passed.
+
+ If the backend isn't a known module, then a BackendError will be raised, it
+ is the responsibility of the caller to handle this error.
+
+ If the backend module exists, but there is not active implementation then a
+ BackendNotImplementedError will be raised, it is also the responsiblity of
+ the caller to handle this error.
+
+ """
+ try:
+ inst = BACKENDS[backend].backend
+ except KeyError:
+ raise BackendError('Unknown backend: {}'.format(backend))
+
+ if inst is None:
+ raise BackendNotImplementedError(
+ 'Backend for {} is not implemented'.format(backend))
+
+ return inst
diff --git a/framework/backends/json.py b/framework/backends/json.py
index 347d3d7..7934ce9 100644
--- a/framework/backends/json.py
+++ b/framework/backends/json.py
@@ -31,14 +31,13 @@ except ImportError:
import framework.status as status
from .abstract import FileBackend
+from .register import Registry
__all__ = [
- 'CURRENT_JSON_VERSION',
+ 'REGISTRY',
'JSONBackend',
- 'piglit_encoder',
]
-
# The current version of the JSON results
CURRENT_JSON_VERSION = 5
@@ -145,3 +144,9 @@ class JSONBackend(FileBackend):
with open(t, 'w') as f:
json.dump({name: data}, f, default=piglit_encoder)
self._fsync(f)
+
+
+REGISTRY = Registry(
+ extensions=['', '.json'],
+ backend=JSONBackend,
+)
diff --git a/framework/backends/junit.py b/framework/backends/junit.py
index 3d32387..122944b 100644
--- a/framework/backends/junit.py
+++ b/framework/backends/junit.py
@@ -32,8 +32,10 @@ except ImportError:
import framework.grouptools as grouptools
from framework.core import PIGLIT_CONFIG
from .abstract import FileBackend
+from .register import Registry
__all__ = [
+ 'REGISTRY',
'JUnitBackend',
]
@@ -203,3 +205,9 @@ class JUnitBackend(FileBackend):
with open(t, 'w') as f:
f.write(etree.tostring(element))
self._fsync(f)
+
+
+REGISTRY = Registry(
+ extensions=['.xml'],
+ backend=JUnitBackend,
+)
diff --git a/framework/backends/__init__.py b/framework/backends/register.py
similarity index 66%
copy from framework/backends/__init__.py
copy to framework/backends/register.py
index eee088e..589a0a8 100644
--- a/framework/backends/__init__.py
+++ b/framework/backends/register.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2014 Intel Corporation
+# Copyright (c) 2015 Intel Corporation
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -18,22 +18,8 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
-""" Import public backend classes into one place """
+"""An object for registering backends."""
-from . import json, junit
+import collections
-# A list of available backends
-BACKENDS = ['json', 'junit']
-
-
-def get_backend(backend):
- """ Returns a BackendInstance based on the string passed """
- backends = {
- 'json': json.JSONBackend,
- 'junit': junit.JUnitBackend,
- }
-
- # Be sure that we're exporting the same list of backends that we actually
- # have available
- assert backends.keys() == BACKENDS
- return backends[backend]
+Registry = collections.namedtuple('Registry', ['extensions', 'backend'])
diff --git a/framework/programs/run.py b/framework/programs/run.py
index 8be6439..989ef45 100644
--- a/framework/programs/run.py
+++ b/framework/programs/run.py
@@ -78,9 +78,10 @@ def _default_backend():
"""
try:
backend = core.PIGLIT_CONFIG.get('core', 'backend')
- if backend not in backends.BACKENDS:
+ if backend not in backends.BACKENDS.keys():
print('Backend is not valid\n',
- 'valid backends are: {}'.format(' '.join(backends.BACKENDS)),
+ 'valid backends are: {}'.format(
+ ' '.join(backends.BACKENDS.keys())),
file=sys.stderr)
sys.exit(1)
return backend
@@ -127,7 +128,7 @@ def _run_parser(input_):
"(can be used more than once)")
parser.add_argument('-b', '--backend',
default=_default_backend(),
- choices=backends.BACKENDS,
+ choices=backends.BACKENDS.keys(),
help='select a results backend to use')
conc_parser = parser.add_mutually_exclusive_group()
conc_parser.add_argument('-c', '--all-concurrent',
--
2.3.5
More information about the Piglit
mailing list