20
20
The methods here allow for api symbol versioning.
23
__all__ = ['warn', 'set_warning_method', 'zero_seven']
23
__all__ = ['deprecated_function',
26
'DEPRECATED_PARAMETER',
28
'warn', 'set_warning_method', 'zero_seven',
25
46
from warnings import warn
49
DEPRECATED_PARAMETER = "A deprecated parameter marker."
28
50
zero_seven = "%s was deprecated in version 0.7."
51
zero_eight = "%s was deprecated in version 0.8."
52
zero_nine = "%s was deprecated in version 0.9."
53
zero_ten = "%s was deprecated in version 0.10."
54
zero_eleven = "%s was deprecated in version 0.11."
55
zero_twelve = "%s was deprecated in version 0.12."
56
zero_thirteen = "%s was deprecated in version 0.13."
57
zero_fourteen = "%s was deprecated in version 0.14."
58
zero_fifteen = "%s was deprecated in version 0.15."
59
zero_sixteen = "%s was deprecated in version 0.16."
60
zero_seventeen = "%s was deprecated in version 0.17."
61
zero_eighteen = "%s was deprecated in version 0.18."
62
zero_ninety = "%s was deprecated in version 0.90."
63
zero_ninetyone = "%s was deprecated in version 0.91."
64
zero_ninetytwo = "%s was deprecated in version 0.92."
65
zero_ninetythree = "%s was deprecated in version 0.93."
31
68
def set_warning_method(method):
42
79
# add that on top of the primitives, once we have all three written
83
def deprecation_string(a_callable, deprecation_version):
84
"""Generate an automatic deprecation string for a_callable.
86
:param a_callable: The callable to substitute into deprecation_version.
87
:param deprecation_version: A deprecation format warning string. This should
88
have a single %s operator in it. a_callable will be turned into a nice
89
python symbol and then substituted into deprecation_version.
91
# We also want to handle old-style classes, in particular exception, and
92
# they don't have an im_class attribute.
93
if getattr(a_callable, 'im_class', None) is None:
94
symbol = "%s.%s" % (a_callable.__module__,
97
symbol = "%s.%s.%s" % (a_callable.im_class.__module__,
98
a_callable.im_class.__name__,
101
return deprecation_version % symbol
45
104
def deprecated_function(deprecation_version):
46
105
"""Decorate a function so that use of it will trigger a warning."""
51
110
def decorated_function(*args, **kwargs):
52
111
"""This is the decorated function."""
53
symbol = "%s.%s" % (callable.__module__,
56
warn(deprecation_version % symbol, DeprecationWarning)
112
warn(deprecation_string(callable, deprecation_version),
113
DeprecationWarning, stacklevel=2)
57
114
return callable(*args, **kwargs)
58
115
_populate_decorated(callable, deprecation_version, "function",
59
116
decorated_function)
73
136
def decorated_method(self, *args, **kwargs):
74
137
"""This is the decorated method."""
75
symbol = "%s.%s.%s" % (self.__class__.__module__,
76
self.__class__.__name__,
79
warn(deprecation_version % symbol, DeprecationWarning)
138
if callable.__name__ == '__init__':
139
symbol = "%s.%s" % (self.__class__.__module__,
140
self.__class__.__name__,
143
symbol = "%s.%s.%s" % (self.__class__.__module__,
144
self.__class__.__name__,
147
warn(deprecation_version % symbol, DeprecationWarning, stacklevel=2)
80
148
return callable(self, *args, **kwargs)
81
149
_populate_decorated(callable, deprecation_version, "method",
84
152
return method_decorator
155
def deprecated_passed(parameter_value):
156
"""Return True if parameter_value was used."""
157
# FIXME: it might be nice to have a parameter deprecation decorator.
158
# it would need to handle positional and *args and **kwargs parameters,
159
# which means some mechanism to describe how the parameter was being
160
# passed before deprecation, and some way to deprecate parameters that
161
# were not at the end of the arg list. Thats needed for __init__ where
162
# we cannot just forward to a new method name.I.e. in the following
163
# examples we would want to have callers that pass any value to 'bad' be
164
# given a warning - because we have applied:
165
# @deprecated_parameter('bad', zero_seven)
167
# def __init__(self, bad=None)
168
# def __init__(self, bad, other)
169
# def __init__(self, **kwargs)
171
return not parameter_value is DEPRECATED_PARAMETER
87
174
def _decorate_docstring(callable, deprecation_version, label,
88
175
decorated_callable):
89
docstring_lines = callable.__doc__.split('\n')
177
docstring_lines = callable.__doc__.split('\n')
90
180
if len(docstring_lines) == 0:
91
181
decorated_callable.__doc__ = deprecation_version % ("This " + label)
92
182
elif len(docstring_lines) == 1:
112
202
decorated_callable)
113
203
decorated_callable.__module__ = callable.__module__
114
204
decorated_callable.__name__ = callable.__name__
205
decorated_callable.is_deprecated = True
208
def _dict_deprecation_wrapper(wrapped_method):
209
"""Returns a closure that emits a warning and calls the superclass"""
210
def cb(dep_dict, *args, **kwargs):
211
msg = 'access to %s' % (dep_dict._variable_name, )
212
msg = dep_dict._deprecation_version % (msg,)
214
msg += ' ' + dep_dict._advice
215
warn(msg, DeprecationWarning, stacklevel=2)
216
return wrapped_method(dep_dict, *args, **kwargs)
220
class DeprecatedDict(dict):
221
"""A dictionary that complains when read or written."""
231
"""Create a dict that warns when read or modified.
233
:param deprecation_version: something like zero_nine
234
:param initial_value: The contents of the dict
235
:param variable_name: This allows better warnings to be printed
236
:param advice: String of advice on what callers should do instead
237
of using this variable.
239
self._deprecation_version = deprecation_version
240
self._variable_name = variable_name
241
self._advice = advice
242
dict.__init__(self, initial_value)
244
# This isn't every possible method but it should trap anyone using the
245
# dict -- add more if desired
246
__len__ = _dict_deprecation_wrapper(dict.__len__)
247
__getitem__ = _dict_deprecation_wrapper(dict.__getitem__)
248
__setitem__ = _dict_deprecation_wrapper(dict.__setitem__)
249
__delitem__ = _dict_deprecation_wrapper(dict.__delitem__)
250
keys = _dict_deprecation_wrapper(dict.keys)
251
__contains__ = _dict_deprecation_wrapper(dict.__contains__)
254
def deprecated_list(deprecation_version, variable_name,
255
initial_value, extra=None):
256
"""Create a list that warns when modified
258
:param deprecation_version: something like zero_nine
259
:param initial_value: The contents of the list
260
:param variable_name: This allows better warnings to be printed
261
:param extra: Extra info to print when printing a warning
264
subst_text = 'Modifying %s' % (variable_name,)
265
msg = deprecation_version % (subst_text,)
269
class _DeprecatedList(list):
270
__doc__ = list.__doc__ + msg
274
def _warn_deprecated(self, func, *args, **kwargs):
275
warn(msg, DeprecationWarning, stacklevel=3)
276
return func(self, *args, **kwargs)
278
def append(self, obj):
279
"""appending to %s is deprecated""" % (variable_name,)
280
return self._warn_deprecated(list.append, obj)
282
def insert(self, index, obj):
283
"""inserting to %s is deprecated""" % (variable_name,)
284
return self._warn_deprecated(list.insert, index, obj)
286
def extend(self, iterable):
287
"""extending %s is deprecated""" % (variable_name,)
288
return self._warn_deprecated(list.extend, iterable)
290
def remove(self, value):
291
"""removing from %s is deprecated""" % (variable_name,)
292
return self._warn_deprecated(list.remove, value)
294
def pop(self, index=None):
295
"""pop'ing from from %s is deprecated""" % (variable_name,)
297
return self._warn_deprecated(list.pop, index)
300
return self._warn_deprecated(list.pop)
302
return _DeprecatedList(initial_value)