/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: Robert Collins
  • Date: 2007-08-08 02:57:22 UTC
  • mto: This revision was merged to the branch mainline in revision 2687.
  • Revision ID: robertc@robertcollins.net-20070808025722-26wvnolkzmnse7s1
* The ``bzrlib.pack`` interface has changed to use tuples of bytestrings
  rather than just bytestrings, making it easier to represent multiple
  element names. As this interface was not used by any internal facilities
  since it was introduced in 0.18 no API compatibility is being preserved.
  The serialised form of these packs is identical with 0.18 when a single
  element tuple is in use. (Robert Collins)

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