~jk0/nova/xs-ipv6

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/python/deprecate.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.python.test.test_deprecate -*-
 
2
# Copyright (c) 2008-2010 Twisted Matrix Laboratories.
 
3
# See LICENSE for details.
 
4
 
 
5
"""
 
6
Deprecation framework for Twisted.
 
7
 
 
8
To mark a method or function as being deprecated do this::
 
9
 
 
10
    def badAPI(self, first, second):
 
11
        '''
 
12
        Docstring for badAPI.
 
13
        '''
 
14
        ...
 
15
    badAPI = deprecate(Version("Twisted", 8, 0, 0))(badAPI)
 
16
 
 
17
The newly-decorated badAPI will issue a warning when called. It will also have
 
18
a deprecation notice appended to its docstring.
 
19
 
 
20
To mark module-level attributes as being deprecated you can use::
 
21
 
 
22
    badAttribute = "someValue"
 
23
 
 
24
    ...
 
25
 
 
26
    deprecatedModuleAttribute(
 
27
        Version("Twisted", 8, 0, 0),
 
28
        "Use goodAttribute instead.",
 
29
        "your.full.module.name",
 
30
        "badAttribute")
 
31
 
 
32
The deprecated attributes will issue a warning whenever they are accessed. If
 
33
the attributes being deprecated are in the same module as the
 
34
L{deprecatedModuleAttribute} call is being made from, the C{__name__} global
 
35
can be used as the C{moduleName} parameter.
 
36
 
 
37
See also L{Version}.
 
38
 
 
39
@type DEPRECATION_WARNING_FORMAT: C{str}
 
40
@var DEPRECATION_WARNING_FORMAT: The default deprecation warning string format
 
41
    to use when one is not provided by the user.
 
42
"""
 
43
 
 
44
 
 
45
__all__ = [
 
46
    'deprecated',
 
47
    'getDeprecationWarningString',
 
48
    'getWarningMethod',
 
49
    'setWarningMethod',
 
50
    'deprecatedModuleAttribute',
 
51
    ]
 
52
 
 
53
 
 
54
import sys, inspect
 
55
from warnings import warn
 
56
 
 
57
from twisted.python.versions import getVersionString
 
58
from twisted.python.util import mergeFunctionMetadata
 
59
 
 
60
 
 
61
 
 
62
DEPRECATION_WARNING_FORMAT = '%(fqpn)s was deprecated in %(version)s'
 
63
 
 
64
 
 
65
# Notionally, part of twisted.python.reflect, but defining it there causes a
 
66
# cyclic dependency between this module and that module.  Define it here,
 
67
# instead, and let reflect import it to re-expose to the public.
 
68
def _fullyQualifiedName(obj):
 
69
    """
 
70
    Return the fully qualified name of a module, class, method or function.
 
71
    Classes and functions need to be module level ones to be correctly
 
72
    qualified.
 
73
 
 
74
    @rtype: C{str}.
 
75
    """
 
76
    name = obj.__name__
 
77
    if inspect.isclass(obj) or inspect.isfunction(obj):
 
78
        moduleName = obj.__module__
 
79
        return "%s.%s" % (moduleName, name)
 
80
    elif inspect.ismethod(obj):
 
81
        className = _fullyQualifiedName(obj.im_class)
 
82
        return "%s.%s" % (className, name)
 
83
    return name
 
84
# Try to keep it looking like something in twisted.python.reflect.
 
85
_fullyQualifiedName.__module__ = 'twisted.python.reflect'
 
86
_fullyQualifiedName.__name__ = 'fullyQualifiedName'
 
87
 
 
88
 
 
89
def getWarningMethod():
 
90
    """
 
91
    Return the warning method currently used to record deprecation warnings.
 
92
    """
 
93
    return warn
 
94
 
 
95
 
 
96
 
 
97
def setWarningMethod(newMethod):
 
98
    """
 
99
    Set the warning method to use to record deprecation warnings.
 
100
 
 
101
    The callable should take message, category and stacklevel. The return
 
102
    value is ignored.
 
103
    """
 
104
    global warn
 
105
    warn = newMethod
 
106
 
 
107
 
 
108
 
 
109
def _getDeprecationDocstring(version):
 
110
    return "Deprecated in %s." % getVersionString(version)
 
111
 
 
112
 
 
113
 
 
114
def _getDeprecationWarningString(fqpn, version, format=None):
 
115
    """
 
116
    Return a string indicating that the Python name was deprecated in the given
 
117
    version.
 
118
 
 
119
    @type fqpn: C{str}
 
120
    @param fqpn: Fully qualified Python name of the thing being deprecated
 
121
 
 
122
    @type version: L{twisted.python.versions.Version}
 
123
    @param version: Version that C{fqpn} was deprecated in
 
124
 
 
125
    @type format: C{str}
 
126
    @param format: A user-provided format to interpolate warning values into,
 
127
        or L{DEPRECATION_WARNING_FORMAT} if C{None} is given
 
128
 
 
129
    @rtype: C{str}
 
130
    @return: A textual description of the deprecation
 
131
    """
 
132
    if format is None:
 
133
        format = DEPRECATION_WARNING_FORMAT
 
134
    return format % {
 
135
        'fqpn': fqpn,
 
136
        'version': getVersionString(version)}
 
137
 
 
138
 
 
139
 
 
140
def getDeprecationWarningString(callableThing, version, format=None):
 
141
    """
 
142
    Return a string indicating that the callable was deprecated in the given
 
143
    version.
 
144
 
 
145
    @type callableThing: C{callable}
 
146
    @param callableThing: Callable object to be deprecated
 
147
 
 
148
    @type version: L{twisted.python.versions.Version}
 
149
    @param version: Version that C{fqpn} was deprecated in
 
150
 
 
151
    @type format: C{str}
 
152
    @param format: A user-provided format to interpolate warning values into,
 
153
        or L{DEPRECATION_WARNING_FORMAT} if C{None} is given
 
154
 
 
155
    @rtype: C{str}
 
156
    @return: A textual description of the deprecation
 
157
    """
 
158
    return _getDeprecationWarningString(
 
159
        _fullyQualifiedName(callableThing), version, format)
 
160
 
 
161
 
 
162
 
 
163
def deprecated(version):
 
164
    """
 
165
    Return a decorator that marks callables as deprecated.
 
166
 
 
167
    @type version: L{twisted.python.versions.Version}
 
168
    @param version: The version in which the callable will be marked as
 
169
        having been deprecated.  The decorated function will be annotated
 
170
        with this version, having it set as its C{deprecatedVersion}
 
171
        attribute.
 
172
    """
 
173
    def deprecationDecorator(function):
 
174
        """
 
175
        Decorator that marks C{function} as deprecated.
 
176
        """
 
177
        warningString = getDeprecationWarningString(function, version)
 
178
 
 
179
        def deprecatedFunction(*args, **kwargs):
 
180
            warn(
 
181
                warningString,
 
182
                DeprecationWarning,
 
183
                stacklevel=2)
 
184
            return function(*args, **kwargs)
 
185
 
 
186
        deprecatedFunction = mergeFunctionMetadata(
 
187
            function, deprecatedFunction)
 
188
        _appendToDocstring(deprecatedFunction,
 
189
                           _getDeprecationDocstring(version))
 
190
        deprecatedFunction.deprecatedVersion = version
 
191
        return deprecatedFunction
 
192
 
 
193
    return deprecationDecorator
 
194
 
 
195
 
 
196
 
 
197
def _appendToDocstring(thingWithDoc, textToAppend):
 
198
    """
 
199
    Append the given text to the docstring of C{thingWithDoc}.
 
200
 
 
201
    If C{thingWithDoc} has no docstring, then the text just replaces the
 
202
    docstring. If it has a single-line docstring then it appends a blank line
 
203
    and the message text. If it has a multi-line docstring, then in appends a
 
204
    blank line a the message text, and also does the indentation correctly.
 
205
    """
 
206
    if thingWithDoc.__doc__:
 
207
        docstringLines = thingWithDoc.__doc__.splitlines()
 
208
    else:
 
209
        docstringLines = []
 
210
 
 
211
    if len(docstringLines) == 0:
 
212
        docstringLines.append(textToAppend)
 
213
    elif len(docstringLines) == 1:
 
214
        docstringLines.extend(['', textToAppend, ''])
 
215
    else:
 
216
        spaces = docstringLines.pop()
 
217
        docstringLines.extend(['',
 
218
                               spaces + textToAppend,
 
219
                               spaces])
 
220
    thingWithDoc.__doc__ = '\n'.join(docstringLines)
 
221
 
 
222
 
 
223
 
 
224
class _ModuleProxy(object):
 
225
    """
 
226
    Python module wrapper to hook module-level attribute access.
 
227
 
 
228
    Access to deprecated attributes first checks L{_deprecatedAttributes}, if
 
229
    the attribute does not appear there then access falls through to L{_module},
 
230
    the wrapped module object.
 
231
 
 
232
    @type _module: C{module}
 
233
    @ivar _module: Module on which to hook attribute access.
 
234
 
 
235
    @type _deprecatedAttributes: C{dict} mapping C{str} to
 
236
        L{_DeprecatedAttribute}
 
237
    @ivar _deprecatedAttributes: Mapping of attribute names to objects that
 
238
        retrieve the module attribute's original value.
 
239
    """
 
240
    def __init__(self, module):
 
241
        object.__setattr__(self, '_module', module)
 
242
        object.__setattr__(self, '_deprecatedAttributes', {})
 
243
 
 
244
 
 
245
    def __repr__(self):
 
246
        """
 
247
        Get a string containing the type of the module proxy and a
 
248
        representation of the wrapped module object.
 
249
        """
 
250
        _module = object.__getattribute__(self, '_module')
 
251
        return '<%s module=%r>' % (
 
252
            type(self).__name__,
 
253
            _module)
 
254
 
 
255
 
 
256
    def __setattr__(self, name, value):
 
257
        """
 
258
        Set an attribute on the wrapped module object.
 
259
        """
 
260
        _module = object.__getattribute__(self, '_module')
 
261
        setattr(_module, name, value)
 
262
 
 
263
 
 
264
    def __getattribute__(self, name):
 
265
        """
 
266
        Get an attribute on the wrapped module object.
 
267
 
 
268
        If the specified name has been deprecated then a warning is issued.
 
269
        """
 
270
        _module = object.__getattribute__(self, '_module')
 
271
        _deprecatedAttributes = object.__getattribute__(
 
272
            self, '_deprecatedAttributes')
 
273
 
 
274
        getter = _deprecatedAttributes.get(name)
 
275
        if getter is not None:
 
276
            value = getter.get()
 
277
        else:
 
278
            value = getattr(_module, name)
 
279
        return value
 
280
 
 
281
 
 
282
 
 
283
class _DeprecatedAttribute(object):
 
284
    """
 
285
    Wrapper for deprecated attributes.
 
286
 
 
287
    This is intended to be used by L{_ModuleProxy}. Calling
 
288
    L{_DeprecatedAttribute.get} will issue a warning and retrieve the
 
289
    underlying attribute's value.
 
290
 
 
291
    @type module: C{module}
 
292
    @ivar module: The original module instance containing this attribute
 
293
 
 
294
    @type fqpn: C{str}
 
295
    @ivar fqpn: Fully qualified Python name for the deprecated attribute
 
296
 
 
297
    @type version: L{twisted.python.versions.Version}
 
298
    @ivar version: Version that the attribute was deprecated in
 
299
 
 
300
    @type message: C{str}
 
301
    @ivar message: Deprecation message
 
302
    """
 
303
    def __init__(self, module, name, version, message):
 
304
        """
 
305
        Initialise a deprecated name wrapper.
 
306
        """
 
307
        self.module = module
 
308
        self.__name__ = name
 
309
        self.fqpn = module.__name__ + '.' + name
 
310
        self.version = version
 
311
        self.message = message
 
312
 
 
313
 
 
314
    def get(self):
 
315
        """
 
316
        Get the underlying attribute value and issue a deprecation warning.
 
317
        """
 
318
        message = _getDeprecationWarningString(self.fqpn, self.version,
 
319
            DEPRECATION_WARNING_FORMAT + ': ' + self.message)
 
320
        warn(message, DeprecationWarning, stacklevel=3)
 
321
        return getattr(self.module, self.__name__)
 
322
 
 
323
 
 
324
 
 
325
def _deprecateAttribute(proxy, name, version, message):
 
326
    """
 
327
    Mark a module-level attribute as being deprecated.
 
328
 
 
329
    @type proxy: L{_ModuleProxy}
 
330
    @param proxy: The module proxy instance proxying the deprecated attributes
 
331
 
 
332
    @type name: C{str}
 
333
    @param name: Attribute name
 
334
 
 
335
    @type version: L{twisted.python.versions.Version}
 
336
    @param version: Version that the attribute was deprecated in
 
337
 
 
338
    @type message: C{str}
 
339
    @param message: Deprecation message
 
340
    """
 
341
    _module = object.__getattribute__(proxy, '_module')
 
342
    attr = _DeprecatedAttribute(_module, name, version, message)
 
343
    # Add a deprecated attribute marker for this module's attribute. When this
 
344
    # attribute is accessed via _ModuleProxy a warning is emitted.
 
345
    _deprecatedAttributes = object.__getattribute__(
 
346
        proxy, '_deprecatedAttributes')
 
347
    _deprecatedAttributes[name] = attr
 
348
 
 
349
 
 
350
 
 
351
def deprecatedModuleAttribute(version, message, moduleName, name):
 
352
    """
 
353
    Declare a module-level attribute as being deprecated.
 
354
 
 
355
    @type version: L{twisted.python.versions.Version}
 
356
    @param version: Version that the attribute was deprecated in
 
357
 
 
358
    @type message: C{str}
 
359
    @param message: Deprecation message
 
360
 
 
361
    @type moduleName: C{str}
 
362
    @param moduleName: Fully-qualified Python name of the module containing
 
363
        the deprecated attribute; if called from the same module as the
 
364
        attributes are being deprecated in, using the C{__name__} global can
 
365
        be helpful
 
366
 
 
367
    @type name: C{str}
 
368
    @param name: Attribute name to deprecate
 
369
    """
 
370
    module = sys.modules[moduleName]
 
371
    if not isinstance(module, _ModuleProxy):
 
372
        module = _ModuleProxy(module)
 
373
        sys.modules[moduleName] = module
 
374
 
 
375
    _deprecateAttribute(module, name, version, message)