/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-10-05 02:41:37 UTC
  • mto: (2592.3.166 repository)
  • mto: This revision was merged to the branch mainline in revision 2896.
  • Revision ID: robertc@robertcollins.net-20071005024137-kn7brcu07nu8cwl1
* The class ``bzrlib.repofmt.knitrepo.KnitRepository3`` has been folded into
  ``KnitRepository`` by parameters to the constructor. (Robert Collins)
* ``bzrlib.xml_serializer.Serializer`` is now responsible for checking that
  mandatory attributes are present on serialisation and deserialisation.
  This fixes some holes in API usage and allows better separation between
  physical storage and object serialisation. (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_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
    if getattr(a_callable, 'im_class', None) is None:
 
90
        symbol = "%s.%s" % (a_callable.__module__,
 
91
                            a_callable.__name__)
 
92
    else:
 
93
        symbol = "%s.%s.%s" % (a_callable.im_class.__module__,
 
94
                               a_callable.im_class.__name__,
 
95
                               a_callable.__name__
 
96
                               )
 
97
    return deprecation_version % symbol
 
98
 
 
99
 
 
100
def deprecated_function(deprecation_version):
 
101
    """Decorate a function so that use of it will trigger a warning."""
 
102
 
 
103
    def function_decorator(callable):
 
104
        """This is the function python calls to perform the decoration."""
 
105
        
 
106
        def decorated_function(*args, **kwargs):
 
107
            """This is the decorated function."""
 
108
            warn(deprecation_string(callable, deprecation_version),
 
109
                DeprecationWarning, stacklevel=2)
 
110
            return callable(*args, **kwargs)
 
111
        _populate_decorated(callable, deprecation_version, "function",
 
112
                            decorated_function)
 
113
        return decorated_function
 
114
    return function_decorator
 
115
 
 
116
 
 
117
def deprecated_method(deprecation_version):
 
118
    """Decorate a method so that use of it will trigger a warning.
 
119
 
 
120
    To deprecate a static or class method, use 
 
121
 
 
122
        @staticmethod
 
123
        @deprecated_function
 
124
        def ...
 
125
    
 
126
    To deprecate an entire class, decorate __init__.
 
127
    """
 
128
 
 
129
    def method_decorator(callable):
 
130
        """This is the function python calls to perform the decoration."""
 
131
        
 
132
        def decorated_method(self, *args, **kwargs):
 
133
            """This is the decorated method."""
 
134
            symbol = "%s.%s.%s" % (self.__class__.__module__,
 
135
                                   self.__class__.__name__,
 
136
                                   callable.__name__
 
137
                                   )
 
138
            warn(deprecation_version % symbol, DeprecationWarning, stacklevel=2)
 
139
            return callable(self, *args, **kwargs)
 
140
        _populate_decorated(callable, deprecation_version, "method",
 
141
                            decorated_method)
 
142
        return decorated_method
 
143
    return method_decorator
 
144
 
 
145
 
 
146
def deprecated_passed(parameter_value):
 
147
    """Return True if parameter_value was used."""
 
148
    # FIXME: it might be nice to have a parameter deprecation decorator. 
 
149
    # it would need to handle positional and *args and **kwargs parameters,
 
150
    # which means some mechanism to describe how the parameter was being
 
151
    # passed before deprecation, and some way to deprecate parameters that
 
152
    # were not at the end of the arg list. Thats needed for __init__ where
 
153
    # we cannot just forward to a new method name.I.e. in the following
 
154
    # examples we would want to have callers that pass any value to 'bad' be
 
155
    # given a warning - because we have applied:
 
156
    # @deprecated_parameter('bad', zero_seven)
 
157
    #
 
158
    # def __init__(self, bad=None)
 
159
    # def __init__(self, bad, other)
 
160
    # def __init__(self, **kwargs)
 
161
    # RBC 20060116
 
162
    return not parameter_value is DEPRECATED_PARAMETER
 
163
 
 
164
 
 
165
def _decorate_docstring(callable, deprecation_version, label,
 
166
                        decorated_callable):
 
167
    if callable.__doc__:
 
168
        docstring_lines = callable.__doc__.split('\n')
 
169
    else:
 
170
        docstring_lines = []
 
171
    if len(docstring_lines) == 0:
 
172
        decorated_callable.__doc__ = deprecation_version % ("This " + label)
 
173
    elif len(docstring_lines) == 1:
 
174
        decorated_callable.__doc__ = (callable.__doc__ 
 
175
                                    + "\n"
 
176
                                    + "\n"
 
177
                                    + deprecation_version % ("This " + label)
 
178
                                    + "\n")
 
179
    else:
 
180
        spaces = len(docstring_lines[-1])
 
181
        new_doc = callable.__doc__
 
182
        new_doc += "\n" + " " * spaces
 
183
        new_doc += deprecation_version % ("This " + label)
 
184
        new_doc += "\n" + " " * spaces
 
185
        decorated_callable.__doc__ = new_doc
 
186
 
 
187
 
 
188
def _populate_decorated(callable, deprecation_version, label,
 
189
                        decorated_callable):
 
190
    """Populate attributes like __name__ and __doc__ on the decorated callable.
 
191
    """
 
192
    _decorate_docstring(callable, deprecation_version, label,
 
193
                        decorated_callable)
 
194
    decorated_callable.__module__ = callable.__module__
 
195
    decorated_callable.__name__ = callable.__name__
 
196
    decorated_callable.is_deprecated = True
 
197
 
 
198
 
 
199
def _dict_deprecation_wrapper(wrapped_method):
 
200
    """Returns a closure that emits a warning and calls the superclass"""
 
201
    def cb(dep_dict, *args, **kwargs):
 
202
        msg = 'access to %s' % (dep_dict._variable_name, )
 
203
        msg = dep_dict._deprecation_version % (msg,)
 
204
        if dep_dict._advice:
 
205
            msg += ' ' + dep_dict._advice
 
206
        warn(msg, DeprecationWarning, stacklevel=2)
 
207
        return wrapped_method(dep_dict, *args, **kwargs)
 
208
    return cb
 
209
 
 
210
 
 
211
class DeprecatedDict(dict):
 
212
    """A dictionary that complains when read or written."""
 
213
 
 
214
    is_deprecated = True
 
215
 
 
216
    def __init__(self,
 
217
        deprecation_version,
 
218
        variable_name,
 
219
        initial_value,
 
220
        advice,
 
221
        ):
 
222
        """Create a dict that warns when read or modified.
 
223
 
 
224
        :param deprecation_version: something like zero_nine
 
225
        :param initial_value: The contents of the dict
 
226
        :param variable_name: This allows better warnings to be printed
 
227
        :param advice: String of advice on what callers should do instead 
 
228
            of using this variable.
 
229
        """
 
230
        self._deprecation_version = deprecation_version
 
231
        self._variable_name = variable_name
 
232
        self._advice = advice
 
233
        dict.__init__(self, initial_value)
 
234
 
 
235
    # This isn't every possible method but it should trap anyone using the
 
236
    # dict -- add more if desired
 
237
    __len__ = _dict_deprecation_wrapper(dict.__len__)
 
238
    __getitem__ = _dict_deprecation_wrapper(dict.__getitem__)
 
239
    __setitem__ = _dict_deprecation_wrapper(dict.__setitem__)
 
240
    __delitem__ = _dict_deprecation_wrapper(dict.__delitem__)
 
241
    keys = _dict_deprecation_wrapper(dict.keys)
 
242
    __contains__ = _dict_deprecation_wrapper(dict.__contains__)
 
243
 
 
244
 
 
245
def deprecated_list(deprecation_version, variable_name,
 
246
                    initial_value, extra=None):
 
247
    """Create a list that warns when modified
 
248
 
 
249
    :param deprecation_version: something like zero_nine
 
250
    :param initial_value: The contents of the list
 
251
    :param variable_name: This allows better warnings to be printed
 
252
    :param extra: Extra info to print when printing a warning
 
253
    """
 
254
 
 
255
    subst_text = 'Modifying %s' % (variable_name,)
 
256
    msg = deprecation_version % (subst_text,)
 
257
    if extra:
 
258
        msg += ' ' + extra
 
259
 
 
260
    class _DeprecatedList(list):
 
261
        __doc__ = list.__doc__ + msg
 
262
 
 
263
        is_deprecated = True
 
264
 
 
265
        def _warn_deprecated(self, func, *args, **kwargs):
 
266
            warn(msg, DeprecationWarning, stacklevel=3)
 
267
            return func(self, *args, **kwargs)
 
268
            
 
269
        def append(self, obj):
 
270
            """appending to %s is deprecated""" % (variable_name,)
 
271
            return self._warn_deprecated(list.append, obj)
 
272
 
 
273
        def insert(self, index, obj):
 
274
            """inserting to %s is deprecated""" % (variable_name,)
 
275
            return self._warn_deprecated(list.insert, index, obj)
 
276
 
 
277
        def extend(self, iterable):
 
278
            """extending %s is deprecated""" % (variable_name,)
 
279
            return self._warn_deprecated(list.extend, iterable)
 
280
 
 
281
        def remove(self, value):
 
282
            """removing from %s is deprecated""" % (variable_name,)
 
283
            return self._warn_deprecated(list.remove, value)
 
284
 
 
285
        def pop(self, index=None):
 
286
            """pop'ing from from %s is deprecated""" % (variable_name,)
 
287
            if index:
 
288
                return self._warn_deprecated(list.pop, index)
 
289
            else:
 
290
                # Can't pass None
 
291
                return self._warn_deprecated(list.pop)
 
292
 
 
293
    return _DeprecatedList(initial_value)