Skip to content

Commit e43b222

Browse files
committed
Proof of concept for deprecation of global non-callables.
Triggered by the recent discussion on deprecating matplotlib.verbose. This ensures that a deprecation warning is triggered just by trying to access matplotlib.verbose (or importing it). The implementation idea is based on njsmith's metamodule, but without the hacks needed to support Py<3.5. Py3.7+ will provide instance-`__getattr__` on all classes by default, making the `_DeprecatorModuleType` patching unnecessary (but the warning emitter would still need to be implemented, so there is actually fairly limited improvement from that).
1 parent c85b029 commit e43b222

File tree

2 files changed

+43
-0
lines changed

2 files changed

+43
-0
lines changed

lib/matplotlib/__init__.py

+6
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,12 @@ def ge(self, level):
391391
return self.vald[self.level] >= self.vald[level]
392392

393393

394+
from matplotlib.cbook.deprecation import _deprecated_global
395+
with warnings.catch_warnings():
396+
warnings.simplefilter("ignore")
397+
_deprecated_global("verbose", Verbose(), "2.2")
398+
399+
394400
def _wrap(fmt, func, level='DEBUG', always=True):
395401
"""
396402
return a callable function that wraps func and reports its

lib/matplotlib/cbook/deprecation.py

+37
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import functools
2+
import sys
23
import textwrap
4+
from types import ModuleType
35
import warnings
46

57

@@ -221,3 +223,38 @@ def wrapper(*args, **kwargs):
221223
return finalize(wrapper, new_doc)
222224

223225
return deprecate
226+
227+
228+
class _DeprecatorModuleType(ModuleType):
229+
def __getattr__(self, name):
230+
try:
231+
val, message = self._deprecated_dict[name]
232+
warnings.warn(message, MatplotlibDeprecationWarning, stacklevel=2)
233+
return val
234+
except KeyError:
235+
raise AttributeError(
236+
"Module {!r} has no attibute {!r}"
237+
.format(self.__name__, name)) from None
238+
239+
240+
def _deprecated_global(
241+
name, obj, since, *,
242+
message="", alternative="", pending=False, obj_type="object",
243+
addendum="", removal=""):
244+
frame = sys._getframe(1)
245+
if frame.f_locals is not frame.f_globals:
246+
raise RuntimeError(
247+
"Matplotlib internal error: cannot globally deprecate object from "
248+
"non-global frame")
249+
mod = sys.modules[frame.f_globals["__name__"]]
250+
if type(mod) == ModuleType:
251+
mod.__class__ = _DeprecatorModuleType
252+
elif mod.__class__ != _DeprecatorModuleType:
253+
warnings.warn("Matplotlib internal error: cannot deprecate global of "
254+
"patched module. Assigning attribute normally.")
255+
mod.__dict__[name] = obj
256+
return
257+
message = _generate_deprecation_message(
258+
since=since, message=message, name=name, alternative=alternative,
259+
pending=pending, obj_type=obj_type, removal=removal)
260+
mod.__dict__.setdefault("_deprecated_dict", {})[name] = (obj, message)

0 commit comments

Comments
 (0)