Skip to content

MAINT: deprecate validCap, validJoin #18817

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions doc/api/next_api_changes/deprecations/18817-BGB.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Line2D and Patch no longer duplicate ``validJoin`` and ``validCap``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Validation of joinstyle and capstyles is now centralized in ``rcsetup``.
7 changes: 7 additions & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
import shutil
import subprocess
import sys
import warnings

import matplotlib
from matplotlib._api import MatplotlibDeprecationWarning
import sphinx

from datetime import datetime
Expand Down Expand Up @@ -109,6 +111,11 @@ def _check_dependencies():
autodoc_docstring_signature = True
autodoc_default_options = {'members': None, 'undoc-members': None}

# make sure to ignore warnings that stem from simply inspecting deprecated
# class-level attributes
warnings.filterwarnings('ignore', category=MatplotlibDeprecationWarning,
module='sphinx.util.inspect')

# missing-references names matches sphinx>=3 behavior, so we can't be nitpicky
# for older sphinxes.
nitpicky = sphinx.version_info >= (3,)
Expand Down
36 changes: 35 additions & 1 deletion lib/matplotlib/_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,44 @@
deprecated, warn_deprecated,
_rename_parameter, _delete_parameter, _make_keyword_only,
_deprecate_method_override, _deprecate_privatize_attribute,
_suppress_matplotlib_deprecation_warning,
suppress_matplotlib_deprecation_warning,
MatplotlibDeprecationWarning)


class classproperty:
"""
Like `property`, but also triggers on access via the class, and it is the
*class* that's passed as argument.

Examples
--------
::

class C:
@classproperty
def foo(cls):
return cls.__name__

assert C.foo == "C"
"""

def __init__(self, fget, fset=None, fdel=None, doc=None):
self._fget = fget
if fset is not None or fdel is not None:
raise ValueError('classproperty only implements fget.')
self.fset = fset
self.fdel = fdel
# docs are ignored for now
self._doc = doc

def __get__(self, instance, owner):
return self._fget(owner)

@property
def fget(self):
return self._fget


def check_in_list(_values, *, _print_supported_values=True, **kwargs):
"""
For each *key, value* pair in *kwargs*, check that *value* is in *_values*.
Expand Down
10 changes: 6 additions & 4 deletions lib/matplotlib/_api/deprecation.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ def the_function_to_deprecate():

def deprecate(obj, message=message, name=name, alternative=alternative,
pending=pending, obj_type=obj_type, addendum=addendum):
from matplotlib._api import classproperty

if isinstance(obj, type):
if obj_type is None:
Expand All @@ -202,15 +203,16 @@ def finalize(wrapper, new_doc):
obj.__init__ = functools.wraps(obj.__init__)(wrapper)
return obj

elif isinstance(obj, property):
elif isinstance(obj, (property, classproperty)):
obj_type = "attribute"
func = None
name = name or obj.fget.__name__
old_doc = obj.__doc__

class _deprecated_property(property):
class _deprecated_property(type(obj)):
def __get__(self, instance, owner):
if instance is not None:
if instance is not None or owner is not None \
and isinstance(self, classproperty):
emit_warning()
return super().__get__(instance, owner)

Expand Down Expand Up @@ -518,7 +520,7 @@ def empty_with_docstring(): """doc"""


@contextlib.contextmanager
def _suppress_matplotlib_deprecation_warning():
def suppress_matplotlib_deprecation_warning():
with warnings.catch_warnings():
warnings.simplefilter("ignore", MatplotlibDeprecationWarning)
yield
28 changes: 3 additions & 25 deletions lib/matplotlib/cbook/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@

import matplotlib
from matplotlib import _c_internal_utils
from matplotlib._api import warn_external as _warn_external
from matplotlib._api import (
warn_external as _warn_external, classproperty as _classproperty)
from matplotlib._api.deprecation import (
deprecated, warn_deprecated,
_rename_parameter, _delete_parameter, _make_keyword_only,
_deprecate_method_override, _deprecate_privatize_attribute,
suppress_matplotlib_deprecation_warning as
_suppress_matplotlib_deprecation_warning,
MatplotlibDeprecationWarning, mplDeprecation)

Expand Down Expand Up @@ -2262,30 +2264,6 @@ def type_name(tp):
type_name(type(v))))


class _classproperty:
"""
Like `property`, but also triggers on access via the class, and it is the
*class* that's passed as argument.
Examples
--------
::
class C:
@classproperty
def foo(cls):
return cls.__name__
assert C.foo == "C"
"""

def __init__(self, fget):
self._fget = fget

def __get__(self, instance, owner):
return self._fget(owner)


def _backend_module_name(name):
"""
Convert a backend name (either a standard backend -- "Agg", "TkAgg", ... --
Expand Down
13 changes: 10 additions & 3 deletions lib/matplotlib/lines.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
colors.
"""

# TODO: expose cap and join style attrs
from numbers import Integral, Number, Real
import logging

Expand Down Expand Up @@ -251,8 +250,16 @@ class Line2D(Artist):
fillStyles = MarkerStyle.fillstyles

zorder = 2
validCap = ('butt', 'round', 'projecting')
validJoin = ('miter', 'round', 'bevel')

@_api.deprecated("3.4")
@_api.classproperty
def validCap(cls):
return ('butt', 'round', 'projecting')

@_api.deprecated("3.4")
@_api.classproperty
def validJoin(cls):
return ('miter', 'round', 'bevel')

def __str__(self):
if self._label != "":
Expand Down
14 changes: 12 additions & 2 deletions lib/matplotlib/patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,18 @@ class Patch(artist.Artist):
are *None*, they default to their rc params setting.
"""
zorder = 1
validCap = mlines.Line2D.validCap
validJoin = mlines.Line2D.validJoin

@_api.deprecated("3.4")
@_api.classproperty
def validCap(cls):
with _api.suppress_matplotlib_deprecation_warning():
return mlines.Line2D.validCap

@_api.deprecated("3.4")
@_api.classproperty
def validJoin(cls):
with _api.suppress_matplotlib_deprecation_warning():
return mlines.Line2D.validJoin

# Whether to draw an edge by default. Set on a
# subclass-by-subclass basis.
Expand Down
13 changes: 13 additions & 0 deletions lib/matplotlib/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,16 @@ def test_check_shape(target, test_shape):
data = np.zeros(test_shape)
with pytest.raises(ValueError, match=error_pattern):
_api.check_shape(target, aardvark=data)


def test_classproperty_deprecation():
class A:
@_api.deprecated("0.0.0")
@_api.classproperty
def f(cls):
pass
with pytest.warns(_api.MatplotlibDeprecationWarning):
A.f
with pytest.warns(_api.MatplotlibDeprecationWarning):
a = A()
a.f