1# Copyright 2010 Google Inc.
2#
3# Permission is hereby granted, free of charge, to any person obtaining a
4# copy of this software and associated documentation files (the
5# "Software"), to deal in the Software without restriction, including
6# without limitation the rights to use, copy, modify, merge, publish, dis-
7# tribute, sublicense, and/or sell copies of the Software, and to permit
8# persons to whom the Software is furnished to do so, subject to the fol-
9# lowing conditions:
10#
11# The above copyright notice and this permission notice shall be included
12# in all copies or substantial portions of the Software.
13#
14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
16# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
17# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18# 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
20# IN THE SOFTWARE.
21
22
23"""
24Implements plugin related api.
25
26To define a new plugin just subclass Plugin, like this.
27
28class AuthPlugin(Plugin):
29    pass
30
31Then start creating subclasses of your new plugin.
32
33class MyFancyAuth(AuthPlugin):
34    capability = ['sign', 'vmac']
35
36The actual interface is duck typed.
37"""
38
39import glob
40import imp
41import os.path
42
43
44class Plugin(object):
45    """Base class for all plugins."""
46
47    capability = []
48
49    @classmethod
50    def is_capable(cls, requested_capability):
51        """Returns true if the requested capability is supported by this plugin
52        """
53        for c in requested_capability:
54            if c not in cls.capability:
55                return False
56        return True
57
58
59def get_plugin(cls, requested_capability=None):
60    if not requested_capability:
61        requested_capability = []
62    result = []
63    for handler in cls.__subclasses__():
64        if handler.is_capable(requested_capability):
65            result.append(handler)
66    return result
67
68
69def _import_module(filename):
70    (path, name) = os.path.split(filename)
71    (name, ext) = os.path.splitext(name)
72
73    (file, filename, data) = imp.find_module(name, [path])
74    try:
75        return imp.load_module(name, file, filename, data)
76    finally:
77        if file:
78            file.close()
79
80_plugin_loaded = False
81
82
83def load_plugins(config):
84    global _plugin_loaded
85    if _plugin_loaded:
86        return
87    _plugin_loaded = True
88
89    if not config.has_option('Plugin', 'plugin_directory'):
90        return
91    directory = config.get('Plugin', 'plugin_directory')
92    for file in glob.glob(os.path.join(directory, '*.py')):
93        _import_module(file)
94