~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/symbol_versioning.py

  • Committer: Martin Packman
  • Date: 2011-12-23 19:38:22 UTC
  • mto: This revision was merged to the branch mainline in revision 6405.
  • Revision ID: martin.packman@canonical.com-20111223193822-hesheea4o8aqwexv
Accept and document passing the medium rather than transport for smart connections

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007 Canonical Ltd
2
 
#   Authors: Robert Collins <robert.collins@canonical.com> and others
 
1
# Copyright (C) 2006-2010 Canonical Ltd
3
2
#
4
3
# This program is free software; you can redistribute it and/or modify
5
4
# it under the terms of the GNU General Public License as published by
13
12
#
14
13
# You should have received a copy of the GNU General Public License
15
14
# along with this program; if not, write to the Free Software
16
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
16
 
18
17
"""Symbol versioning
19
18
 
21
20
"""
22
21
 
23
22
__all__ = ['deprecated_function',
 
23
           'deprecated_in',
24
24
           'deprecated_list',
25
25
           'deprecated_method',
26
26
           'DEPRECATED_PARAMETER',
27
27
           'deprecated_passed',
28
 
           'warn', 'set_warning_method', 'zero_seven',
29
 
           'zero_eight',
30
 
           'zero_nine',
31
 
           'zero_ten',
32
 
           'zero_eleven',
33
 
           'zero_twelve',
34
 
           'zero_thirteen',
35
 
           'zero_fourteen',
36
 
           'zero_fifteen',
37
 
           'zero_sixteen',
38
 
           'zero_seventeen',
39
 
           'zero_eighteen',
 
28
           'set_warning_method',
 
29
           'warn',
40
30
           ]
41
31
 
 
32
 
 
33
import warnings
 
34
# Import the 'warn' symbol so bzrlib can call it even if we redefine it
42
35
from warnings import warn
43
36
 
 
37
import bzrlib
 
38
 
44
39
 
45
40
DEPRECATED_PARAMETER = "A deprecated parameter marker."
46
 
zero_seven = "%s was deprecated in version 0.7."
47
 
zero_eight = "%s was deprecated in version 0.8."
48
 
zero_nine = "%s was deprecated in version 0.9."
49
 
zero_ten = "%s was deprecated in version 0.10."
50
 
zero_eleven = "%s was deprecated in version 0.11."
51
 
zero_twelve = "%s was deprecated in version 0.12."
52
 
zero_thirteen = "%s was deprecated in version 0.13."
53
 
zero_fourteen = "%s was deprecated in version 0.14."
54
 
zero_fifteen = "%s was deprecated in version 0.15."
55
 
zero_sixteen = "%s was deprecated in version 0.16."
56
 
zero_seventeen = "%s was deprecated in version 0.17."
57
 
zero_eighteen = "%s was deprecated in version 0.18."
 
41
 
 
42
 
 
43
def deprecated_in(version_tuple):
 
44
    """Generate a message that something was deprecated in a release.
 
45
 
 
46
    >>> deprecated_in((1, 4, 0))
 
47
    '%s was deprecated in version 1.4.0.'
 
48
    """
 
49
    return ("%%s was deprecated in version %s."
 
50
            % bzrlib._format_version_tuple(version_tuple))
58
51
 
59
52
 
60
53
def set_warning_method(method):
80
73
        have a single %s operator in it. a_callable will be turned into a nice
81
74
        python symbol and then substituted into deprecation_version.
82
75
    """
 
76
    # We also want to handle old-style classes, in particular exception, and
 
77
    # they don't have an im_class attribute.
83
78
    if getattr(a_callable, 'im_class', None) is None:
84
79
        symbol = "%s.%s" % (a_callable.__module__,
85
80
                            a_callable.__name__)
96
91
 
97
92
    def function_decorator(callable):
98
93
        """This is the function python calls to perform the decoration."""
99
 
        
 
94
 
100
95
        def decorated_function(*args, **kwargs):
101
96
            """This is the decorated function."""
 
97
            from bzrlib import trace
 
98
            trace.mutter_callsite(4, "Deprecated function called")
102
99
            warn(deprecation_string(callable, deprecation_version),
103
100
                DeprecationWarning, stacklevel=2)
104
101
            return callable(*args, **kwargs)
110
107
 
111
108
def deprecated_method(deprecation_version):
112
109
    """Decorate a method so that use of it will trigger a warning.
113
 
    
 
110
 
 
111
    To deprecate a static or class method, use
 
112
 
 
113
        @staticmethod
 
114
        @deprecated_function
 
115
        def ...
 
116
 
114
117
    To deprecate an entire class, decorate __init__.
115
118
    """
116
119
 
117
120
    def method_decorator(callable):
118
121
        """This is the function python calls to perform the decoration."""
119
 
        
 
122
 
120
123
        def decorated_method(self, *args, **kwargs):
121
124
            """This is the decorated method."""
122
 
            symbol = "%s.%s.%s" % (self.__class__.__module__,
123
 
                                   self.__class__.__name__,
124
 
                                   callable.__name__
125
 
                                   )
 
125
            from bzrlib import trace
 
126
            if callable.__name__ == '__init__':
 
127
                symbol = "%s.%s" % (self.__class__.__module__,
 
128
                                    self.__class__.__name__,
 
129
                                    )
 
130
            else:
 
131
                symbol = "%s.%s.%s" % (self.__class__.__module__,
 
132
                                       self.__class__.__name__,
 
133
                                       callable.__name__
 
134
                                       )
 
135
            trace.mutter_callsite(4, "Deprecated method called")
126
136
            warn(deprecation_version % symbol, DeprecationWarning, stacklevel=2)
127
137
            return callable(self, *args, **kwargs)
128
138
        _populate_decorated(callable, deprecation_version, "method",
133
143
 
134
144
def deprecated_passed(parameter_value):
135
145
    """Return True if parameter_value was used."""
136
 
    # FIXME: it might be nice to have a parameter deprecation decorator. 
 
146
    # FIXME: it might be nice to have a parameter deprecation decorator.
137
147
    # it would need to handle positional and *args and **kwargs parameters,
138
148
    # which means some mechanism to describe how the parameter was being
139
149
    # passed before deprecation, and some way to deprecate parameters that
141
151
    # we cannot just forward to a new method name.I.e. in the following
142
152
    # examples we would want to have callers that pass any value to 'bad' be
143
153
    # given a warning - because we have applied:
144
 
    # @deprecated_parameter('bad', zero_seven)
 
154
    # @deprecated_parameter('bad', deprecated_in((1, 5, 0))
145
155
    #
146
156
    # def __init__(self, bad=None)
147
157
    # def __init__(self, bad, other)
159
169
    if len(docstring_lines) == 0:
160
170
        decorated_callable.__doc__ = deprecation_version % ("This " + label)
161
171
    elif len(docstring_lines) == 1:
162
 
        decorated_callable.__doc__ = (callable.__doc__ 
 
172
        decorated_callable.__doc__ = (callable.__doc__
163
173
                                    + "\n"
164
174
                                    + "\n"
165
175
                                    + deprecation_version % ("This " + label)
209
219
        ):
210
220
        """Create a dict that warns when read or modified.
211
221
 
212
 
        :param deprecation_version: something like zero_nine
 
222
        :param deprecation_version: string for the warning format to raise,
 
223
            typically from deprecated_in()
213
224
        :param initial_value: The contents of the dict
214
225
        :param variable_name: This allows better warnings to be printed
215
 
        :param advice: String of advice on what callers should do instead 
 
226
        :param advice: String of advice on what callers should do instead
216
227
            of using this variable.
217
228
        """
218
229
        self._deprecation_version = deprecation_version
234
245
                    initial_value, extra=None):
235
246
    """Create a list that warns when modified
236
247
 
237
 
    :param deprecation_version: something like zero_nine
 
248
    :param deprecation_version: string for the warning format to raise,
 
249
        typically from deprecated_in()
238
250
    :param initial_value: The contents of the list
239
251
    :param variable_name: This allows better warnings to be printed
240
252
    :param extra: Extra info to print when printing a warning
253
265
        def _warn_deprecated(self, func, *args, **kwargs):
254
266
            warn(msg, DeprecationWarning, stacklevel=3)
255
267
            return func(self, *args, **kwargs)
256
 
            
 
268
 
257
269
        def append(self, obj):
258
270
            """appending to %s is deprecated""" % (variable_name,)
259
271
            return self._warn_deprecated(list.append, obj)
271
283
            return self._warn_deprecated(list.remove, value)
272
284
 
273
285
        def pop(self, index=None):
274
 
            """pop'ing from from %s is deprecated""" % (variable_name,)
 
286
            """pop'ing from %s is deprecated""" % (variable_name,)
275
287
            if index:
276
288
                return self._warn_deprecated(list.pop, index)
277
289
            else:
279
291
                return self._warn_deprecated(list.pop)
280
292
 
281
293
    return _DeprecatedList(initial_value)
 
294
 
 
295
 
 
296
def _check_for_filter(error_only):
 
297
    """Check if there is already a filter for deprecation warnings.
 
298
 
 
299
    :param error_only: Only match an 'error' filter
 
300
    :return: True if a filter is found, False otherwise
 
301
    """
 
302
    for filter in warnings.filters:
 
303
        if issubclass(DeprecationWarning, filter[2]):
 
304
            # This filter will effect DeprecationWarning
 
305
            if not error_only or filter[0] == 'error':
 
306
                return True
 
307
    return False
 
308
 
 
309
 
 
310
def _remove_filter_callable(filter):
 
311
    """Build and returns a callable removing filter from the warnings.
 
312
 
 
313
    :param filter: The filter to remove (can be None).
 
314
 
 
315
    :return: A callable that will remove filter from warnings.filters.
 
316
    """
 
317
    def cleanup():
 
318
        if filter:
 
319
            warnings.filters.remove(filter)
 
320
    return cleanup
 
321
 
 
322
 
 
323
def suppress_deprecation_warnings(override=True):
 
324
    """Call this function to suppress all deprecation warnings.
 
325
 
 
326
    When this is a final release version, we don't want to annoy users with
 
327
    lots of deprecation warnings. We only want the deprecation warnings when
 
328
    running a dev or release candidate.
 
329
 
 
330
    :param override: If True, always set the ignore, if False, only set the
 
331
        ignore if there isn't already a filter.
 
332
 
 
333
    :return: A callable to remove the new warnings this added.
 
334
    """
 
335
    if not override and _check_for_filter(error_only=False):
 
336
        # If there is already a filter effecting suppress_deprecation_warnings,
 
337
        # then skip it.
 
338
        filter = None
 
339
    else:
 
340
        warnings.filterwarnings('ignore', category=DeprecationWarning)
 
341
        filter = warnings.filters[0]
 
342
    return _remove_filter_callable(filter)
 
343
 
 
344
 
 
345
def activate_deprecation_warnings(override=True):
 
346
    """Call this function to activate deprecation warnings.
 
347
 
 
348
    When running in a 'final' release we suppress deprecation warnings.
 
349
    However, the test suite wants to see them. So when running selftest, we
 
350
    re-enable the deprecation warnings.
 
351
 
 
352
    Note: warnings that have already been issued under 'ignore' will not be
 
353
    reported after this point. The 'warnings' module has already marked them as
 
354
    handled, so they don't get issued again.
 
355
 
 
356
    :param override: If False, only add a filter if there isn't an error filter
 
357
        already. (This slightly differs from suppress_deprecation_warnings, in
 
358
        because it always overrides everything but -Werror).
 
359
 
 
360
    :return: A callable to remove the new warnings this added.
 
361
    """
 
362
    if not override and _check_for_filter(error_only=True):
 
363
        # DeprecationWarnings are already turned into errors, don't downgrade
 
364
        # them to 'default'.
 
365
        filter = None
 
366
    else:
 
367
        warnings.filterwarnings('default', category=DeprecationWarning)
 
368
        filter = warnings.filters[0]
 
369
    return _remove_filter_callable(filter)