/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: John Arbash Meinel
  • Date: 2007-05-31 20:29:04 UTC
  • mto: This revision was merged to the branch mainline in revision 2499.
  • Revision ID: john@arbash-meinel.com-20070531202904-34h7ygudo7qq9ha1
Update the code so that symlinks aren't cached at incorrect times
and fix the tests so that they don't assume files and symlinks
get cached even when the timestamp doesn't declare them 'safe'.

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