~certify-web-dev/twisted/certify-staging

« back to all changes in this revision

Viewing changes to twisted/python/plugin.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2010-01-02 19:38:17 UTC
  • mfrom: (2.2.4 sid)
  • Revision ID: james.westby@ubuntu.com-20100102193817-jphp464ppwh7dulg
Tags: 9.0.0-1
* python-twisted: Depend on the python-twisted-* 9.0 packages.
* python-twisted: Depend on python-zope.interface only. Closes: #557781.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
2
 
# See LICENSE for details.
3
 
 
4
 
 
5
 
from __future__ import nested_scopes
6
 
 
7
 
# System Imports
8
 
import sys
9
 
import os
10
 
import errno
11
 
import types
12
 
import warnings
13
 
 
14
 
# Twisted imports
15
 
from twisted.python import util
16
 
 
17
 
# Sibling Imports
18
 
from reflect import namedModule
19
 
 
20
 
try:
21
 
    from os.path import realpath as cacheTransform
22
 
except ImportError:
23
 
    from os.path import abspath as cacheTransform
24
 
 
25
 
class PlugIn:
26
 
    """I am a Python module registered in a plugins.tml file.
27
 
    """
28
 
    def __init__(self, name, module, **kw):
29
 
        self.name = name
30
 
        self.module = module
31
 
        for key, value in kw.items():
32
 
            setattr(self, key, value)
33
 
 
34
 
    def isLoaded(self):
35
 
        """Check to see if the module for this plugin has been imported yet.
36
 
 
37
 
        @rtype: C{int}
38
 
        @return: A true value if the module for this plugin has been loaded,
39
 
        false otherwise.
40
 
        """
41
 
        return sys.modules.has_key(self.module)
42
 
 
43
 
    def load(self):
44
 
        """Load the module for this plugin.
45
 
 
46
 
        @rtype: C{ModuleType}
47
 
        @return: The module object that is loaded.
48
 
        """
49
 
        return namedModule(self.module)
50
 
 
51
 
    def __repr__(self):
52
 
        if self.isLoaded():
53
 
            loaded = ' loaded'
54
 
        else:
55
 
            loaded = ''
56
 
        return "<Plugin %s %s%s>" % (repr(self.name), self.module, loaded)
57
 
 
58
 
class DropIn:
59
 
    """I am a Python package containing plugins.tml.
60
 
    """
61
 
    def __init__(self, name):
62
 
        self.name = name
63
 
        self.plugins = []
64
 
 
65
 
    def register(self, name, module, **kw):
66
 
        """Register a new plug-in.
67
 
        """
68
 
        warnings.warn("The twisted.python.plugin system is deprecated.  "
69
 
                      "See twisted.plugin for the revised edition.",
70
 
                      DeprecationWarning, 2)
71
 
        self.plugins.append(PlugIn(name, module, **kw))
72
 
 
73
 
    def __repr__(self):
74
 
        return "<Package %s %s>" % (self.name, self.plugins)
75
 
 
76
 
 
77
 
def _prepCallbacks(debug, progress):
78
 
    if debug:
79
 
        try:
80
 
            debug('Looking for plugin.tml files')
81
 
        except:
82
 
            debug = lambda x: sys.stdout.write(x + '\n')
83
 
            debug('Looking for plugin.tml files')
84
 
    else:
85
 
        debug = lambda x: None
86
 
    if progress:
87
 
        try:
88
 
            progress(0.0)
89
 
        except:
90
 
            pb = util.makeStatusBar(76)
91
 
            progress = lambda x, pb=pb: sys.stdout.write(pb(x) + '\r')
92
 
            progress(0.0)
93
 
    else:
94
 
        progress = lambda x: None
95
 
    return debug, progress
96
 
 
97
 
def getPluginFileList(debugInspection=None, showProgress=None):
98
 
    """Find plugin.tml files in subdirectories of paths in C{sys.path}
99
 
 
100
 
    @type debugInspection: C{None} or a callable taking one argument
101
 
    @param debugInspection: If not None, this is invoked with strings containing
102
 
    debug information about the loading process.  If it is any other true value,
103
 
    this debug information is written to stdout (This behavior is deprecated).
104
 
 
105
 
    @type showProgress: C{None} or a callable taking one argument.
106
 
    @param showProgress: If not None, this is invoked with floating point
107
 
    values between 0 and 1 describing the progress of the loading process.
108
 
    If it is any other true value, this progress information is written to
109
 
    stdout.  (This behavior is deprecated).
110
 
 
111
 
    @rtype: C{list} of C{str}
112
 
    @return: A list of the plugin.tml files found.
113
 
    """
114
 
    if isinstance(debugInspection, types.IntType):
115
 
        warnings.warn(
116
 
            "int parameter for debugInspection is deprecated, pass None or "
117
 
            "a function that takes a single argument instead.",
118
 
            DeprecationWarning, 2
119
 
        )
120
 
    if isinstance(showProgress, types.IntType):
121
 
        warnings.warn(
122
 
            "int parameter for showProgress is deprecated, pass None or "
123
 
            "a function that takes a single argument instead.",
124
 
            DeprecationWarning, 2
125
 
        )
126
 
    debugInspection, showProgress = _prepCallbacks(debugInspection, showProgress)
127
 
    exists = os.path.exists
128
 
    join = os.sep.join
129
 
    result = []
130
 
    loaded = {}
131
 
    seenNames = {}
132
 
 
133
 
    # XXX Some people claim to have found non-strings in sys.path (an empty
134
 
    # list, in particular).  Instead of tracking down the cause for their
135
 
    # presence, they decided it was better to discard them unconditionally
136
 
    # without further investigation.  At some point, someone should track
137
 
    # down where non-strings are coming from and do something about them.
138
 
    paths = [cacheTransform(p) for p in sys.path
139
 
             if isinstance(p, str) and os.path.isdir(p)]
140
 
 
141
 
    # special case for commonly used directories we *know* shouldn't be checked
142
 
    # and really slow down mktap and such-like in real installations
143
 
    for p in ("/usr/bin", "/usr/local/bin"):
144
 
        try:
145
 
            paths.remove(p)
146
 
        except ValueError:
147
 
            pass
148
 
    progress = 0.0
149
 
    increments = 1.0 / len(paths)
150
 
 
151
 
    for (index, d) in zip(range(len(paths)), paths):
152
 
        showProgress(progress)
153
 
        if loaded.has_key(d):
154
 
            debugInspection('Already saw ' + d)
155
 
            continue
156
 
        else:
157
 
            debugInspection('Recursing through ' + d)
158
 
        try:
159
 
            subDirs = os.listdir(d)
160
 
        except OSError, (err, s):
161
 
            # Permission denied, carry on
162
 
            if err == errno.EACCES:
163
 
                debugInspection('Permission denied on ' + d)
164
 
            else:
165
 
                raise
166
 
        else:
167
 
            # filter out files we obviously don't need to check - ones with '.' in them
168
 
            subDirs = [s for s in subDirs if "." not in s]
169
 
            if not subDirs:
170
 
                continue
171
 
            incr = increments * (1.0 / len(subDirs))
172
 
            for plugindir in subDirs:
173
 
                if seenNames.has_key(plugindir):
174
 
                    debugInspection('Seen %s already' % plugindir)
175
 
                    continue
176
 
                tmlname = join((d, plugindir, "plugins.tml"))
177
 
                if isAModule(join((d,plugindir))):
178
 
                    seenNames[plugindir] = 1
179
 
                    if exists(tmlname):
180
 
                        result.append(tmlname)
181
 
                        debugInspection('Found ' + tmlname)
182
 
                    else:
183
 
                        debugInspection('Failed ' + tmlname)
184
 
                else:
185
 
                    debugInspection('Not a module ' + tmlname)
186
 
                progress = progress + incr
187
 
                showProgress(progress)
188
 
 
189
 
    showProgress(1.0)
190
 
    return result
191
 
 
192
 
def loadPlugins(plugInType, fileList, debugInspection=None, showProgress=None):
193
 
    """Traverse the given list of files and attempt to load plugins from them.
194
 
 
195
 
    @type plugInType: C{str}
196
 
    @param plugInType: The type of plugin to search for.  This is tested
197
 
    against the C{type} argument to the C{register} function in the
198
 
    plugin.tml files.
199
 
 
200
 
    @type fileList: C{list} of C{str}
201
 
    @param fileList: A list of the files to attempt to load plugin
202
 
    information from.  One name is put in their scope, the C{register}
203
 
    function.
204
 
 
205
 
    @type debugInspection: C{None} or a callable taking one argument
206
 
    @param debugInspection: If not None, this is invoked with strings containing
207
 
    debug information about the loading process.  If it is any other true value,
208
 
    this debug information is written to stdout (This behavior is deprecated).
209
 
 
210
 
    @type showProgress: C{None} or a callable taking one argument.
211
 
    @param showProgress: If not None, this is invoked with floating point
212
 
    values between 0 and 1 describing the progress of the loading process.
213
 
    If it is any other true value, this progress information is written to
214
 
    stdout.  (This behavior is deprecated).
215
 
 
216
 
    @rtype: C{list}
217
 
    @return: A list of the C{PlugIn} objects found.
218
 
    """
219
 
    if isinstance(debugInspection, types.IntType):
220
 
        warnings.warn(
221
 
            "int parameter for debugInspection is deprecated, pass None or "
222
 
            "a function that takes a single argument instead.",
223
 
            DeprecationWarning, 4
224
 
        )
225
 
    if isinstance(showProgress, types.IntType):
226
 
        warnings.warn(
227
 
            "int parameter for showProgress is deprecated, pass None or "
228
 
            "a function that takes a single argument instead.",
229
 
            DeprecationWarning, 4
230
 
        )
231
 
    result = []
232
 
    debugInspection, showProgress = _prepCallbacks(debugInspection, showProgress)
233
 
 
234
 
    if not fileList:
235
 
        raise ValueError("No plugins passed to loadPlugins")
236
 
 
237
 
    increments = 1.0 / len(fileList)
238
 
    progress = 0.0
239
 
 
240
 
    for (index, tmlFile) in zip(range(len(fileList)), fileList):
241
 
        showProgress(progress)
242
 
        debugInspection("Loading from " + tmlFile)
243
 
        pname = os.path.split(os.path.abspath(tmlFile))[-2]
244
 
        dropin = DropIn(pname)
245
 
        ns = {'register': dropin.register, '__file__': tmlFile}
246
 
        try:
247
 
            execfile(tmlFile, ns)
248
 
        except (IOError, OSError), e:
249
 
            # guess we don't have permissions for that
250
 
            debugInspection("Error loading: %s" % e)
251
 
            continue
252
 
 
253
 
        ldp = len(dropin.plugins) or 1.0
254
 
        incr = increments * (1.0 / ldp)
255
 
        for plugin in dropin.plugins:
256
 
            if plugInType == plugin.type:
257
 
                result.append(plugin)
258
 
                debugInspection("Found %r" % (plugin,))
259
 
            else:
260
 
                debugInspection("Disqualified %r" % (plugin,))
261
 
            progress = progress + incr
262
 
            showProgress(progress)
263
 
        debugInspection("Finished loading from %s!" % tmlFile)
264
 
 
265
 
    showProgress(1.0)
266
 
    debugInspection("Returning %r" % (result,))
267
 
    return result
268
 
 
269
 
def getPlugIns(plugInType, debugInspection=None, showProgress=None):
270
 
    """Helper function to get all the plugins of a particular type.
271
 
 
272
 
    @type plugInType: C{str}
273
 
    @param plugInType: The type of plugin to search for.  This is tested
274
 
    against the C{type} argument to the C{register} function in the
275
 
    plugin.tml files.
276
 
 
277
 
    @type debugInspection: C{None} or a callable taking one argument
278
 
    @param debugInspection: If not None, this is invoked with strings containing
279
 
    debug information about the loading process.  If it is any other true value,
280
 
    this debug information is written to stdout (This behavior is deprecated).
281
 
 
282
 
    @type showProgress: C{None} or a callable taking one argument.
283
 
    @param showProgress: If not None, this is invoked with floating point
284
 
    values between 0 and 1 describing the progress of the loading process.
285
 
    If it is any other true value, this progress information is written to
286
 
    stdout.  (This behavior is deprecated).
287
 
 
288
 
    @rtype: C{list}
289
 
    @return: A list of C{PlugIn} objects that were found.
290
 
    """
291
 
    warnings.warn("The twisted.python.plugin system is deprecated.  "
292
 
                  "See twisted.plugin for the revised edition.",
293
 
                  DeprecationWarning, 2)
294
 
    return _getPlugIns(plugInType, debugInspection, showProgress)
295
 
 
296
 
def _getPlugIns(plugInType, debugInspection=None, showProgress=None):
297
 
    if isinstance(debugInspection, types.IntType):
298
 
        warnings.warn(
299
 
            "int parameter for debugInspection is deprecated, pass None or "
300
 
            "a function that takes a single argument instead.",
301
 
            DeprecationWarning, 3
302
 
        )
303
 
    if isinstance(showProgress, types.IntType):
304
 
        warnings.warn(
305
 
            "int parameter for showProgress is deprecated, pass None or "
306
 
            "a function that takes a single argument instead.",
307
 
            DeprecationWarning, 3
308
 
        )
309
 
    debugInspection, showProgress = _prepCallbacks(debugInspection, showProgress)
310
 
 
311
 
    firstHalf = secondHalf = lambda x: None
312
 
    if showProgress:
313
 
        firstHalf = lambda x: showProgress(x / 2.0)
314
 
        secondHalf = lambda x: showProgress(x / 2.0 + 0.5)
315
 
 
316
 
    tmlFiles = getPluginFileList(debugInspection, firstHalf)
317
 
    if not tmlFiles:
318
 
        return []
319
 
    return loadPlugins(plugInType, tmlFiles, debugInspection, secondHalf)
320
 
 
321
 
def isAModule(d):
322
 
    """This function checks the directory for __init__ files.
323
 
    """
324
 
    suffixes = ['py', 'pyc', 'pyo', 'so', 'pyd', 'dll']
325
 
    exists = os.path.exists
326
 
    join = os.sep.join
327
 
    for s in suffixes: # bad algorithm, but probably works
328
 
        if exists(join((d,'__init__.%s' % s))):
329
 
            return 1
330
 
    return 0
331
 
 
332
 
__all__ = ['PlugIn', 'DropIn', 'getPluginFileList', 'loadPlugins', 'getPlugIns']