Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1cdc5aa

Browse files
Merge branch 'main' into pan-zoom-3d
2 parents 772b511 + 25b39d4 commit 1cdc5aa

File tree

21 files changed

+188
-62
lines changed

21 files changed

+188
-62
lines changed

.circleci/config.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,12 @@ commands:
153153
steps:
154154
- run:
155155
name: Bundle sphinx-gallery documentation artifacts
156-
command: tar cf doc/build/sphinx-gallery-files.tar.gz doc/api/_as_gen doc/gallery doc/tutorials
156+
command: >
157+
tar cf doc/build/sphinx-gallery-files.tar.gz
158+
doc/api/_as_gen
159+
doc/gallery
160+
doc/plot_types
161+
doc/tutorials
157162
when: always
158163
- store_artifacts:
159164
path: doc/build/sphinx-gallery-files.tar.gz

.github/workflows/nightlies.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ jobs:
4949
5050
# N.B. anaconda-client is only maintained on the main channel
5151
- name: Install anaconda-client
52-
uses: mamba-org/provision-with-micromamba@v12
52+
uses: mamba-org/provision-with-micromamba@v13
5353
with:
5454
environment-file: false
5555
environment-name: nightlies

doc/conf.py

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import shutil
1717
import subprocess
1818
import sys
19+
from urllib.parse import urlsplit, urlunsplit
1920
import warnings
2021

2122
import matplotlib
@@ -248,10 +249,6 @@ def matplotlib_reduced_latex_scraper(block, block_vars, gallery_conf,
248249
except (subprocess.CalledProcessError, FileNotFoundError):
249250
SHA = matplotlib.__version__
250251

251-
html_context = {
252-
"sha": SHA,
253-
}
254-
255252
project = 'Matplotlib'
256253
copyright = (
257254
'2002–2012 John Hunter, Darren Dale, Eric Firing, Michael Droettboom '
@@ -312,16 +309,50 @@ def matplotlib_reduced_latex_scraper(block, block_vars, gallery_conf,
312309

313310
github_project_url = "https://github.com/matplotlib/matplotlib/"
314311

312+
315313
# Options for HTML output
316314
# -----------------------
317315

316+
def add_html_cache_busting(app, pagename, templatename, context, doctree):
317+
"""
318+
Add cache busting query on CSS and JavaScript assets.
319+
320+
This adds the Matplotlib version as a query to the link reference in the
321+
HTML, if the path is not absolute (i.e., it comes from the `_static`
322+
directory) and doesn't already have a query.
323+
"""
324+
from sphinx.builders.html import Stylesheet, JavaScript
325+
326+
css_tag = context['css_tag']
327+
js_tag = context['js_tag']
328+
329+
def css_tag_with_cache_busting(css):
330+
if isinstance(css, Stylesheet) and css.filename is not None:
331+
url = urlsplit(css.filename)
332+
if not url.netloc and not url.query:
333+
url = url._replace(query=SHA)
334+
css = Stylesheet(urlunsplit(url), priority=css.priority,
335+
**css.attributes)
336+
return css_tag(css)
337+
338+
def js_tag_with_cache_busting(js):
339+
if isinstance(js, JavaScript) and js.filename is not None:
340+
url = urlsplit(js.filename)
341+
if not url.netloc and not url.query:
342+
url = url._replace(query=SHA)
343+
js = JavaScript(urlunsplit(url), priority=js.priority,
344+
**js.attributes)
345+
return js_tag(js)
346+
347+
context['css_tag'] = css_tag_with_cache_busting
348+
context['js_tag'] = js_tag_with_cache_busting
349+
350+
318351
# The style sheet to use for HTML and HTML Help pages. A file of that name
319352
# must exist either in Sphinx' static/ path, or in one of the custom paths
320353
# given in html_static_path.
321-
# html_style = 'matplotlib.css'
322-
# html_style = f"mpl.css?{SHA}"
323354
html_css_files = [
324-
f"mpl.css?{SHA}",
355+
"mpl.css",
325356
]
326357

327358
html_theme = "mpl_sphinx_theme"
@@ -574,14 +605,6 @@ def matplotlib_reduced_latex_scraper(block, block_vars, gallery_conf,
574605
# https://github.com/sphinx-doc/sphinx/issues/3176
575606
# graphviz_output_format = 'svg'
576607

577-
578-
def setup(app):
579-
if any(st in version for st in ('post', 'alpha', 'beta')):
580-
bld_type = 'dev'
581-
else:
582-
bld_type = 'rel'
583-
app.add_config_value('releaselevel', bld_type, 'env')
584-
585608
# -----------------------------------------------------------------------------
586609
# Source code links
587610
# -----------------------------------------------------------------------------
@@ -649,3 +672,15 @@ def linkcode_resolve(domain, info):
649672
f"/{tag}/lib/{fn}{linespec}")
650673
else:
651674
extensions.append('sphinx.ext.viewcode')
675+
676+
677+
# -----------------------------------------------------------------------------
678+
# Sphinx setup
679+
# -----------------------------------------------------------------------------
680+
def setup(app):
681+
if any(st in version for st in ('post', 'dev', 'alpha', 'beta')):
682+
bld_type = 'dev'
683+
else:
684+
bld_type = 'rel'
685+
app.add_config_value('releaselevel', bld_type, 'env')
686+
app.connect('html-page-context', add_html_cache_busting, priority=1000)

doc/users/explain/backends.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ allows both interactive work and plotting from scripts, with output to the
107107
screen and/or to a file, so at least initially, you will not need to worry
108108
about the backend. The most common exception is if your Python distribution
109109
comes without :mod:`tkinter` and you have no other GUI toolkit installed.
110-
This happens on certain Linux distributions, where you need to install a
110+
This happens with certain Linux distributions, where you need to install a
111111
Linux package named ``python-tk`` (or similar).
112112

113113
If, however, you want to write graphical user interfaces, or a web

doc/users/explain/interactive.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ and run the GUI event loop for the specified period of time.
169169

170170
The GUI event loop being integrated with your command prompt and
171171
the figures being in interactive mode are independent of each other.
172-
If you use `pyplot.ion` but have not arranged for the event loop integration,
172+
If you try to use `pyplot.ion` without arranging for the event-loop integration,
173173
your figures will appear but will not be interactive while the prompt is waiting for input.
174174
You will not be able to pan/zoom and the figure may not even render
175175
(the window might appear black, transparent, or as a snapshot of the

doc/users/faq/troubleshooting_faq.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ the tracker so the issue doesn't get lost.
157157
Problems with recent git versions
158158
=================================
159159

160-
First make sure you have a clean build and install (see :ref:`clean-install`),
160+
First, make sure you have a clean build and install (see :ref:`clean-install`),
161161
get the latest git update, install it and run a simple test script in debug
162162
mode::
163163

doc/users/next_whats_new/3d_plot_aspects.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Users can set the aspect ratio for the X, Y, Z axes of a 3D plot to be 'equal',
2727
for i, ax in enumerate(axs):
2828
ax.set_box_aspect((3, 4, 5))
2929
ax.set_aspect(aspects[i])
30-
ax.set_title("set_aspect('{aspects[i]}')")
30+
ax.set_title(f"set_aspect('{aspects[i]}')")
3131

32+
fig.set_size_inches(13, 3)
3233
plt.show()
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
*adjustable* keyword argument for setting equal aspect ratios in 3D
2+
-------------------------------------------------------------------
3+
4+
While setting equal aspect ratios for 3D plots, users can choose to modify
5+
either the data limits or the bounding box.
6+
7+
.. plot::
8+
:include-source: true
9+
10+
import matplotlib.pyplot as plt
11+
import numpy as np
12+
from itertools import combinations, product
13+
14+
aspects = ('auto', 'equal', 'equalxy', 'equalyz', 'equalxz')
15+
fig, axs = plt.subplots(1, len(aspects), subplot_kw={'projection': '3d'},
16+
figsize=(12, 6))
17+
18+
# Draw rectangular cuboid with side lengths [4, 3, 5]
19+
r = [0, 1]
20+
scale = np.array([4, 3, 5])
21+
pts = combinations(np.array(list(product(r, r, r))), 2)
22+
for start, end in pts:
23+
if np.sum(np.abs(start - end)) == r[1] - r[0]:
24+
for ax in axs:
25+
ax.plot3D(*zip(start*scale, end*scale), color='C0')
26+
27+
# Set the aspect ratios
28+
for i, ax in enumerate(axs):
29+
ax.set_aspect(aspects[i], adjustable='datalim')
30+
# Alternatively: ax.set_aspect(aspects[i], adjustable='box')
31+
# which will change the box aspect ratio instead of axis data limits.
32+
ax.set_title(f"set_aspect('{aspects[i]}')")
33+
34+
plt.show()

doc/users/next_whats_new/3d_plot_focal_length.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ The focal length can be calculated from a desired FOV via the equation:
1919

2020
from mpl_toolkits.mplot3d import axes3d
2121
import matplotlib.pyplot as plt
22-
fig, axs = plt.subplots(1, 3, subplot_kw={'projection': '3d'},
23-
constrained_layout=True)
22+
from numpy import inf
23+
fig, axs = plt.subplots(1, 3, subplot_kw={'projection': '3d'})
2424
X, Y, Z = axes3d.get_test_data(0.05)
25-
focal_lengths = [0.25, 1, 4]
25+
focal_lengths = [0.2, 1, inf]
2626
for ax, fl in zip(axs, focal_lengths):
2727
ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10)
2828
ax.set_proj_type('persp', focal_length=fl)
2929
ax.set_title(f"focal_length = {fl}")
30-
plt.tight_layout()
30+
fig.set_size_inches(10, 4)
3131
plt.show()

doc/users/next_whats_new/3d_plot_roll_angle.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ existing 3D plots.
1818
X, Y, Z = axes3d.get_test_data(0.05)
1919
ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10)
2020
ax.view_init(elev=0, azim=0, roll=30)
21+
ax.set_title('elev=0, azim=0, roll=30')
2122
plt.show()

doc/users/next_whats_new/font_fallback.rst

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
Font fallback in Agg
2-
--------------------
1+
Font fallback
2+
-------------
33

4-
It is now possible to specify a list of fonts families and the Agg renderer
4+
It is now possible to specify a list of fonts families and Matplotlib
55
will try them in order to locate a required glyph.
66

77
.. plot::
@@ -23,5 +23,5 @@ will try them in order to locate a required glyph.
2323
plt.show()
2424

2525

26-
This currently only works with the Agg backend, but support for the vector
27-
backends is planned for Matplotlib 3.7.
26+
This currently works with the Agg (and all of the GUI embeddings), svg, pdf,
27+
ps, and inline backends.

examples/animation/animation_demo.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020

2121
fig, ax = plt.subplots()
2222

23-
for i in range(len(data)):
24-
ax.cla()
25-
ax.imshow(data[i])
26-
ax.set_title("frame {}".format(i))
23+
for i, img in enumerate(data):
24+
ax.clear()
25+
ax.imshow(img)
26+
ax.set_title(f"frame {i}")
2727
# Note that using time.sleep does *not* work here!
2828
plt.pause(0.1)

examples/event_handling/data_browser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def update(self):
7575

7676
dataind = self.lastind
7777

78-
ax2.cla()
78+
ax2.clear()
7979
ax2.plot(X[dataind])
8080

8181
ax2.text(0.05, 0.9, f'mu={xs[dataind]:1.3f}\nsigma={ys[dataind]:1.3f}',

lib/matplotlib/cm.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,8 @@ def _ensure_cmap(cmap):
701701
"""
702702
if isinstance(cmap, colors.Colormap):
703703
return cmap
704-
return mpl.colormaps[
705-
cmap if cmap is not None else mpl.rcParams['image.cmap']
706-
]
704+
cmap_name = cmap if cmap is not None else mpl.rcParams["image.cmap"]
705+
# use check_in_list to ensure type stability of the exception raised by
706+
# the internal usage of this (ValueError vs KeyError)
707+
_api.check_in_list(sorted(_colormaps), cmap=cmap_name)
708+
return mpl.colormaps[cmap_name]

lib/matplotlib/dates.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -525,8 +525,7 @@ def num2date(x, tz=None):
525525
526526
Notes
527527
-----
528-
The addition of one here is a historical artifact. Also, note that the
529-
Gregorian calendar is assumed; this is not universal practice.
528+
The Gregorian calendar is assumed; this is not universal practice.
530529
For details, see the module docstring.
531530
"""
532531
tz = _get_tzinfo(tz)
@@ -866,13 +865,13 @@ class AutoDateFormatter(ticker.Formatter):
866865
defaults to ::
867866
868867
self.scaled = {
869-
DAYS_PER_YEAR: rcParams['date.autoformat.year'],
870-
DAYS_PER_MONTH: rcParams['date.autoformat.month'],
871-
1: rcParams['date.autoformat.day'],
872-
1 / HOURS_PER_DAY: rcParams['date.autoformat.hour'],
873-
1 / MINUTES_PER_DAY: rcParams['date.autoformat.minute'],
874-
1 / SEC_PER_DAY: rcParams['date.autoformat.second'],
875-
1 / MUSECONDS_PER_DAY: rcParams['date.autoformat.microsecond'],
868+
DAYS_PER_YEAR: rcParams['date.autoformatter.year'],
869+
DAYS_PER_MONTH: rcParams['date.autoformatter.month'],
870+
1: rcParams['date.autoformatter.day'],
871+
1 / HOURS_PER_DAY: rcParams['date.autoformatter.hour'],
872+
1 / MINUTES_PER_DAY: rcParams['date.autoformatter.minute'],
873+
1 / SEC_PER_DAY: rcParams['date.autoformatter.second'],
874+
1 / MUSECONDS_PER_DAY: rcParams['date.autoformatter.microsecond'],
876875
}
877876
878877
The formatter uses the format string corresponding to the lowest key in

lib/matplotlib/legend.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1140,7 +1140,7 @@ def _get_legend_handles(axs, legend_handler_map=None):
11401140
label = handle.get_label()
11411141
if label != '_nolegend_' and has_handler(handler_map, handle):
11421142
yield handle
1143-
elif (label not in ['_nolegend_', ''] and
1143+
elif (label and not label.startswith('_') and
11441144
not has_handler(handler_map, handle)):
11451145
_api.warn_external(
11461146
"Legend does not support handles for {0} "

lib/matplotlib/tests/test_colors.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1543,3 +1543,11 @@ def test_color_sequences():
15431543
plt.color_sequences.unregister('rgb') # multiple unregisters are ok
15441544
with pytest.raises(ValueError, match="Cannot unregister builtin"):
15451545
plt.color_sequences.unregister('tab10')
1546+
1547+
1548+
def test_cm_set_cmap_error():
1549+
sm = cm.ScalarMappable()
1550+
# Pick a name we are pretty sure will never be a colormap name
1551+
bad_cmap = 'AardvarksAreAwkward'
1552+
with pytest.raises(ValueError, match=bad_cmap):
1553+
sm.set_cmap(bad_cmap)

lib/matplotlib/tests/test_legend.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import collections
22
import platform
33
from unittest import mock
4+
import warnings
45

56
import numpy as np
67
import pytest
@@ -515,6 +516,13 @@ def test_text_nohandler_warning():
515516
ax.legend()
516517
assert len(record) == 1
517518

519+
# this should _not_ warn:
520+
f, ax = plt.subplots()
521+
ax.pcolormesh(np.random.uniform(0, 1, (10, 10)))
522+
with warnings.catch_warnings():
523+
warnings.simplefilter("error")
524+
ax.get_legend_handles_labels()
525+
518526

519527
def test_empty_bar_chart_with_legend():
520528
"""Test legend when bar chart is empty with a label."""

0 commit comments

Comments
 (0)