/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/symbol_versioning.py

  • Committer: Martin Pool
  • Date: 2007-10-03 08:06:44 UTC
  • mto: This revision was merged to the branch mainline in revision 2901.
  • Revision ID: mbp@sourcefrog.net-20071003080644-oivy0gkg98sex0ed
Avoid internal error tracebacks on failure to lock on readonly transport (#129701).

Add new LockFailed, which doesn't imply that we failed to get it because of
contention.  Raise this if we fail to create the pending or lock directories
because of Transport errors.

UnlockableTransport is not an internal error.

ReadOnlyLockError has a message which didn't match its name or usage; it's now
deprecated and callers are updated to use LockFailed which is more appropriate.

Add zero_ninetytwo deprecation symbol.

Unify assertMatchesRe with TestCase.assertContainsRe.

When the constructor is deprecated, just say that the class is deprecated, not
the __init__ method - this works better with applyDeprecated in tests.

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
 
3
#
 
4
# This program is free software; you can redistribute it and/or modify
 
5
# it under the terms of the GNU General Public License as published by
 
6
# the Free Software Foundation; either version 2 of the License, or
 
7
# (at your option) any later version.
 
8
#
 
9
# This program is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU General Public License
 
15
# along with this program; if not, write to the Free Software
 
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
17
 
 
18
"""Symbol versioning
 
19
 
 
20
The methods here allow for api symbol versioning.
 
21
"""
 
22
 
 
23
__all__ = ['deprecated_function',
 
24
           'deprecated_list',
 
25
           'deprecated_method',
 
26
           'DEPRECATED_PARAMETER',
 
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',
 
40
           'zero_ninety',
 
41
           'zero_ninetyone',
 
42
           'zero_ninetytwo',
 
43
           ]
 
44
 
 
45
from warnings import warn
 
46
 
 
47
 
 
48
DEPRECATED_PARAMETER = "A deprecated parameter marker."
 
49
zero_seven = "%s was deprecated in version 0.7."
 
50
zero_eight = "%s was deprecated in version 0.8."
 
51
zero_nine = "%s was deprecated in version 0.9."
 
52
zero_ten = "%s was deprecated in version 0.10."
 
53
zero_eleven = "%s was deprecated in version 0.11."
 
54
zero_twelve = "%s was deprecated in version 0.12."
 
55
zero_thirteen = "%s was deprecated in version 0.13."
 
56
zero_fourteen = "%s was deprecated in version 0.14."
 
57
zero_fifteen = "%s was deprecated in version 0.15."
 
58
zero_sixteen = "%s was deprecated in version 0.16."
 
59
zero_seventeen = "%s was deprecated in version 0.17."
 
60
zero_eighteen = "%s was deprecated in version 0.18."
 
61
zero_ninety = "%s was deprecated in version 0.90."
 
62
zero_ninetyone = "%s was deprecated in version 0.91."
 
63
zero_ninetytwo = "%s was deprecated in version 0.92."
 
64
 
 
65
 
 
66
def set_warning_method(method):
 
67
    """Set the warning method to be used by this module.
 
68
 
 
69
    It should take a message and a warning category as warnings.warn does.
 
70
    """
 
71
    global warn
 
72
    warn = method
 
73
 
 
74
 
 
75
# TODO - maybe this would be easier to use as one 'smart' method that
 
76
# guess if it is a method or a class or an attribute ? If so, we can
 
77
# add that on top of the primitives, once we have all three written
 
78
# - RBC 20050105
 
79
 
 
80
 
 
81
def deprecation_string(a_callable, deprecation_version):
 
82
    """Generate an automatic deprecation string for a_callable.
 
83
 
 
84
    :param a_callable: The callable to substitute into deprecation_version.
 
85
    :param deprecation_version: A deprecation format warning string. This should
 
86
        have a single %s operator in it. a_callable will be turned into a nice
 
87
        python symbol and then substituted into deprecation_version.
 
88
    """
 
89
    # We also want to handle old-style classes, in particular exception, and
 
90
    # they don't have an im_class attribute.
 
91
    if getattr(a_callable, 'im_class', None) is None:
 
92
        symbol = "%s.%s" % (a_callable.__module__,
 
93
                            a_callable.__name__)
 
94
    else:
 
95
        symbol = "%s.%s.%s" % (a_callable.im_class.__module__,
 
96
                               a_callable.im_class.__name__,
 
97
                               a_callable.__name__
 
98
                               )
 
99
    return deprecation_version % symbol
 
100
 
 
101
 
 
102
def deprecated_function(deprecation_version):
 
103
    """Decorate a function so that use of it will trigger a warning."""
 
104
 
 
105
    def function_decorator(callable):
 
106
        """This is the function python calls to perform the decoration."""
 
107
        
 
108
        def decorated_function(*args, **kwargs):
 
109
            """This is the decorated function."""
 
110
            warn(deprecation_string(callable, deprecation_version),
 
111
                DeprecationWarning, stacklevel=2)
 
112
            return callable(*args, **kwargs)
 
113
        _populate_decorated(callable, deprecation_version, "function",
 
114
                            decorated_function)
 
115
        return decorated_function
 
116
    return function_decorator
 
117
 
 
118
 
 
119
def deprecated_method(deprecation_version):
 
120
    """Decorate a method so that use of it will trigger a warning.
 
121
 
 
122
    To deprecate a static or class method, use 
 
123
 
 
124
        @staticmethod
 
125
        @deprecated_function
 
126
        def ...
 
127
    
 
128
    To deprecate an entire class, decorate __init__.
 
129
    """
 
130
 
 
131
    def method_decorator(callable):
 
132
        """This is the function python calls to perform the decoration."""
 
133
        
 
134
        def decorated_method(self, *args, **kwargs):
 
135
            """This is the decorated method."""
 
136
            if callable.__name__ == '__init__':
 
137
                symbol = "%s.%s" % (self.__class__.__module__,
 
138
                                    self.__class__.__name__,
 
139
                                    )
 
140
            else:
 
141
                symbol = "%s.%s.%s" % (self.__class__.__module__,
 
142
                                       self.__class__.__name__,
 
143
                                       callable.__name__
 
144
                                       )
 
145
            warn(deprecation_version % symbol, DeprecationWarning, stacklevel=2)
 
146
            return callable(self, *args, **kwargs)
 
147
        _populate_decorated(callable, deprecation_version, "method",
 
148
                            decorated_method)
 
149
        return decorated_method
 
150
    return method_decorator
 
151
 
 
152
 
 
153
def deprecated_passed(parameter_value):
 
154
    """Return True if parameter_value was used."""
 
155
    # FIXME: it might be nice to have a parameter deprecation decorator. 
 
156
    # it would need to handle positional and *args and **kwargs parameters,
 
157
    # which means some mechanism to describe how the parameter was being
 
158
    # passed before deprecation, and some way to deprecate parameters that
 
159
    # were not at the end of the arg list. Thats needed for __init__ where
 
160
    # we cannot just forward to a new method name.I.e. in the following
 
161
    # examples we would want to have callers that pass any value to 'bad' be
 
162
    # given a warning - because we have applied:
 
163
    # @deprecated_parameter('bad', zero_seven)
 
164
    #
 
165
    # def __init__(self, bad=None)
 
166
    # def __init__(self, bad, other)
 
167
    # def __init__(self, **kwargs)
 
168
    # RBC 20060116
 
169
    return not parameter_value is DEPRECATED_PARAMETER
 
170
 
 
171
 
 
172
def _decorate_docstring(callable, deprecation_version, label,
 
173
                        decorated_callable):
 
174
    if callable.__doc__:
 
175
        docstring_lines = callable.__doc__.split('\n')
 
176
    else:
 
177
        docstring_lines = []
 
178
    if len(docstring_lines) == 0:
 
179
        decorated_callable.__doc__ = deprecation_version % ("This " + label)
 
180
    elif len(docstring_lines) == 1:
 
181
        decorated_callable.__doc__ = (callable.__doc__ 
 
182
                                    + "\n"
 
183
                                    + "\n"
 
184
                                    + deprecation_version % ("This " + label)
 
185
                                    + "\n")
 
186
    else:
 
187
        spaces = len(docstring_lines[-1])
 
188
        new_doc = callable.__doc__
 
189
        new_doc += "\n" + " " * spaces
 
190
        new_doc += deprecation_version % ("This " + label)
 
191
        new_doc += "\n" + " " * spaces
 
192
        decorated_callable.__doc__ = new_doc
 
193
 
 
194
 
 
195
def _populate_decorated(callable, deprecation_version, label,
 
196
                        decorated_callable):
 
197
    """Populate attributes like __name__ and __doc__ on the decorated callable.
 
198
    """
 
199
    _decorate_docstring(callable, deprecation_version, label,
 
200
                        decorated_callable)
 
201
    decorated_callable.__module__ = callable.__module__
 
202
    decorated_callable.__name__ = callable.__name__
 
203
    decorated_callable.is_deprecated = True
 
204
 
 
205
 
 
206
def _dict_deprecation_wrapper(wrapped_method):
 
207
    """Returns a closure that emits a warning and calls the superclass"""
 
208
    def cb(dep_dict, *args, **kwargs):
 
209
        msg = 'access to %s' % (dep_dict._variable_name, )
 
210
        msg = dep_dict._deprecation_version % (msg,)
 
211
        if dep_dict._advice:
 
212
            msg += ' ' + dep_dict._advice
 
213
        warn(msg, DeprecationWarning, stacklevel=2)
 
214
        return wrapped_method(dep_dict, *args, **kwargs)
 
215
    return cb
 
216
 
 
217
 
 
218
class DeprecatedDict(dict):
 
219
    """A dictionary that complains when read or written."""
 
220
 
 
221
    is_deprecated = True
 
222
 
 
223
    def __init__(self,
 
224
        deprecation_version,
 
225
        variable_name,
 
226
        initial_value,
 
227
        advice,
 
228
        ):
 
229
        """Create a dict that warns when read or modified.
 
230
 
 
231
        :param deprecation_version: something like zero_nine
 
232
        :param initial_value: The contents of the dict
 
233
        :param variable_name: This allows better warnings to be printed
 
234
        :param advice: String of advice on what callers should do instead 
 
235
            of using this variable.
 
236
        """
 
237
        self._deprecation_version = deprecation_version
 
238
        self._variable_name = variable_name
 
239
        self._advice = advice
 
240
        dict.__init__(self, initial_value)
 
241
 
 
242
    # This isn't every possible method but it should trap anyone using the
 
243
    # dict -- add more if desired
 
244
    __len__ = _dict_deprecation_wrapper(dict.__len__)
 
245
    __getitem__ = _dict_deprecation_wrapper(dict.__getitem__)
 
246
    __setitem__ = _dict_deprecation_wrapper(dict.__setitem__)
 
247
    __delitem__ = _dict_deprecation_wrapper(dict.__delitem__)
 
248
    keys = _dict_deprecation_wrapper(dict.keys)
 
249
    __contains__ = _dict_deprecation_wrapper(dict.__contains__)
 
250
 
 
251
 
 
252
def deprecated_list(deprecation_version, variable_name,
 
253
                    initial_value, extra=None):
 
254
    """Create a list that warns when modified
 
255
 
 
256
    :param deprecation_version: something like zero_nine
 
257
    :param initial_value: The contents of the list
 
258
    :param variable_name: This allows better warnings to be printed
 
259
    :param extra: Extra info to print when printing a warning
 
260
    """
 
261
 
 
262
    subst_text = 'Modifying %s' % (variable_name,)
 
263
    msg = deprecation_version % (subst_text,)
 
264
    if extra:
 
265
        msg += ' ' + extra
 
266
 
 
267
    class _DeprecatedList(list):
 
268
        __doc__ = list.__doc__ + msg
 
269
 
 
270
        is_deprecated = True
 
271
 
 
272
        def _warn_deprecated(self, func, *args, **kwargs):
 
273
            warn(msg, DeprecationWarning, stacklevel=3)
 
274
            return func(self, *args, **kwargs)
 
275
            
 
276
        def append(self, obj):
 
277
            """appending to %s is deprecated""" % (variable_name,)
 
278
            return self._warn_deprecated(list.append, obj)
 
279
 
 
280
        def insert(self, index, obj):
 
281
            """inserting to %s is deprecated""" % (variable_name,)
 
282
            return self._warn_deprecated(list.insert, index, obj)
 
283
 
 
284
        def extend(self, iterable):
 
285
            """extending %s is deprecated""" % (variable_name,)
 
286
            return self._warn_deprecated(list.extend, iterable)
 
287
 
 
288
        def remove(self, value):
 
289
            """removing from %s is deprecated""" % (variable_name,)
 
290
            return self._warn_deprecated(list.remove, value)
 
291
 
 
292
        def pop(self, index=None):
 
293
            """pop'ing from from %s is deprecated""" % (variable_name,)
 
294
            if index:
 
295
                return self._warn_deprecated(list.pop, index)
 
296
            else:
 
297
                # Can't pass None
 
298
                return self._warn_deprecated(list.pop)
 
299
 
 
300
    return _DeprecatedList(initial_value)