/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-09-19 05:14:14 UTC
  • mto: (2835.1.1 ianc-integration)
  • mto: This revision was merged to the branch mainline in revision 2836.
  • Revision ID: robertc@robertcollins.net-20070919051414-2tgjqteg7k3ps4h0
* ``pull``, ``merge`` and ``push`` will no longer silently correct some
  repository index errors that occured as a result of the Weave disk format.
  Instead the ``reconcile`` command needs to be run to correct those
  problems if they exist (and it has been able to fix most such problems
  since bzr 0.8). Some new problems have been identified during this release
  and you should run ``bzr check`` once on every repository to see if you
  need to reconcile. If you cannot ``pull`` or ``merge`` from a remote
  repository due to mismatched parent errors - a symptom of index errors -
  you should simply take a full copy of that remote repository to a clean
  directory outside any local repositories, then run reconcile on it, and
  finally pull from it locally. (And naturally email the repositories owner
  to ask them to upgrade and run reconcile).
  (Robert Collins)

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