diff --git a/.flake8 b/.flake8 index b139c87d0490..630d7dfda16e 100644 --- a/.flake8 +++ b/.flake8 @@ -23,7 +23,7 @@ exclude = per-file-ignores = setup.py: E402 - setupext.py: E302, E501 + setupext.py: E501 tools/compare_backend_driver_results.py: E501 tools/subset.py: E221, E231, E251, E261, E302, E501, E701 diff --git a/doc-requirements.txt b/doc-requirements.txt index 58a5c698043b..c0ee2014cf64 100644 --- a/doc-requirements.txt +++ b/doc-requirements.txt @@ -6,7 +6,7 @@ # Install the documentation requirements with: # pip install -r doc-requirements.txt # -sphinx>=1.3,!=1.5.0,!=1.6.4,!=1.7.3,<1.8 +sphinx>=1.3,!=1.5.0,!=1.6.4,!=1.7.3 colorspacious ipython ipywidgets diff --git a/doc/conf.py b/doc/conf.py index 5b2395702e62..c67b3f335ee5 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -84,7 +84,10 @@ def _check_deps(): autosummary_generate = True autodoc_docstring_signature = True -autodoc_default_flags = ['members', 'undoc-members'] +if sphinx.version_info < (1, 8): + autodoc_default_flags = ['members', 'undoc-members'] +else: + autodoc_default_options = {'members': None, 'undoc-members': None} intersphinx_mapping = { 'python': ('https://docs.python.org/3', None), diff --git a/doc/sphinxext/github.py b/doc/sphinxext/github.py index 8f0ffc0d9782..75c5ce10ae9d 100644 --- a/doc/sphinxext/github.py +++ b/doc/sphinxext/github.py @@ -75,7 +75,6 @@ def ghissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]): prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] app = inliner.document.settings.env.app - #app.info('issue %r' % text) if 'pull' in name.lower(): category = 'pull' elif 'issue' in name.lower(): @@ -105,7 +104,6 @@ def ghuser_role(name, rawtext, text, lineno, inliner, options={}, content=[]): :param content: The directive content for customization. """ app = inliner.document.settings.env.app - #app.info('user link %r' % text) ref = 'https://www.github.com/' + text node = nodes.reference(rawtext, text, refuri=ref, **options) return [node], [] @@ -126,7 +124,6 @@ def ghcommit_role(name, rawtext, text, lineno, inliner, options={}, content=[]): :param content: The directive content for customization. """ app = inliner.document.settings.env.app - #app.info('user link %r' % text) try: base = app.config.github_project_url if not base: @@ -146,7 +143,6 @@ def setup(app): :param app: Sphinx application context. """ - app.info('Initializing GitHub plugin') app.add_role('ghissue', ghissue_role) app.add_role('ghpull', ghissue_role) app.add_role('ghuser', ghuser_role) diff --git a/doc/users/next_whats_new/2018-09-06-AL.rst b/doc/users/next_whats_new/2018-09-06-AL.rst deleted file mode 100644 index dc0ca84f3252..000000000000 --- a/doc/users/next_whats_new/2018-09-06-AL.rst +++ /dev/null @@ -1,12 +0,0 @@ -:orphan: - -Return type of ArtistInspector.get_aliases changed -`````````````````````````````````````````````````` - -`ArtistInspector.get_aliases` previously returned the set of aliases as -``{fullname: {alias1: None, alias2: None, ...}}``. The dict-to-None mapping -was used to simulate a set in earlier versions of Python. It has now been -replaced by a set, i.e. ``{fullname: {alias1, alias2, ...}}``. - -This value is also stored in `ArtistInspector.aliasd`, which has likewise -changed. diff --git a/doc/users/whats_new.rst b/doc/users/whats_new.rst index 4f89851070b5..2c130030dded 100644 --- a/doc/users/whats_new.rst +++ b/doc/users/whats_new.rst @@ -239,6 +239,29 @@ Headless linux servers (identified by the DISPLAY env not being defined) will not select a GUI backend. +Return type of ArtistInspector.get_aliases changed +`````````````````````````````````````````````````` + +`ArtistInspector.get_aliases` previously returned the set of aliases as +``{fullname: {alias1: None, alias2: None, ...}}``. The dict-to-None mapping +was used to simulate a set in earlier versions of Python. It has now been +replaced by a set, i.e. ``{fullname: {alias1, alias2, ...}}``. + +This value is also stored in `ArtistInspector.aliasd`, which has likewise +changed. + + +``:math:`` directive renamed to ``:mathmpl:`` +````````````````````````````````````````````` + +The ``:math:`` rst role provided by `matplotlib.sphinxext.mathmpl` has been +renamed to ``:mathmpl:`` to avoid conflicting with the ``:math:`` role that +Sphinx 1.8 provides by default. (``:mathmpl:`` uses Matplotlib to render math +expressions to images embedded in html, whereas Sphinx uses MathJax.) + +When using Sphinx<1.8, both names (``:math:`` and ``:mathmpl:``) remain +available for backcompatibility. + ================== Previous Whats New diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index a198cb5fe66c..3df090452a02 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -267,7 +267,8 @@ def __call__(self): vmin = self._colorbar.norm.vmin vmax = self._colorbar.norm.vmax ticks = ticker.AutoMinorLocator.__call__(self) - return ticks[(ticks >= vmin) & (ticks <= vmax)] + rtol = (vmax - vmin) * 1e-10 + return ticks[(ticks >= vmin - rtol) & (ticks <= vmax + rtol)] class _ColorbarLogLocator(ticker.LogLocator): diff --git a/lib/matplotlib/sphinxext/mathmpl.py b/lib/matplotlib/sphinxext/mathmpl.py index aef2fb877bb9..b1b934304a76 100644 --- a/lib/matplotlib/sphinxext/mathmpl.py +++ b/lib/matplotlib/sphinxext/mathmpl.py @@ -1,10 +1,11 @@ +import hashlib import os import sys -from hashlib import md5 +import warnings from docutils import nodes from docutils.parsers.rst import directives -import warnings +import sphinx from matplotlib import rcParams from matplotlib.mathtext import MathTextParser @@ -61,7 +62,7 @@ def latex2png(latex, filename, fontset='cm'): def latex2html(node, source): inline = isinstance(node.parent, nodes.TextElement) latex = node['latex'] - name = 'math-%s' % md5(latex.encode()).hexdigest()[-10:] + name = 'math-%s' % hashlib.md5(latex.encode()).hexdigest()[-10:] destdir = os.path.join(setup.app.builder.outdir, '_images', 'mathmpl') if not os.path.exists(destdir): @@ -110,9 +111,13 @@ def depart_latex_math_latex(self, node): app.add_node(latex_math, html=(visit_latex_math_html, depart_latex_math_html), latex=(visit_latex_math_latex, depart_latex_math_latex)) - app.add_role('math', math_role) - app.add_directive('math', math_directive, + app.add_role('mathmpl', math_role) + app.add_directive('mathmpl', math_directive, True, (0, 0, 0), **options_spec) + if sphinx.version_info < (1, 8): + app.add_role('math', math_role) + app.add_directive('math', math_directive, + True, (0, 0, 0), **options_spec) metadata = {'parallel_read_safe': True, 'parallel_write_safe': True} return metadata diff --git a/lib/matplotlib/tests/test_colorbar.py b/lib/matplotlib/tests/test_colorbar.py index 2230c20e7d2f..56a829418910 100644 --- a/lib/matplotlib/tests/test_colorbar.py +++ b/lib/matplotlib/tests/test_colorbar.py @@ -294,6 +294,15 @@ def test_colorbar_minorticks_on_off(): np.testing.assert_almost_equal(cbar.ax.yaxis.get_minorticklocs(), np.array([])) + im.set_clim(vmin=-1.2, vmax=1.2) + cbar.minorticks_on() + correct_minorticklocs = np.array([-1.2, -1.1, -0.9, -0.8, -0.7, -0.6, + -0.4, -0.3, -0.2, -0.1, 0.1, 0.2, + 0.3, 0.4, 0.6, 0.7, 0.8, 0.9, + 1.1, 1.2]) + np.testing.assert_almost_equal(cbar.ax.yaxis.get_minorticklocs(), + correct_minorticklocs) + def test_colorbar_autoticks(): # Test new autotick modes. Needs to be classic because diff --git a/setup.cfg.template b/setup.cfg.template index 7ccfb7edcbe4..4fed27d31302 100644 --- a/setup.cfg.template +++ b/setup.cfg.template @@ -63,15 +63,9 @@ # behavior # #agg = auto -#cairo = auto -#gtk3agg = auto -#gtk3cairo = auto #macosx = auto -#pyside = auto -#qt4agg = auto #tkagg = auto #windowing = auto -#wxagg = auto [rc_options] # User-configurable options @@ -81,10 +75,9 @@ # # The Agg, Ps, Pdf and SVG backends do not require external dependencies. Do # not choose MacOSX, or TkAgg if you have disabled the relevant extension -# modules. Agg will be used by default. +# modules. The default is determined by fallback. # #backend = Agg -# [package_data] # Package additional files found in the lib/matplotlib directories. diff --git a/setup.py b/setup.py index 6eab9b6b88fd..7e82e94c29c8 100644 --- a/setup.py +++ b/setup.py @@ -70,18 +70,9 @@ setupext.Tests(), setupext.Toolkits_Tests(), 'Optional backend extensions', - # These backends are listed in order of preference, the first - # being the most preferred. The first one that looks like it will - # work will be selected as the default backend. - setupext.BackendMacOSX(), - setupext.BackendQt5(), - setupext.BackendQt4(), - setupext.BackendGtk3Agg(), - setupext.BackendGtk3Cairo(), - setupext.BackendTkAgg(), - setupext.BackendWxAgg(), setupext.BackendAgg(), - setupext.BackendCairo(), + setupext.BackendTkAgg(), + setupext.BackendMacOSX(), setupext.Windowing(), 'Optional package data', setupext.Dlls(), @@ -133,7 +124,6 @@ def run(self): package_dir = {'': 'lib'} install_requires = [] setup_requires = [] - default_backend = None # If the user just queries for information, don't bother figuring out which # packages to build or install. @@ -169,10 +159,6 @@ def run(self): required_failed.append(package) else: good_packages.append(package) - if (isinstance(package, setupext.OptionalBackendPackage) - and package.runtime_check() - and default_backend is None): - default_backend = package.name print_raw('') # Abort if any of the required packages can not be built. @@ -203,14 +189,16 @@ def run(self): setup_requires.extend(package.get_setup_requires()) # Write the default matplotlibrc file - if default_backend is None: - default_backend = 'svg' - if setupext.options['backend']: - default_backend = setupext.options['backend'] with open('matplotlibrc.template') as fd: - template = fd.read() + template_lines = fd.read().splitlines(True) + backend_line_idx, = [ # Also asserts that there is a single such line. + idx for idx, line in enumerate(template_lines) + if line.startswith('#backend ')] + if setupext.options['backend']: + template_lines[backend_line_idx] = ( + 'backend: {}'.format(setupext.options['backend'])) with open('lib/matplotlib/mpl-data/matplotlibrc', 'w') as fd: - fd.write(template) + fd.write(''.join(template_lines)) # Finalize the extension modules so they can get the Numpy include # dirs diff --git a/setupext.py b/setupext.py index 5644bae9dab5..582c7d89c9da 100644 --- a/setupext.py +++ b/setupext.py @@ -384,13 +384,6 @@ def check(self): """ pass - def runtime_check(self): - """ - True if the runtime dependencies of the backend are met. Assumes that - the build-time dependencies are met. - """ - return True - def get_packages(self): """ Get a list of package names to add to the configuration. @@ -1368,10 +1361,6 @@ class BackendTkAgg(OptionalBackendPackage): def check(self): return "installing; run-time loading from Python Tcl / Tk" - def runtime_check(self): - """Checks whether TkAgg runtime dependencies are met.""" - return importlib.util.find_spec("tkinter") is not None - def get_extension(self): sources = [ 'src/_tkagg.cpp' @@ -1391,57 +1380,6 @@ def add_flags(self, ext): ext.libraries.extend(['dl']) -class BackendGtk3Agg(OptionalBackendPackage): - name = "gtk3agg" - - def check_requirements(self): - if not any(map(importlib.util.find_spec, ["cairocffi", "cairo"])): - raise CheckFailed("Requires cairocffi or pycairo to be installed.") - - try: - import gi - except ImportError: - raise CheckFailed("Requires pygobject to be installed.") - - try: - gi.require_version("Gtk", "3.0") - except ValueError: - raise CheckFailed( - "Requires gtk3 development files to be installed.") - except AttributeError: - raise CheckFailed("pygobject version too old.") - - try: - from gi.repository import Gtk, Gdk, GObject - except (ImportError, RuntimeError): - raise CheckFailed("Requires pygobject to be installed.") - - return "version {}.{}.{}".format( - Gtk.get_major_version(), - Gtk.get_minor_version(), - Gtk.get_micro_version()) - - def get_package_data(self): - return {'matplotlib': ['mpl-data/*.glade']} - - -class BackendGtk3Cairo(BackendGtk3Agg): - name = "gtk3cairo" - - -class BackendWxAgg(OptionalBackendPackage): - name = "wxagg" - - def check_requirements(self): - try: - import wx - backend_version = wx.VERSION_STRING - except ImportError: - raise CheckFailed("requires wxPython") - - return "version %s" % backend_version - - class BackendMacOSX(OptionalBackendPackage): name = 'macosx' @@ -1487,174 +1425,6 @@ def get_extension(self): return ext -class BackendQtBase(OptionalBackendPackage): - - def convert_qt_version(self, version): - version = '%x' % version - temp = [] - while len(version) > 0: - version, chunk = version[:-2], version[-2:] - temp.insert(0, str(int(chunk, 16))) - return '.'.join(temp) - - def check_requirements(self): - """ - If PyQt4/PyQt5 is already imported, importing PyQt5/PyQt4 will fail - so we need to test in a subprocess (as for Gtk3). - """ - try: - p = multiprocessing.Pool() - - except: - # Can't do multiprocessing, fall back to normal approach - # (this will fail if importing both PyQt4 and PyQt5). - try: - # Try in-process - msg = self.callback(self) - except RuntimeError: - raise CheckFailed( - "Could not import: are PyQt4 & PyQt5 both installed?") - - else: - # Multiprocessing OK - try: - res = p.map_async(self.callback, [self]) - msg = res.get(timeout=10)[0] - except multiprocessing.TimeoutError: - p.terminate() - # No result returned. Probably hanging, terminate the process. - raise CheckFailed("Check timed out") - except: - # Some other error. - p.close() - raise - else: - # Clean exit - p.close() - finally: - # Tidy up multiprocessing - p.join() - - return msg - - -def backend_pyside_internal_check(self): - try: - from PySide import __version__ - from PySide import QtCore - except ImportError: - raise CheckFailed("PySide not found") - else: - return ("Qt: %s, PySide: %s" % - (QtCore.__version__, __version__)) - - -def backend_pyqt4_internal_check(self): - try: - from PyQt4 import QtCore - except ImportError: - raise CheckFailed("PyQt4 not found") - - try: - qt_version = QtCore.QT_VERSION - pyqt_version_str = QtCore.PYQT_VERSION_STR - except AttributeError: - raise CheckFailed('PyQt4 not correctly imported') - else: - return ("Qt: %s, PyQt: %s" % (self.convert_qt_version(qt_version), pyqt_version_str)) - - -def backend_qt4_internal_check(self): - successes = [] - failures = [] - try: - successes.append(backend_pyside_internal_check(self)) - except CheckFailed as e: - failures.append(str(e)) - - try: - successes.append(backend_pyqt4_internal_check(self)) - except CheckFailed as e: - failures.append(str(e)) - - if len(successes) == 0: - raise CheckFailed('; '.join(failures)) - return '; '.join(successes + failures) - - -class BackendQt4(BackendQtBase): - name = "qt4agg" - - def __init__(self, *args, **kwargs): - BackendQtBase.__init__(self, *args, **kwargs) - self.callback = backend_qt4_internal_check - -def backend_pyside2_internal_check(self): - try: - from PySide2 import __version__ - from PySide2 import QtCore - except ImportError: - raise CheckFailed("PySide2 not found") - else: - return ("Qt: %s, PySide2: %s" % - (QtCore.__version__, __version__)) - -def backend_pyqt5_internal_check(self): - try: - from PyQt5 import QtCore - except ImportError: - raise CheckFailed("PyQt5 not found") - - try: - qt_version = QtCore.QT_VERSION - pyqt_version_str = QtCore.PYQT_VERSION_STR - except AttributeError: - raise CheckFailed('PyQt5 not correctly imported') - else: - return ("Qt: %s, PyQt: %s" % (self.convert_qt_version(qt_version), pyqt_version_str)) - -def backend_qt5_internal_check(self): - successes = [] - failures = [] - try: - successes.append(backend_pyside2_internal_check(self)) - except CheckFailed as e: - failures.append(str(e)) - - try: - successes.append(backend_pyqt5_internal_check(self)) - except CheckFailed as e: - failures.append(str(e)) - - if len(successes) == 0: - raise CheckFailed('; '.join(failures)) - return '; '.join(successes + failures) - -class BackendQt5(BackendQtBase): - name = "qt5agg" - - def __init__(self, *args, **kwargs): - BackendQtBase.__init__(self, *args, **kwargs) - self.callback = backend_qt5_internal_check - - -class BackendCairo(OptionalBackendPackage): - name = "cairo" - - def check_requirements(self): - try: - import cairocffi - except ImportError: - try: - import cairo - except ImportError: - raise CheckFailed("cairocffi or pycairo not found") - else: - return "pycairo version %s" % cairo.version - else: - return "cairocffi version %s" % cairocffi.version - - class OptionalPackageData(OptionalPackage): config_category = "package_data" diff --git a/tutorials/text/mathtext.py b/tutorials/text/mathtext.py index 9fafdcf1ed64..4ed6e297c73e 100644 --- a/tutorials/text/mathtext.py +++ b/tutorials/text/mathtext.py @@ -44,7 +44,7 @@ # # math text # plt.title(r'$\alpha > \beta$') # -# produces ":math:`\alpha > \beta`". +# produces ":mathmpl:`\alpha > \beta`". # # .. note:: # Mathtext should be placed between a pair of dollar signs ($). To @@ -77,9 +77,9 @@ # # \alpha_i > \beta_i # -# Some symbols automatically put their sub/superscripts under and over -# the operator. For example, to write the sum of :math:`x_i` from :math:`0` to -# :math:`\infty`, you could do:: +# Some symbols automatically put their sub/superscripts under and over the +# operator. For example, to write the sum of :mathmpl:`x_i` from :mathmpl:`0` +# to :mathmpl:`\infty`, you could do:: # # r'$\sum_{i=0}^\infty x_i$' # @@ -200,13 +200,13 @@ # ============================ ================================== # Command Result # ============================ ================================== -# ``\mathrm{Roman}`` :math:`\mathrm{Roman}` -# ``\mathit{Italic}`` :math:`\mathit{Italic}` -# ``\mathtt{Typewriter}`` :math:`\mathtt{Typewriter}` -# ``\mathcal{CALLIGRAPHY}`` :math:`\mathcal{CALLIGRAPHY}` +# ``\mathrm{Roman}`` :mathmpl:`\mathrm{Roman}` +# ``\mathit{Italic}`` :mathmpl:`\mathit{Italic}` +# ``\mathtt{Typewriter}`` :mathmpl:`\mathtt{Typewriter}` +# ``\mathcal{CALLIGRAPHY}`` :mathmpl:`\mathcal{CALLIGRAPHY}` # ============================ ================================== # -# .. role:: math-stix(math) +# .. role:: math-stix(mathmpl) # :fontset: stix # # When using the `STIX `_ fonts, you also have the choice of: @@ -294,16 +294,16 @@ # ============================== ================================= # Command Result # ============================== ================================= -# ``\acute a`` or ``\'a`` :math:`\acute a` -# ``\bar a`` :math:`\bar a` -# ``\breve a`` :math:`\breve a` -# ``\ddot a`` or ``\''a`` :math:`\ddot a` -# ``\dot a`` or ``\.a`` :math:`\dot a` -# ``\grave a`` or ``\`a`` :math:`\grave a` -# ``\hat a`` or ``\^a`` :math:`\hat a` -# ``\tilde a`` or ``\~a`` :math:`\tilde a` -# ``\vec a`` :math:`\vec a` -# ``\overline{abc}`` :math:`\overline{abc}` +# ``\acute a`` or ``\'a`` :mathmpl:`\acute a` +# ``\bar a`` :mathmpl:`\bar a` +# ``\breve a`` :mathmpl:`\breve a` +# ``\ddot a`` or ``\''a`` :mathmpl:`\ddot a` +# ``\dot a`` or ``\.a`` :mathmpl:`\dot a` +# ``\grave a`` or ``\`a`` :mathmpl:`\grave a` +# ``\hat a`` or ``\^a`` :mathmpl:`\hat a` +# ``\tilde a`` or ``\~a`` :mathmpl:`\tilde a` +# ``\vec a`` :mathmpl:`\vec a` +# ``\overline{abc}`` :mathmpl:`\overline{abc}` # ============================== ================================= # # In addition, there are two special accents that automatically adjust @@ -312,8 +312,8 @@ # ============================== ================================= # Command Result # ============================== ================================= -# ``\widehat{xyz}`` :math:`\widehat{xyz}` -# ``\widetilde{xyz}`` :math:`\widetilde{xyz}` +# ``\widehat{xyz}`` :mathmpl:`\widehat{xyz}` +# ``\widetilde{xyz}`` :mathmpl:`\widetilde{xyz}` # ============================== ================================= # # Care should be taken when putting accents on lower-case i's and j's.