/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: Vincent Ladeuil
  • Date: 2007-06-24 15:16:40 UTC
  • mto: (2547.2.1 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 2550.
  • Revision ID: v.ladeuil+lp@free.fr-20070624151640-5tg62jb5g9tqvybd
Fix 121889 by working around urllib2 bug.

* tests/HTTPTestUtil.py:
(DigestAuthRequestHandler.send_header_auth_reqed): python-2.4.1
fail to decode the header without the quotes. The RFC do not
require them, python >= 2.4.4 handles them gracefully. Not a big
deal.

* tests/test_http.py:
(TestAuth.setUp): Add a comment in hope I will not running around
shouting: "Who takes my traces ? Gimme my traces !" when running
the only tests who capture their own traces without showing them.

* transport/http/_urllib2_wrappers.py (BasicAuthHandler,
DigestAuthHandler): Not directly related to the bug, bug good to
fix anyway, the digest auth should be preferred to the basic
one. To do so, the digest handler should be tried before the basic
one.

* builtins.py:
(cmd_selftest.run): Fix typo. Note to reviewers: No, it's not
related to the bug. No there are no tests for that. No I don't
intend to write some :) But I'll understand if you veto that
because you want to take care of it :D

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