Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/matplotlib/cbook/deprecation.py : 76%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import contextlib
2import functools
3import inspect
4import warnings
7class MatplotlibDeprecationWarning(UserWarning):
8 """
9 A class for issuing deprecation warnings for Matplotlib users.
11 In light of the fact that Python builtin DeprecationWarnings are ignored
12 by default as of Python 2.7 (see link below), this class was put in to
13 allow for the signaling of deprecation, but via UserWarnings which are not
14 ignored by default.
16 https://docs.python.org/dev/whatsnew/2.7.html#the-future-for-python-2-x
17 """
20mplDeprecation = MatplotlibDeprecationWarning
21"""mplDeprecation is deprecated. Use MatplotlibDeprecationWarning instead."""
24def _generate_deprecation_warning(
25 since, message='', name='', alternative='', pending=False, obj_type='',
26 addendum='', *, removal=''):
27 if pending:
28 if removal:
29 raise ValueError(
30 "A pending deprecation cannot have a scheduled removal")
31 else:
32 if removal:
33 removal = "in {}".format(removal)
34 else:
35 removal = {"2.2": "in 3.1", "3.0": "in 3.2", "3.1": "in 3.3"}.get(
36 since, "two minor releases later")
37 if not message:
38 message = (
39 "\nThe %(name)s %(obj_type)s"
40 + (" will be deprecated in a future version"
41 if pending else
42 (" was deprecated in Matplotlib %(since)s"
43 + (" and will be removed %(removal)s"
44 if removal else
45 "")))
46 + "."
47 + (" Use %(alternative)s instead." if alternative else "")
48 + (" %(addendum)s" if addendum else ""))
49 warning_cls = (PendingDeprecationWarning if pending
50 else MatplotlibDeprecationWarning)
51 return warning_cls(message % dict(
52 func=name, name=name, obj_type=obj_type, since=since, removal=removal,
53 alternative=alternative, addendum=addendum))
56def warn_deprecated(
57 since, *, message='', name='', alternative='', pending=False,
58 obj_type='', addendum='', removal=''):
59 """
60 Used to display deprecation in a standard way.
62 Parameters
63 ----------
64 since : str
65 The release at which this API became deprecated.
67 message : str, optional
68 Override the default deprecation message. The format
69 specifier `%(name)s` may be used for the name of the function,
70 and `%(alternative)s` may be used in the deprecation message
71 to insert the name of an alternative to the deprecated
72 function. `%(obj_type)s` may be used to insert a friendly name
73 for the type of object being deprecated.
75 name : str, optional
76 The name of the deprecated object.
78 alternative : str, optional
79 An alternative API that the user may use in place of the deprecated
80 API. The deprecation warning will tell the user about this alternative
81 if provided.
83 pending : bool, optional
84 If True, uses a PendingDeprecationWarning instead of a
85 DeprecationWarning. Cannot be used together with *removal*.
87 obj_type : str, optional
88 The object type being deprecated.
90 addendum : str, optional
91 Additional text appended directly to the final message.
93 removal : str, optional
94 The expected removal version. With the default (an empty string), a
95 removal version is automatically computed from *since*. Set to other
96 Falsy values to not schedule a removal date. Cannot be used together
97 with *pending*.
99 Examples
100 --------
101 Basic example::
103 # To warn of the deprecation of "matplotlib.name_of_module"
104 warn_deprecated('1.4.0', name='matplotlib.name_of_module',
105 obj_type='module')
106 """
107 warning = _generate_deprecation_warning(
108 since, message, name, alternative, pending, obj_type, addendum,
109 removal=removal)
110 from . import _warn_external
111 _warn_external(warning)
114def deprecated(since, *, message='', name='', alternative='', pending=False,
115 obj_type=None, addendum='', removal=''):
116 """
117 Decorator to mark a function, a class, or a property as deprecated.
119 When deprecating a classmethod, a staticmethod, or a property, the
120 ``@deprecated`` decorator should go *under* the ``@classmethod``, etc.
121 decorator (i.e., `deprecated` should directly decorate the underlying
122 callable).
124 Parameters
125 ----------
126 since : str
127 The release at which this API became deprecated. This is
128 required.
130 message : str, optional
131 Override the default deprecation message. The format
132 specifier `%(name)s` may be used for the name of the object,
133 and `%(alternative)s` may be used in the deprecation message
134 to insert the name of an alternative to the deprecated
135 object.
137 name : str, optional
138 The name used in the deprecation message; if not provided, the name
139 is automatically determined from the deprecated object.
141 alternative : str, optional
142 An alternative API that the user may use in place of the deprecated
143 API. The deprecation warning will tell the user about this alternative
144 if provided.
146 pending : bool, optional
147 If True, uses a PendingDeprecationWarning instead of a
148 DeprecationWarning. Cannot be used together with *removal*.
150 obj_type : str, optional
151 The object type being deprecated; by default, 'class' if decorating
152 a class, 'attribute' if decorating a property, 'function' otherwise.
154 addendum : str, optional
155 Additional text appended directly to the final message.
157 removal : str, optional
158 The expected removal version. With the default (an empty string), a
159 removal version is automatically computed from *since*. Set to other
160 Falsy values to not schedule a removal date. Cannot be used together
161 with *pending*.
163 Examples
164 --------
165 Basic example::
167 @deprecated('1.4.0')
168 def the_function_to_deprecate():
169 pass
170 """
172 def deprecate(obj, message=message, name=name, alternative=alternative,
173 pending=pending, obj_type=obj_type, addendum=addendum):
175 if isinstance(obj, type):
176 if obj_type is None:
177 obj_type = "class"
178 func = obj.__init__
179 name = name or obj.__name__
180 old_doc = obj.__doc__
182 def finalize(wrapper, new_doc):
183 try:
184 obj.__doc__ = new_doc
185 except AttributeError: # Can't set on some extension objects.
186 pass
187 obj.__init__ = functools.wraps(obj.__init__)(wrapper)
188 return obj
190 elif isinstance(obj, property):
191 obj_type = "attribute"
192 func = None
193 name = name or obj.fget.__name__
194 old_doc = obj.__doc__
196 class _deprecated_property(property):
197 def __get__(self, instance, owner):
198 if instance is not None:
199 from . import _warn_external
200 _warn_external(warning)
201 return super().__get__(instance, owner)
203 def __set__(self, instance, value):
204 if instance is not None:
205 from . import _warn_external
206 _warn_external(warning)
207 return super().__set__(instance, value)
209 def __delete__(self, instance):
210 if instance is not None:
211 from . import _warn_external
212 _warn_external(warning)
213 return super().__delete__(instance)
215 def finalize(_, new_doc):
216 return _deprecated_property(
217 fget=obj.fget, fset=obj.fset, fdel=obj.fdel, doc=new_doc)
219 else:
220 if obj_type is None:
221 obj_type = "function"
222 func = obj
223 name = name or obj.__name__
224 old_doc = func.__doc__
226 def finalize(wrapper, new_doc):
227 wrapper = functools.wraps(func)(wrapper)
228 wrapper.__doc__ = new_doc
229 return wrapper
231 warning = _generate_deprecation_warning(
232 since, message, name, alternative, pending, obj_type, addendum,
233 removal=removal)
235 def wrapper(*args, **kwargs):
236 from . import _warn_external
237 _warn_external(warning)
238 return func(*args, **kwargs)
240 old_doc = inspect.cleandoc(old_doc or '').strip('\n')
242 notes_header = '\nNotes\n-----'
243 new_doc = (f"[*Deprecated*] {old_doc}\n"
244 f"{notes_header if notes_header not in old_doc else ''}\n"
245 f".. deprecated:: {since}\n"
246 f" {message.strip()}")
248 if not old_doc:
249 # This is to prevent a spurious 'unexpected unindent' warning from
250 # docutils when the original docstring was blank.
251 new_doc += r'\ '
253 return finalize(wrapper, new_doc)
255 return deprecate
258def _rename_parameter(since, old, new, func=None):
259 """
260 Decorator indicating that parameter *old* of *func* is renamed to *new*.
262 The actual implementation of *func* should use *new*, not *old*. If *old*
263 is passed to *func*, a DeprecationWarning is emitted, and its value is
264 used, even if *new* is also passed by keyword (this is to simplify pyplot
265 wrapper functions, which always pass *new* explicitly to the Axes method).
266 If *new* is also passed but positionally, a TypeError will be raised by the
267 underlying function during argument binding.
269 Examples
270 --------
271 ::
273 @_rename_parameter("3.1", "bad_name", "good_name")
274 def func(good_name): ...
275 """
277 if func is None:
278 return functools.partial(_rename_parameter, since, old, new)
280 signature = inspect.signature(func)
281 assert old not in signature.parameters, (
282 f"Matplotlib internal error: {old!r} cannot be a parameter for "
283 f"{func.__name__}()")
284 assert new in signature.parameters, (
285 f"Matplotlib internal error: {new!r} must be a parameter for "
286 f"{func.__name__}()")
288 @functools.wraps(func)
289 def wrapper(*args, **kwargs):
290 if old in kwargs:
291 warn_deprecated(
292 since, message=f"The {old!r} parameter of {func.__name__}() "
293 f"has been renamed {new!r} since Matplotlib {since}; support "
294 f"for the old name will be dropped %(removal)s.")
295 kwargs[new] = kwargs.pop(old)
296 return func(*args, **kwargs)
298 # wrapper() must keep the same documented signature as func(): if we
299 # instead made both *old* and *new* appear in wrapper()'s signature, they
300 # would both show up in the pyplot function for an Axes method as well and
301 # pyplot would explicitly pass both arguments to the Axes method.
303 return wrapper
306class _deprecated_parameter_class:
307 def __repr__(self):
308 return "<deprecated parameter>"
311_deprecated_parameter = _deprecated_parameter_class()
314def _delete_parameter(since, name, func=None):
315 """
316 Decorator indicating that parameter *name* of *func* is being deprecated.
318 The actual implementation of *func* should keep the *name* parameter in its
319 signature.
321 Parameters that come after the deprecated parameter effectively become
322 keyword-only (as they cannot be passed positionally without triggering the
323 DeprecationWarning on the deprecated parameter), and should be marked as
324 such after the deprecation period has passed and the deprecated parameter
325 is removed.
327 Examples
328 --------
329 ::
331 @_delete_parameter("3.1", "unused")
332 def func(used_arg, other_arg, unused, more_args): ...
333 """
335 if func is None:
336 return functools.partial(_delete_parameter, since, name)
338 signature = inspect.signature(func)
339 assert name in signature.parameters, (
340 f"Matplotlib internal error: {name!r} must be a parameter for "
341 f"{func.__name__}()")
342 func.__signature__ = signature.replace(parameters=[
343 param.replace(default=_deprecated_parameter) if param.name == name
344 else param
345 for param in signature.parameters.values()])
347 @functools.wraps(func)
348 def wrapper(*args, **kwargs):
349 arguments = func.__signature__.bind(*args, **kwargs).arguments
350 # We cannot just check `name not in arguments` because the pyplot
351 # wrappers always pass all arguments explicitly.
352 if name in arguments and arguments[name] != _deprecated_parameter:
353 warn_deprecated(
354 since, message=f"The {name!r} parameter of {func.__name__}() "
355 f"is deprecated since Matplotlib {since} and will be removed "
356 f"%(removal)s. If any parameter follows {name!r}, they "
357 f"should be pass as keyword, not positionally.")
358 return func(*args, **kwargs)
360 return wrapper
363def _make_keyword_only(since, name, func=None):
364 """
365 Decorator indicating that passing parameter *name* (or any of the following
366 ones) positionally to *func* is being deprecated.
368 Note that this decorator **cannot** be applied to a function that has a
369 pyplot-level wrapper, as the wrapper always pass all arguments by keyword.
370 If it is used, users will see spurious DeprecationWarnings every time they
371 call the pyplot wrapper.
372 """
374 if func is None:
375 return functools.partial(_make_keyword_only, since, name)
377 signature = inspect.signature(func)
378 POK = inspect.Parameter.POSITIONAL_OR_KEYWORD
379 KWO = inspect.Parameter.KEYWORD_ONLY
380 assert (name in signature.parameters
381 and signature.parameters[name].kind == POK), (
382 f"Matplotlib internal error: {name!r} must be a positional-or-keyword "
383 f"parameter for {func.__name__}()")
384 names = [*signature.parameters]
385 kwonly = [name for name in names[names.index(name):]
386 if signature.parameters[name].kind == POK]
387 func.__signature__ = signature.replace(parameters=[
388 param.replace(kind=KWO) if param.name in kwonly else param
389 for param in signature.parameters.values()])
391 @functools.wraps(func)
392 def wrapper(*args, **kwargs):
393 bound = signature.bind(*args, **kwargs)
394 if name in bound.arguments and name not in kwargs:
395 warn_deprecated(
396 since, message="Passing the %(name)s %(obj_type)s "
397 "positionally is deprecated since Matplotlib %(since)s; the "
398 "parameter will become keyword-only %(removal)s.",
399 name=name, obj_type=f"parameter of {func.__name__}()")
400 return func(*args, **kwargs)
402 return wrapper
405@contextlib.contextmanager
406def _suppress_matplotlib_deprecation_warning():
407 with warnings.catch_warnings():
408 warnings.simplefilter("ignore", MatplotlibDeprecationWarning)
409 yield