diff --git a/lib/matplotlib/_api/deprecation.py b/lib/matplotlib/_api/deprecation.py index e9722f5d26c4..65a754bbb43d 100644 --- a/lib/matplotlib/_api/deprecation.py +++ b/lib/matplotlib/_api/deprecation.py @@ -26,25 +26,20 @@ def _generate_deprecation_warning( addendum='', *, removal=''): if pending: if removal: - raise ValueError( - "A pending deprecation cannot have a scheduled removal") - else: - if not removal: - macro, meso, *_ = since.split('.') - removal = f'{macro}.{int(meso) + 2}' - removal = f"in {removal}" + raise ValueError("A pending deprecation cannot have a scheduled removal") + elif removal == '': + macro, meso, *_ = since.split('.') + removal = f'{macro}.{int(meso) + 2}' if not message: message = ( - ("The %(name)s %(obj_type)s" if obj_type else "%(name)s") - + (" will be deprecated in a future version" - if pending else - " was deprecated in Matplotlib %(since)s and will be removed %(removal)s" - ) - + "." - + (" Use %(alternative)s instead." if alternative else "") - + (" %(addendum)s" if addendum else "")) - warning_cls = (PendingDeprecationWarning if pending - else MatplotlibDeprecationWarning) + ("The %(name)s %(obj_type)s" if obj_type else "%(name)s") + + (" will be deprecated in a future version" if pending else + (" was deprecated in Matplotlib %(since)s" + + (" and will be removed in %(removal)s" if removal else ""))) + + "." + + (" Use %(alternative)s instead." if alternative else "") + + (" %(addendum)s" if addendum else "")) + warning_cls = PendingDeprecationWarning if pending else MatplotlibDeprecationWarning return warning_cls(message % dict( func=name, name=name, obj_type=obj_type, since=since, removal=removal, alternative=alternative, addendum=addendum)) @@ -295,7 +290,7 @@ def wrapper(*args, **kwargs): warn_deprecated( since, message=f"The {old!r} parameter of {func.__name__}() " f"has been renamed {new!r} since Matplotlib {since}; support " - f"for the old name will be dropped %(removal)s.") + f"for the old name will be dropped in %(removal)s.") kwargs[new] = kwargs.pop(old) return func(*args, **kwargs) @@ -390,12 +385,12 @@ def wrapper(*inner_args, **inner_kwargs): warn_deprecated( since, message=f"Additional positional arguments to " f"{func.__name__}() are deprecated since %(since)s and " - f"support for them will be removed %(removal)s.") + f"support for them will be removed in %(removal)s.") elif is_varkwargs and arguments.get(name): warn_deprecated( since, message=f"Additional keyword arguments to " f"{func.__name__}() are deprecated since %(since)s and " - f"support for them will be removed %(removal)s.") + f"support for them will be removed in %(removal)s.") # We cannot just check `name not in arguments` because the pyplot # wrappers always pass all arguments explicitly. elif any(name in d and d[name] != _deprecated_parameter @@ -453,7 +448,7 @@ def wrapper(*args, **kwargs): warn_deprecated( since, message="Passing the %(name)s %(obj_type)s " "positionally is deprecated since Matplotlib %(since)s; the " - "parameter will become keyword-only %(removal)s.", + "parameter will become keyword-only in %(removal)s.", name=name, obj_type=f"parameter of {func.__name__}()") return func(*args, **kwargs) diff --git a/lib/matplotlib/_api/deprecation.pyi b/lib/matplotlib/_api/deprecation.pyi index d0d04d987410..e050290662d9 100644 --- a/lib/matplotlib/_api/deprecation.pyi +++ b/lib/matplotlib/_api/deprecation.pyi @@ -1,6 +1,6 @@ from collections.abc import Callable import contextlib -from typing import Any, ParamSpec, TypedDict, TypeVar, overload +from typing import Any, Literal, ParamSpec, TypedDict, TypeVar, overload from typing_extensions import ( Unpack, # < Py 3.11 ) @@ -17,7 +17,7 @@ class DeprecationKwargs(TypedDict, total=False): pending: bool obj_type: str addendum: str - removal: str + removal: str | Literal[False] class NamedDeprecationKwargs(DeprecationKwargs, total=False): name: str diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index a8170ce4f6b0..817eb51705fe 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -3333,7 +3333,7 @@ def _get_image_filename(self, tool): _api.warn_deprecated( "3.9", message=f"Loading icon {tool.image!r} from the current " "directory or from Matplotlib's image directory. This behavior " - "is deprecated since %(since)s and will be removed %(removal)s; " + "is deprecated since %(since)s and will be removed in %(removal)s; " "Tool.image should be set to a path relative to the Tool's source " "file, or to an absolute path.") return os.path.abspath(fname) diff --git a/lib/matplotlib/hatch.py b/lib/matplotlib/hatch.py index 7a4b283c1dbe..0cbd042e1628 100644 --- a/lib/matplotlib/hatch.py +++ b/lib/matplotlib/hatch.py @@ -192,7 +192,7 @@ def _validate_hatch_pattern(hatch): message=f'hatch must consist of a string of "{valid}" or ' 'None, but found the following invalid values ' f'"{invalids}". Passing invalid values is deprecated ' - 'since %(since)s and will become an error %(removal)s.' + 'since %(since)s and will become an error in %(removal)s.' ) diff --git a/lib/matplotlib/legend.py b/lib/matplotlib/legend.py index 0d487a48bde7..270757fc298e 100644 --- a/lib/matplotlib/legend.py +++ b/lib/matplotlib/legend.py @@ -1337,7 +1337,7 @@ def _parse_legend_args(axs, *args, handles=None, labels=None, **kwargs): _api.warn_deprecated("3.9", message=( "You have mixed positional and keyword arguments, some input may " "be discarded. This is deprecated since %(since)s and will " - "become an error %(removal)s.")) + "become an error in %(removal)s.")) if (hasattr(handles, "__len__") and hasattr(labels, "__len__") and diff --git a/lib/matplotlib/projections/polar.py b/lib/matplotlib/projections/polar.py index 325da95105ab..7fe6045039b1 100644 --- a/lib/matplotlib/projections/polar.py +++ b/lib/matplotlib/projections/polar.py @@ -21,7 +21,7 @@ def _apply_theta_transforms_warn(): message=( "Passing `apply_theta_transforms=True` (the default) " "is deprecated since Matplotlib %(since)s. " - "Support for this will be removed in Matplotlib %(removal)s. " + "Support for this will be removed in Matplotlib in %(removal)s. " "To prevent this warning, set `apply_theta_transforms=False`, " "and make sure to shift theta values before being passed to " "this transform." diff --git a/lib/matplotlib/tests/test_api.py b/lib/matplotlib/tests/test_api.py index 23d3ec48f31f..f04604c14cce 100644 --- a/lib/matplotlib/tests/test_api.py +++ b/lib/matplotlib/tests/test_api.py @@ -49,6 +49,41 @@ def f(cls: Self) -> None: a.f +def test_warn_deprecated(): + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match=r'foo was deprecated in Matplotlib 3\.10 and will be ' + r'removed in 3\.12\.'): + _api.warn_deprecated('3.10', name='foo') + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match=r'The foo class was deprecated in Matplotlib 3\.10 and ' + r'will be removed in 3\.12\.'): + _api.warn_deprecated('3.10', name='foo', obj_type='class') + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match=r'foo was deprecated in Matplotlib 3\.10 and will be ' + r'removed in 3\.12\. Use bar instead\.'): + _api.warn_deprecated('3.10', name='foo', alternative='bar') + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match=r'foo was deprecated in Matplotlib 3\.10 and will be ' + r'removed in 3\.12\. More information\.'): + _api.warn_deprecated('3.10', name='foo', addendum='More information.') + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match=r'foo was deprecated in Matplotlib 3\.10 and will be ' + r'removed in 4\.0\.'): + _api.warn_deprecated('3.10', name='foo', removal='4.0') + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match=r'foo was deprecated in Matplotlib 3\.10\.'): + _api.warn_deprecated('3.10', name='foo', removal=False) + with pytest.warns(PendingDeprecationWarning, + match=r'foo will be deprecated in a future version'): + _api.warn_deprecated('3.10', name='foo', pending=True) + with pytest.raises(ValueError, match=r'cannot have a scheduled removal'): + _api.warn_deprecated('3.10', name='foo', pending=True, removal='3.12') + with pytest.warns(mpl.MatplotlibDeprecationWarning, match=r'Complete replacement'): + _api.warn_deprecated('3.10', message='Complete replacement', name='foo', + alternative='bar', addendum='More information.', + obj_type='class', removal='4.0') + + def test_deprecate_privatize_attribute() -> None: class C: def __init__(self) -> None: self._attr = 1