From c4dddc459f7ff1b53892f2a5eb8d2a4fb57c202c Mon Sep 17 00:00:00 2001 From: Nelle Varoquaux Date: Sat, 14 Jan 2017 12:32:53 -0800 Subject: [PATCH 1/3] MAINT moved _backports to cbook module --- lib/matplotlib/_backports/__init__.py | 3 - .../{cbook.py => cbook/__init__.py} | 215 +---------------- .../numpy.py => cbook/_backports.py} | 0 lib/matplotlib/cbook/deprecation.py | 220 ++++++++++++++++++ lib/mpl_toolkits/mplot3d/art3d.py | 4 +- setupext.py | 2 +- 6 files changed, 225 insertions(+), 219 deletions(-) delete mode 100644 lib/matplotlib/_backports/__init__.py rename lib/matplotlib/{cbook.py => cbook/__init__.py} (92%) rename lib/matplotlib/{_backports/numpy.py => cbook/_backports.py} (100%) create mode 100644 lib/matplotlib/cbook/deprecation.py diff --git a/lib/matplotlib/_backports/__init__.py b/lib/matplotlib/_backports/__init__.py deleted file mode 100644 index 8b3efda1bf57..000000000000 --- a/lib/matplotlib/_backports/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -""" -Backported features from dependencies. -""" diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook/__init__.py similarity index 92% rename from lib/matplotlib/cbook.py rename to lib/matplotlib/cbook/__init__.py index 8e31699142e1..1b471b17fbdc 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook/__init__.py @@ -28,223 +28,12 @@ import types import warnings from weakref import ref, WeakKeyDictionary +from .deprecation import deprecated, warn_deprecated +from .deprecation import mplDeprecation, MatplotlibDeprecationWarning import numpy as np -class MatplotlibDeprecationWarning(UserWarning): - """ - A class for issuing deprecation warnings for Matplotlib users. - - In light of the fact that Python builtin DeprecationWarnings are ignored - by default as of Python 2.7 (see link below), this class was put in to - allow for the signaling of deprecation, but via UserWarnings which are not - ignored by default. - - https://docs.python.org/dev/whatsnew/2.7.html#the-future-for-python-2-x - """ - pass - -mplDeprecation = MatplotlibDeprecationWarning - - -def _generate_deprecation_message(since, message='', name='', - alternative='', pending=False, - obj_type='attribute', - addendum=''): - - if not message: - - if pending: - message = ( - 'The %(name)s %(obj_type)s will be deprecated in a ' - 'future version.') - else: - message = ( - 'The %(name)s %(obj_type)s was deprecated in version ' - '%(since)s.') - - altmessage = '' - if alternative: - altmessage = ' Use %s instead.' % alternative - - message = ((message % { - 'func': name, - 'name': name, - 'alternative': alternative, - 'obj_type': obj_type, - 'since': since}) + - altmessage) - - if addendum: - message += addendum - - return message - - -def warn_deprecated( - since, message='', name='', alternative='', pending=False, - obj_type='attribute', addendum=''): - """ - Used to display deprecation warning in a standard way. - - Parameters - ---------- - since : str - The release at which this API became deprecated. - - message : str, optional - Override the default deprecation message. The format - specifier `%(name)s` may be used for the name of the function, - and `%(alternative)s` may be used in the deprecation message - to insert the name of an alternative to the deprecated - function. `%(obj_type)s` may be used to insert a friendly name - for the type of object being deprecated. - - name : str, optional - The name of the deprecated object. - - alternative : str, optional - An alternative function that the user may use in place of the - deprecated function. The deprecation warning will tell the user - about this alternative if provided. - - pending : bool, optional - If True, uses a PendingDeprecationWarning instead of a - DeprecationWarning. - - obj_type : str, optional - The object type being deprecated. - - addendum : str, optional - Additional text appended directly to the final message. - - Examples - -------- - - Basic example:: - - # To warn of the deprecation of "matplotlib.name_of_module" - warn_deprecated('1.4.0', name='matplotlib.name_of_module', - obj_type='module') - - """ - message = _generate_deprecation_message( - since, message, name, alternative, pending, obj_type) - - warnings.warn(message, mplDeprecation, stacklevel=1) - - -def deprecated(since, message='', name='', alternative='', pending=False, - obj_type=None, addendum=''): - """ - Decorator to mark a function or a class as deprecated. - - Parameters - ---------- - since : str - The release at which this API became deprecated. This is - required. - - message : str, optional - Override the default deprecation message. The format - specifier `%(name)s` may be used for the name of the object, - and `%(alternative)s` may be used in the deprecation message - to insert the name of an alternative to the deprecated - object. `%(obj_type)s` may be used to insert a friendly name - for the type of object being deprecated. - - name : str, optional - The name of the deprecated object; if not provided the name - is automatically determined from the passed in object, - though this is useful in the case of renamed functions, where - the new function is just assigned to the name of the - deprecated function. For example:: - - def new_function(): - ... - oldFunction = new_function - - alternative : str, optional - An alternative object that the user may use in place of the - deprecated object. The deprecation warning will tell the user - about this alternative if provided. - - pending : bool, optional - If True, uses a PendingDeprecationWarning instead of a - DeprecationWarning. - - addendum : str, optional - Additional text appended directly to the final message. - - Examples - -------- - - Basic example:: - - @deprecated('1.4.0') - def the_function_to_deprecate(): - pass - - """ - def deprecate(obj, message=message, name=name, alternative=alternative, - pending=pending, addendum=addendum): - import textwrap - - if not name: - name = obj.__name__ - - if isinstance(obj, type): - obj_type = "class" - old_doc = obj.__doc__ - func = obj.__init__ - def finalize(wrapper, new_doc): - try: - obj.__doc__ = new_doc - except AttributeError: - pass # cls.__doc__ is not writeable on Py2. - obj.__init__ = wrapper - return obj - else: - obj_type = "function" - if isinstance(obj, classmethod): - func = obj.__func__ - old_doc = func.__doc__ - def finalize(wrapper, new_doc): - wrapper = functools.wraps(func)(wrapper) - wrapper.__doc__ = new_doc - return classmethod(wrapper) - else: - func = obj - old_doc = func.__doc__ - def finalize(wrapper, new_doc): - wrapper = functools.wraps(func)(wrapper) - wrapper.__doc__ = new_doc - return wrapper - - message = _generate_deprecation_message( - since, message, name, alternative, pending, - obj_type, addendum) - - def wrapper(*args, **kwargs): - warnings.warn(message, mplDeprecation, stacklevel=2) - return func(*args, **kwargs) - - old_doc = textwrap.dedent(old_doc or '').strip('\n') - message = message.strip() - new_doc = (('\n.. deprecated:: %(since)s' - '\n %(message)s\n\n' % - {'since': since, 'message': message}) + old_doc) - if not old_doc: - # This is to prevent a spurious 'unexected unindent' warning from - # docutils when the original docstring was blank. - new_doc += r'\ ' - - return finalize(wrapper, new_doc) - - return deprecate - - # On some systems, locale.getpreferredencoding returns None, # which can break unicode; and the sage project reports that # some systems have incorrect locale specifications, e.g., diff --git a/lib/matplotlib/_backports/numpy.py b/lib/matplotlib/cbook/_backports.py similarity index 100% rename from lib/matplotlib/_backports/numpy.py rename to lib/matplotlib/cbook/_backports.py diff --git a/lib/matplotlib/cbook/deprecation.py b/lib/matplotlib/cbook/deprecation.py new file mode 100644 index 000000000000..98159b77b2c8 --- /dev/null +++ b/lib/matplotlib/cbook/deprecation.py @@ -0,0 +1,220 @@ +import warnings +import functools + + +class MatplotlibDeprecationWarning(UserWarning): + """ + A class for issuing deprecation warnings for Matplotlib users. + + In light of the fact that Python builtin DeprecationWarnings are ignored + by default as of Python 2.7 (see link below), this class was put in to + allow for the signaling of deprecation, but via UserWarnings which are not + ignored by default. + + https://docs.python.org/dev/whatsnew/2.7.html#the-future-for-python-2-x + """ + pass + + +mplDeprecation = MatplotlibDeprecationWarning + + +def _generate_deprecation_message(since, message='', name='', + alternative='', pending=False, + obj_type='attribute', + addendum=''): + + if not message: + + if pending: + message = ( + 'The %(name)s %(obj_type)s will be deprecated in a ' + 'future version.') + else: + message = ( + 'The %(name)s %(obj_type)s was deprecated in version ' + '%(since)s.') + + altmessage = '' + if alternative: + altmessage = ' Use %s instead.' % alternative + + message = ((message % { + 'func': name, + 'name': name, + 'alternative': alternative, + 'obj_type': obj_type, + 'since': since}) + + altmessage) + + if addendum: + message += addendum + + return message + + +def warn_deprecated( + since, message='', name='', alternative='', pending=False, + obj_type='attribute', addendum=''): + """ + Used to display deprecation warning in a standard way. + + Parameters + ---------- + since : str + The release at which this API became deprecated. + + message : str, optional + Override the default deprecation message. The format + specifier `%(name)s` may be used for the name of the function, + and `%(alternative)s` may be used in the deprecation message + to insert the name of an alternative to the deprecated + function. `%(obj_type)s` may be used to insert a friendly name + for the type of object being deprecated. + + name : str, optional + The name of the deprecated object. + + alternative : str, optional + An alternative function that the user may use in place of the + deprecated function. The deprecation warning will tell the user + about this alternative if provided. + + pending : bool, optional + If True, uses a PendingDeprecationWarning instead of a + DeprecationWarning. + + obj_type : str, optional + The object type being deprecated. + + addendum : str, optional + Additional text appended directly to the final message. + + Examples + -------- + + Basic example:: + + # To warn of the deprecation of "matplotlib.name_of_module" + warn_deprecated('1.4.0', name='matplotlib.name_of_module', + obj_type='module') + + """ + message = _generate_deprecation_message( + since, message, name, alternative, pending, obj_type) + + warnings.warn(message, mplDeprecation, stacklevel=1) + + +def deprecated(since, message='', name='', alternative='', pending=False, + obj_type=None, addendum=''): + """ + Decorator to mark a function or a class as deprecated. + + Parameters + ---------- + since : str + The release at which this API became deprecated. This is + required. + + message : str, optional + Override the default deprecation message. The format + specifier `%(name)s` may be used for the name of the object, + and `%(alternative)s` may be used in the deprecation message + to insert the name of an alternative to the deprecated + object. `%(obj_type)s` may be used to insert a friendly name + for the type of object being deprecated. + + name : str, optional + The name of the deprecated object; if not provided the name + is automatically determined from the passed in object, + though this is useful in the case of renamed functions, where + the new function is just assigned to the name of the + deprecated function. For example:: + + def new_function(): + ... + oldFunction = new_function + + alternative : str, optional + An alternative object that the user may use in place of the + deprecated object. The deprecation warning will tell the user + about this alternative if provided. + + pending : bool, optional + If True, uses a PendingDeprecationWarning instead of a + DeprecationWarning. + + addendum : str, optional + Additional text appended directly to the final message. + + Examples + -------- + + Basic example:: + + @deprecated('1.4.0') + def the_function_to_deprecate(): + pass + + """ + + def deprecate(obj, message=message, name=name, alternative=alternative, + pending=pending, addendum=addendum): + import textwrap + + if not name: + name = obj.__name__ + + if isinstance(obj, type): + obj_type = "class" + old_doc = obj.__doc__ + func = obj.__init__ + + def finalize(wrapper, new_doc): + try: + obj.__doc__ = new_doc + except AttributeError: + pass # cls.__doc__ is not writeable on Py2. + obj.__init__ = wrapper + return obj + else: + obj_type = "function" + if isinstance(obj, classmethod): + func = obj.__func__ + old_doc = func.__doc__ + + def finalize(wrapper, new_doc): + wrapper = functools.wraps(func)(wrapper) + wrapper.__doc__ = new_doc + return classmethod(wrapper) + else: + func = obj + old_doc = func.__doc__ + + def finalize(wrapper, new_doc): + wrapper = functools.wraps(func)(wrapper) + wrapper.__doc__ = new_doc + return wrapper + + message = _generate_deprecation_message( + since, message, name, alternative, pending, + obj_type, addendum) + + def wrapper(*args, **kwargs): + warnings.warn(message, mplDeprecation, stacklevel=2) + return func(*args, **kwargs) + + old_doc = textwrap.dedent(old_doc or '').strip('\n') + message = message.strip() + new_doc = (('\n.. deprecated:: %(since)s' + '\n %(message)s\n\n' % + {'since': since, 'message': message}) + old_doc) + if not old_doc: + # This is to prevent a spurious 'unexected unindent' warning from + # docutils when the original docstring was blank. + new_doc += r'\ ' + + return finalize(wrapper, new_doc) + + return deprecate diff --git a/lib/mpl_toolkits/mplot3d/art3d.py b/lib/mpl_toolkits/mplot3d/art3d.py index 22b8416f5a4d..54d585bb645b 100644 --- a/lib/mpl_toolkits/mplot3d/art3d.py +++ b/lib/mpl_toolkits/mplot3d/art3d.py @@ -14,7 +14,7 @@ from matplotlib import ( artist, cbook, colors as mcolors, lines, text as mtext, path as mpath) -from matplotlib._backports import numpy as _backports_np +from matplotlib.cbook import _backports from matplotlib.collections import ( Collection, LineCollection, PolyCollection, PatchCollection, PathCollection) @@ -774,7 +774,7 @@ def iscolor(c): def get_colors(c, num): """Stretch the color argument to provide the required number num""" - return _backports_np.broadcast_to( + return _backports.broadcast_to( mcolors.to_rgba_array(c) if len(c) else [0, 0, 0, 0], (num, 4)) diff --git a/setupext.py b/setupext.py index 34ac8b384e52..66b5efd31946 100644 --- a/setupext.py +++ b/setupext.py @@ -705,7 +705,6 @@ def check(self): def get_packages(self): return [ 'matplotlib', - 'matplotlib._backports', 'matplotlib.backends', 'matplotlib.backends.qt_editor', 'matplotlib.compat', @@ -718,6 +717,7 @@ def get_packages(self): 'matplotlib.testing.nose.plugins', 'matplotlib.testing.jpl_units', 'matplotlib.tri', + 'matplotlib.cbook' ] def get_py_modules(self): From a28d1c5b569c8aba0ee2ce29af461c1be921cf51 Mon Sep 17 00:00:00 2001 From: Nelle Varoquaux Date: Thu, 19 Jan 2017 16:37:53 -0800 Subject: [PATCH 2/3] FIX pep8 compliance --- lib/matplotlib/cbook/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/matplotlib/cbook/__init__.py b/lib/matplotlib/cbook/__init__.py index 1b471b17fbdc..b8acc03b5269 100644 --- a/lib/matplotlib/cbook/__init__.py +++ b/lib/matplotlib/cbook/__init__.py @@ -830,6 +830,7 @@ def mkdirs(newdir, mode=0o777): if exception.errno != errno.EEXIST: raise + class GetRealpathAndStat(object): def __init__(self): self._cache = {} @@ -846,6 +847,8 @@ def __call__(self, path): result = realpath, stat_key self._cache[path] = result return result + + get_realpath_and_stat = GetRealpathAndStat() @@ -932,6 +935,7 @@ def wrap(prefix, text, cols): ret += pad + ' '.join(line) + '\n' return ret + # A regular expression used to determine the amount of space to # remove. It looks for the first sequence of spaces immediately # following the first newline, or at the beginning of the string. @@ -1098,10 +1102,6 @@ def onetrue(seq): def allpairs(x): """ return all possible pairs in sequence *x* - - Condensed by Alex Martelli from this thread_ on c.l.python - - .. _thread: http://groups.google.com/groups?q=all+pairs+group:*python*&hl=en&lr=&ie=UTF-8&selm=mailman.4028.1096403649.5135.python-list%40python.org&rnum=1 """ return [(s, f) for i, f in enumerate(x) for s in x[i + 1:]] @@ -1563,7 +1563,7 @@ def recursive_remove(path): os.removedirs(fname) else: os.remove(fname) - #os.removedirs(path) + # os.removedirs(path) else: os.remove(path) @@ -1850,7 +1850,6 @@ def _compute_conf_interval(data, med, iqr, bootstrap): # add in the remaining stats stats['q1'], stats['med'], stats['q3'] = q1, med, q3 - return bxpstats @@ -2413,6 +2412,7 @@ def get_label(y, default_name): except AttributeError: return default_name + _lockstr = """\ LOCKERROR: matplotlib is trying to acquire the lock {!r} From aca81ac0139885c52f5dd2f2964b18c8d8f62a48 Mon Sep 17 00:00:00 2001 From: Nelle Varoquaux Date: Thu, 19 Jan 2017 19:03:22 -0800 Subject: [PATCH 3/3] FIX removed cbook.py from our pep8 uncompliant file --- lib/matplotlib/tests/test_coding_standards.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/matplotlib/tests/test_coding_standards.py b/lib/matplotlib/tests/test_coding_standards.py index 353720ccd221..b1f28e12b3ca 100644 --- a/lib/matplotlib/tests/test_coding_standards.py +++ b/lib/matplotlib/tests/test_coding_standards.py @@ -178,7 +178,6 @@ def test_pep8_conformance_installed_files(): expected_bad_files = ['_cm.py', '_mathtext_data.py', 'backend_bases.py', - 'cbook.py', 'collections.py', 'font_manager.py', 'fontconfig_pattern.py',