From 479c84e23b4fd4c6ecb52146ea37c7b32d5d98e1 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 18 Sep 2018 12:00:47 +0200 Subject: [PATCH] Machinery for deprecating properties. - Generate a message saying 'The foo attribute has been deprecated' instead of 'The foo function'. - Automatically deprecate the setter at the same time as the getter. - Also emit the warning when accessing the attribute on the class rather than on an instance. (Compare with the recently removed attempt at supporting deprecation of classmethods, which didn't really bring anything except the ability to put the `@deprecated` decorator over the `@classmethod` decorator.) --- lib/matplotlib/__init__.py | 10 +++---- lib/matplotlib/artist.py | 2 +- lib/matplotlib/axes/_axes.py | 2 +- lib/matplotlib/axes/_base.py | 2 +- lib/matplotlib/axis.py | 3 +-- lib/matplotlib/backends/backend_pdf.py | 2 +- lib/matplotlib/backends/backend_ps.py | 6 ++--- lib/matplotlib/backends/backend_qt5.py | 5 +--- lib/matplotlib/cbook/deprecation.py | 37 +++++++++++++++++++++----- lib/matplotlib/contour.py | 6 ++--- lib/matplotlib/font_manager.py | 4 +-- lib/matplotlib/texmanager.py | 4 +-- 12 files changed, 52 insertions(+), 31 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 27f6e72fdd5b..5749f5da35c9 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -781,30 +781,30 @@ class RcParams(MutableMapping, dict): for key, (default, converter) in defaultParams.items() if key not in _all_deprecated} - @property @cbook.deprecated("3.0") + @property def msg_depr(self): return "%s is deprecated and replaced with %s; please use the latter." - @property @cbook.deprecated("3.0") + @property def msg_depr_ignore(self): return "%s is deprecated and ignored. Use %s instead." - @property @cbook.deprecated("3.0") + @property def msg_depr_set(self): return ("%s is deprecated. Please remove it from your matplotlibrc " "and/or style files.") - @property @cbook.deprecated("3.0") + @property def msg_obsolete(self): return ("%s is obsolete. Please remove it from your matplotlibrc " "and/or style files.") - @property @cbook.deprecated("3.0") + @property def msg_backend_obsolete(self): return ("The {} rcParam was deprecated in version 2.2. In order to " "force the use of a specific Qt binding, either import that " diff --git a/lib/matplotlib/artist.py b/lib/matplotlib/artist.py index 49af30d2478c..7a79a28339a1 100644 --- a/lib/matplotlib/artist.py +++ b/lib/matplotlib/artist.py @@ -56,8 +56,8 @@ class Artist(object): Typically, all visible elements in a figure are subclasses of Artist. """ - @property @cbook.deprecated("3.1") + @property def aname(self): return 'Artist' diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 80df57e49961..c80e0fb60085 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -128,8 +128,8 @@ class Axes(_AxesBase): """ ### Labelling, legend and texts - @property @cbook.deprecated("3.1") + @property def aname(self): return 'Axes' diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index ef2b8966bb51..c2003f3b5380 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -1094,8 +1094,8 @@ def cla(self): self.stale = True - @property @cbook.deprecated("3.0") + @property def mouseover_set(self): return frozenset(self._mouseover_set) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 5164567ae1c7..a139c5023ca5 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -774,13 +774,12 @@ def _set_scale(self, value, **kwargs): def limit_range_for_scale(self, vmin, vmax): return self._scale.limit_range_for_scale(vmin, vmax, self.get_minpos()) - @property @cbook.deprecated("2.2.0") + @property def unit_data(self): return self.units @unit_data.setter - @cbook.deprecated("2.2.0") def unit_data(self, unit_data): self.set_units(unit_data) diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index 44bf35b96fb3..1f93ded4496c 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -653,8 +653,8 @@ def fontName(self, fontprop): return Fx - @property @cbook.deprecated("3.0") + @property def texFontMap(self): # lazy-load texFontMap, it takes a while to parse # and usetex is a relatively rare use case diff --git a/lib/matplotlib/backends/backend_ps.py b/lib/matplotlib/backends/backend_ps.py index 5ea4d770929c..103eacfd5d4a 100644 --- a/lib/matplotlib/backends/backend_ps.py +++ b/lib/matplotlib/backends/backend_ps.py @@ -46,8 +46,8 @@ class PsBackendHelper(object): def __init__(self): self._cached = {} - @property @cbook.deprecated("3.1") + @property def gs_exe(self): """ executable name of ghostscript. @@ -64,8 +64,8 @@ def gs_exe(self): self._cached["gs_exe"] = str(gs_exe) return str(gs_exe) - @property @cbook.deprecated("3.1") + @property def gs_version(self): """ version of ghostscript. @@ -87,8 +87,8 @@ def gs_version(self): self._cached["gs_version"] = gs_version return gs_version - @property @cbook.deprecated("3.1") + @property def supports_ps2write(self): """ True if the installed ghostscript supports ps2write device. diff --git a/lib/matplotlib/backends/backend_qt5.py b/lib/matplotlib/backends/backend_qt5.py index 0df0f764dc1e..8013245e9833 100644 --- a/lib/matplotlib/backends/backend_qt5.py +++ b/lib/matplotlib/backends/backend_qt5.py @@ -377,9 +377,8 @@ def keyReleaseEvent(self, event): if key is not None: FigureCanvasBase.key_release_event(self, key, guiEvent=event) + @cbook.deprecated("3.0", alternative="event.guiEvent.isAutoRepeat") @property - @cbook.deprecated("3.0", "Manually check `event.guiEvent.isAutoRepeat()` " - "in the event handler.") def keyAutoRepeat(self): """ If True, enable auto-repeat for key events. @@ -387,8 +386,6 @@ def keyAutoRepeat(self): return self._keyautorepeat @keyAutoRepeat.setter - @cbook.deprecated("3.0", "Manually check `event.guiEvent.isAutoRepeat()` " - "in the event handler.") def keyAutoRepeat(self, val): self._keyautorepeat = bool(val) diff --git a/lib/matplotlib/cbook/deprecation.py b/lib/matplotlib/cbook/deprecation.py index a8b007f40d0d..43de50858cd5 100644 --- a/lib/matplotlib/cbook/deprecation.py +++ b/lib/matplotlib/cbook/deprecation.py @@ -116,7 +116,7 @@ def warn_deprecated( def deprecated(since, message='', name='', alternative='', pending=False, obj_type=None, addendum='', *, removal=''): """ - Decorator to mark a function or a class as deprecated. + Decorator to mark a function, a class, or a property as deprecated. When deprecating a classmethod, a staticmethod, or a property, the ``@deprecated`` decorator should go *under* the ``@classmethod``, etc. @@ -145,7 +145,7 @@ def deprecated(since, message='', name='', alternative='', pending=False, def new_function(): ... - oldFunction = new_function + old_function = new_function alternative : str, optional An alternative API that the user may use in place of the deprecated @@ -183,22 +183,47 @@ def the_function_to_deprecate(): def deprecate(obj, message=message, name=name, alternative=alternative, pending=pending, addendum=addendum): - - if not name: - name = obj.__name__ - if isinstance(obj, type): obj_type = "class" func = obj.__init__ + name = name or obj.__name__ old_doc = obj.__doc__ def finalize(wrapper, new_doc): obj.__doc__ = new_doc obj.__init__ = wrapper return obj + + elif isinstance(obj, property): + obj_type = "attribute" + func = None + name = name or obj.fget.__name__ + old_doc = obj.__doc__ + + class _deprecated_property(property): + def __get__(self, instance, owner): + from . import _warn_external + _warn_external(message, category) + return super().__get__(instance, owner) + + def __set__(self, instance, value): + from . import _warn_external + _warn_external(message, category) + return super().__set__(instance, value) + + def __delete__(self, instance): + from . import _warn_external + _warn_external(message, category) + return super().__delete__(instance) + + def finalize(_, new_doc): + return _deprecated_property( + fget=obj.fget, fset=obj.fset, fdel=obj.fdel, doc=new_doc) + else: obj_type = "function" func = obj + name = name or obj.__name__ old_doc = func.__doc__ def finalize(wrapper, new_doc): diff --git a/lib/matplotlib/contour.py b/lib/matplotlib/contour.py index 5354b315340c..284836f90856 100644 --- a/lib/matplotlib/contour.py +++ b/lib/matplotlib/contour.py @@ -216,11 +216,11 @@ def clabel(self, *args, self.labelTextsList = cbook.silent_list('text.Text', self.labelTexts) return self.labelTextsList - cl = property(cbook.deprecated("3.0", alternative="labelTexts")( + cl = cbook.deprecated("3.0", alternative="labelTexts")(property( lambda self: self.labelTexts)) - cl_xy = property(cbook.deprecated("3.0", alternative="labelXYs")( + cl_xy = cbook.deprecated("3.0", alternative="labelXYs")(property( lambda self: self.labelXYs)) - cl_cvalues = property(cbook.deprecated("3.0", alternative="labelCValues")( + cl_cvalues = cbook.deprecated("3.0", alternative="labelCValues")(property( lambda self: self.labelCValues)) def print_label(self, linecontour, labelwidth): diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index edd530e27869..def4f769b311 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -984,13 +984,13 @@ def __init__(self, size=None, weight='normal'): self.afmlist = createFontList(afmfiles, fontext='afm') self.defaultFont['afm'] = afmfiles[0] if afmfiles else None - @property @cbook.deprecated("3.0") + @property def ttffiles(self): return [font.fname for font in self.ttflist] - @property @cbook.deprecated("3.0") + @property def afmfiles(self): return [font.fname for font in self.afmlist] diff --git a/lib/matplotlib/texmanager.py b/lib/matplotlib/texmanager.py index f58cacb11d6a..0f61dbdca441 100644 --- a/lib/matplotlib/texmanager.py +++ b/lib/matplotlib/texmanager.py @@ -62,8 +62,8 @@ class TexManager(object): # Caches. rgba_arrayd = {} grey_arrayd = {} - postscriptd = property(mpl.cbook.deprecated("2.2")(lambda self: {})) - pscnt = property(mpl.cbook.deprecated("2.2")(lambda self: 0)) + postscriptd = mpl.cbook.deprecated("2.2")(property(lambda self: {})) + pscnt = mpl.cbook.deprecated("2.2")(property(lambda self: 0)) serif = ('cmr', '') sans_serif = ('cmss', '')