~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/symbol_versioning.py

Change version to 1.1.0dev

While the 1.0 branch goes on to 1.0.1, the trunk will head for 1.1.0 next. 
Any patches that fix issues should preferrably be coded against the 1.0
branch, so that they can be applied to both branches without the need to
cherry-pick or rebase them.  New features should be introduced in trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 by Canonical Ltd
2
 
#   Authors: Robert Collins <robert.collins@canonical.com>
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
 
           ]
33
 
 
34
 
from warnings import warn
35
 
 
36
 
 
37
 
DEPRECATED_PARAMETER = "A deprecated parameter marker."
38
 
zero_seven = "%s was deprecated in version 0.7."
39
 
zero_eight = "%s was deprecated in version 0.8."
40
 
zero_nine = "%s was deprecated in version 0.9."
41
 
zero_ten = "%s was deprecated in version 0.10."
42
 
 
43
 
 
44
 
def set_warning_method(method):
45
 
    """Set the warning method to be used by this module.
46
 
 
47
 
    It should take a message and a warning category as warnings.warn does.
48
 
    """
49
 
    global warn
50
 
    warn = method
51
 
 
52
 
 
53
 
# TODO - maybe this would be easier to use as one 'smart' method that
54
 
# guess if it is a method or a class or an attribute ? If so, we can
55
 
# add that on top of the primitives, once we have all three written
56
 
# - RBC 20050105
57
 
 
58
 
def deprecated_function(deprecation_version):
59
 
    """Decorate a function so that use of it will trigger a warning."""
60
 
 
61
 
    def function_decorator(callable):
62
 
        """This is the function python calls to perform the decoration."""
63
 
        
64
 
        def decorated_function(*args, **kwargs):
65
 
            """This is the decorated function."""
66
 
            symbol = "%s.%s" % (callable.__module__, 
67
 
                                callable.__name__
68
 
                                )
69
 
            warn(deprecation_version % symbol, DeprecationWarning, stacklevel=2)
70
 
            return callable(*args, **kwargs)
71
 
        _populate_decorated(callable, deprecation_version, "function",
72
 
                            decorated_function)
73
 
        return decorated_function
74
 
    return function_decorator
75
 
 
76
 
 
77
 
def deprecated_method(deprecation_version):
78
 
    """Decorate a method so that use of it will trigger a warning.
79
 
    
80
 
    To deprecate an entire class, decorate __init__.
81
 
    """
82
 
 
83
 
    def method_decorator(callable):
84
 
        """This is the function python calls to perform the decoration."""
85
 
        
86
 
        def decorated_method(self, *args, **kwargs):
87
 
            """This is the decorated method."""
88
 
            symbol = "%s.%s.%s" % (self.__class__.__module__, 
89
 
                                   self.__class__.__name__,
90
 
                                   callable.__name__
91
 
                                   )
92
 
            warn(deprecation_version % symbol, DeprecationWarning, stacklevel=2)
93
 
            return callable(self, *args, **kwargs)
94
 
        _populate_decorated(callable, deprecation_version, "method",
95
 
                            decorated_method)
96
 
        return decorated_method
97
 
    return method_decorator
98
 
 
99
 
 
100
 
def deprecated_passed(parameter_value):
101
 
    """Return True if parameter_value was used."""
102
 
    # FIXME: it might be nice to have a parameter deprecation decorator. 
103
 
    # it would need to handle positional and *args and **kwargs parameters,
104
 
    # which means some mechanism to describe how the parameter was being
105
 
    # passed before deprecation, and some way to deprecate parameters that
106
 
    # were not at the end of the arg list. Thats needed for __init__ where
107
 
    # we cannot just forward to a new method name.I.e. in the following
108
 
    # examples we would want to have callers that pass any value to 'bad' be
109
 
    # given a warning - because we have applied:
110
 
    # @deprecated_parameter('bad', zero_seven)
111
 
    #
112
 
    # def __init__(self, bad=None)
113
 
    # def __init__(self, bad, other)
114
 
    # def __init__(self, **kwargs)
115
 
    # RBC 20060116
116
 
    return not parameter_value is DEPRECATED_PARAMETER
117
 
 
118
 
 
119
 
def _decorate_docstring(callable, deprecation_version, label,
120
 
                        decorated_callable):
121
 
    if callable.__doc__:
122
 
        docstring_lines = callable.__doc__.split('\n')
123
 
    else:
124
 
        docstring_lines = []
125
 
    if len(docstring_lines) == 0:
126
 
        decorated_callable.__doc__ = deprecation_version % ("This " + label)
127
 
    elif len(docstring_lines) == 1:
128
 
        decorated_callable.__doc__ = (callable.__doc__ 
129
 
                                    + "\n"
130
 
                                    + "\n"
131
 
                                    + deprecation_version % ("This " + label)
132
 
                                    + "\n")
133
 
    else:
134
 
        spaces = len(docstring_lines[-1])
135
 
        new_doc = callable.__doc__
136
 
        new_doc += "\n" + " " * spaces
137
 
        new_doc += deprecation_version % ("This " + label)
138
 
        new_doc += "\n" + " " * spaces
139
 
        decorated_callable.__doc__ = new_doc
140
 
 
141
 
 
142
 
def _populate_decorated(callable, deprecation_version, label,
143
 
                        decorated_callable):
144
 
    """Populate attributes like __name__ and __doc__ on the decorated callable.
145
 
    """
146
 
    _decorate_docstring(callable, deprecation_version, label,
147
 
                        decorated_callable)
148
 
    decorated_callable.__module__ = callable.__module__
149
 
    decorated_callable.__name__ = callable.__name__
150
 
    decorated_callable.is_deprecated = True
151
 
 
152
 
 
153
 
def deprecated_list(deprecation_version, variable_name,
154
 
                    initial_value, extra=None):
155
 
    """Create a list that warns when modified
156
 
 
157
 
    :param deprecation_version: something like zero_nine
158
 
    :param initial_value: The contents of the list
159
 
    :param variable_name: This allows better warnings to be printed
160
 
    :param extra: Extra info to print when printing a warning
161
 
    """
162
 
 
163
 
    subst_text = 'Modifying %s' % (variable_name,)
164
 
    msg = deprecation_version % (subst_text,)
165
 
    if extra:
166
 
        msg += ' ' + extra
167
 
 
168
 
    class _DeprecatedList(list):
169
 
        __doc__ = list.__doc__ + msg
170
 
 
171
 
        is_deprecated = True
172
 
 
173
 
        def _warn_deprecated(self, func, *args, **kwargs):
174
 
            warn(msg, DeprecationWarning, stacklevel=3)
175
 
            return func(self, *args, **kwargs)
176
 
            
177
 
        def append(self, obj):
178
 
            """appending to %s is deprecated""" % (variable_name,)
179
 
            return self._warn_deprecated(list.append, obj)
180
 
 
181
 
        def insert(self, index, obj):
182
 
            """inserting to %s is deprecated""" % (variable_name,)
183
 
            return self._warn_deprecated(list.insert, index, obj)
184
 
 
185
 
        def extend(self, iterable):
186
 
            """extending %s is deprecated""" % (variable_name,)
187
 
            return self._warn_deprecated(list.extend, iterable)
188
 
 
189
 
        def remove(self, value):
190
 
            """removing from %s is deprecated""" % (variable_name,)
191
 
            return self._warn_deprecated(list.remove, value)
192
 
 
193
 
        def pop(self, index=None):
194
 
            """pop'ing from from %s is deprecated""" % (variable_name,)
195
 
            if index:
196
 
                return self._warn_deprecated(list.pop, index)
197
 
            else:
198
 
                # Can't pass None
199
 
                return self._warn_deprecated(list.pop)
200
 
 
201
 
    return _DeprecatedList(initial_value)