Skip to content

Commit bb9aae4

Browse files
committed
Merge branch 'main' into text-overhaul
2 parents 67d1a02 + 33a0d07 commit bb9aae4

File tree

16 files changed

+432
-139
lines changed

16 files changed

+432
-139
lines changed

.github/workflows/cibuildwheel.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ jobs:
141141
path: dist/
142142

143143
- name: Build wheels for CPython 3.13
144-
uses: pypa/cibuildwheel@faf86a6ed7efa889faf6996aa23820831055001a # v2.23.3
144+
uses: pypa/cibuildwheel@5f22145df44122af0f5a201f93cf0207171beca7 # v3.0.0
145145
with:
146146
package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }}
147147
env:
@@ -152,15 +152,15 @@ jobs:
152152
CIBW_ARCHS: ${{ matrix.cibw_archs }}
153153

154154
- name: Build wheels for CPython 3.12
155-
uses: pypa/cibuildwheel@faf86a6ed7efa889faf6996aa23820831055001a # v2.23.3
155+
uses: pypa/cibuildwheel@5f22145df44122af0f5a201f93cf0207171beca7 # v3.0.0
156156
with:
157157
package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }}
158158
env:
159159
CIBW_BUILD: "cp312-*"
160160
CIBW_ARCHS: ${{ matrix.cibw_archs }}
161161

162162
- name: Build wheels for CPython 3.11
163-
uses: pypa/cibuildwheel@faf86a6ed7efa889faf6996aa23820831055001a # v2.23.3
163+
uses: pypa/cibuildwheel@5f22145df44122af0f5a201f93cf0207171beca7 # v3.0.0
164164
with:
165165
package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }}
166166
env:
@@ -169,7 +169,7 @@ jobs:
169169

170170

171171
- name: Build wheels for PyPy
172-
uses: pypa/cibuildwheel@faf86a6ed7efa889faf6996aa23820831055001a # v2.23.3
172+
uses: pypa/cibuildwheel@5f22145df44122af0f5a201f93cf0207171beca7 # v3.0.0
173173
with:
174174
package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }}
175175
env:
@@ -208,7 +208,7 @@ jobs:
208208
run: ls dist
209209

210210
- name: Generate artifact attestation for sdist and wheel
211-
uses: actions/attest-build-provenance@db473fddc028af60658334401dc6fa3ffd8669fd # v2.3.0
211+
uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2.4.0
212212
with:
213213
subject-path: dist/matplotlib-*
214214

.github/workflows/codeql-analysis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
persist-credentials: false
3333

3434
- name: Initialize CodeQL
35-
uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
35+
uses: github/codeql-action/init@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
3636
with:
3737
languages: ${{ matrix.language }}
3838

@@ -43,4 +43,4 @@ jobs:
4343
pip install --user -v .
4444
4545
- name: Perform CodeQL Analysis
46-
uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
46+
uses: github/codeql-action/analyze@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0

galleries/examples/pie_and_polar_charts/polar_demo.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,41 @@
44
==========
55
66
Demo of a line plot on a polar axis.
7+
8+
The second plot shows the same data, but with the radial axis starting at r=1
9+
and the angular axis starting at 0 degrees and ending at 225 degrees. Setting
10+
the origin of the radial axis to 0 allows the radial ticks to be placed at the
11+
same location as the first plot.
712
"""
813
import matplotlib.pyplot as plt
914
import numpy as np
1015

1116
r = np.arange(0, 2, 0.01)
1217
theta = 2 * np.pi * r
1318

14-
fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})
19+
fig, axs = plt.subplots(2, 1, figsize=(5, 8), subplot_kw={'projection': 'polar'},
20+
layout='constrained')
21+
ax = axs[0]
1522
ax.plot(theta, r)
1623
ax.set_rmax(2)
17-
ax.set_rticks([0.5, 1, 1.5, 2]) # Less radial ticks
24+
ax.set_rticks([0.5, 1, 1.5, 2]) # Fewer radial ticks
1825
ax.set_rlabel_position(-22.5) # Move radial labels away from plotted line
1926
ax.grid(True)
2027

2128
ax.set_title("A line plot on a polar axis", va='bottom')
29+
30+
ax = axs[1]
31+
ax.plot(theta, r)
32+
ax.set_rmax(2)
33+
ax.set_rmin(1) # Change the radial axis to only go from 1 to 2
34+
ax.set_rorigin(0) # Set the origin of the radial axis to 0
35+
ax.set_thetamin(0)
36+
ax.set_thetamax(225)
37+
ax.set_rticks([1, 1.5, 2]) # Fewer radial ticks
38+
ax.set_rlabel_position(-22.5) # Move radial labels away from plotted line
39+
40+
ax.grid(True)
41+
ax.set_title("Same plot, but with reduced axis limits", va='bottom')
2242
plt.show()
2343

2444
# %%
@@ -32,6 +52,8 @@
3252
# - `matplotlib.projections.polar`
3353
# - `matplotlib.projections.polar.PolarAxes`
3454
# - `matplotlib.projections.polar.PolarAxes.set_rticks`
55+
# - `matplotlib.projections.polar.PolarAxes.set_rmin`
56+
# - `matplotlib.projections.polar.PolarAxes.set_rorigin`
3557
# - `matplotlib.projections.polar.PolarAxes.set_rmax`
3658
# - `matplotlib.projections.polar.PolarAxes.set_rlabel_position`
3759
#

galleries/users_explain/text/fonts.py

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -27,30 +27,35 @@
2727
Matplotlib supports three font specifications (in addition to pdf 'core fonts',
2828
which are explained later in the guide):
2929
30-
.. list-table:: Type of Fonts
31-
:header-rows: 1
32-
33-
* - Type 1 (PDF)
34-
- Type 3 (PDF/PS)
35-
- TrueType (PDF)
36-
* - One of the oldest types, introduced by Adobe
37-
- Similar to Type 1 in terms of introduction
38-
- Newer than previous types, used commonly today, introduced by Apple
39-
* - Restricted subset of PostScript, charstrings are in bytecode
40-
- Full PostScript language, allows embedding arbitrary code
41-
(in theory, even render fractals when rasterizing!)
42-
- Include a virtual machine that can execute code!
43-
* - These fonts support font hinting
44-
- Do not support font hinting
45-
- Hinting supported (virtual machine processes the "hints")
46-
* - Non-subsetted through Matplotlib
47-
- Subsetted via external module ttconv
48-
- Subsetted via external module
49-
`fontTools <https://github.com/fonttools/fonttools>`__
30+
.. table:: Type of Fonts
31+
32+
+--------------------------+----------------------------+----------------------------+
33+
| Type 1 (PDF with usetex) | Type 3 (PDF/PS) | TrueType (PDF) |
34+
+==========================+============================+============================+
35+
| One of the oldest types, | Similar to Type 1 in | Newer than previous types, |
36+
| introduced by Adobe | terms of introduction | used commonly today, |
37+
| | | introduced by Apple |
38+
+--------------------------+----------------------------+----------------------------+
39+
| Restricted subset of | Full PostScript language, | Includes a virtual machine |
40+
| PostScript, charstrings | allows embedding arbitrary | that can execute code! |
41+
| are in bytecode | code (in theory, even | |
42+
| | render fractals when | |
43+
| | rasterizing!) | |
44+
+--------------------------+----------------------------+----------------------------+
45+
| Supports font | Does not support font | Supports font hinting |
46+
| hinting | hinting | (virtual machine processes |
47+
| | | the "hints") |
48+
+--------------------------+----------------------------+----------------------------+
49+
| Subsetted by code in | Subsetted via external module |
50+
| `matplotlib._type1font` | `fontTools <https://github.com/fonttools/fonttools>`__ |
51+
+--------------------------+----------------------------+----------------------------+
5052
5153
.. note::
5254
5355
Adobe disabled__ support for authoring with Type 1 fonts in January 2023.
56+
Matplotlib uses Type 1 fonts for compatibility with TeX; when the usetex
57+
feature is used with the PDF backend, Matplotlib reads the fonts used by
58+
the TeX engine, which are usually Type 1.
5459
5560
__ https://helpx.adobe.com/fonts/kb/postscript-type-1-fonts-end-of-support.html
5661
@@ -83,14 +88,12 @@
8388
files, particularly with fonts with many glyphs such as those that support CJK
8489
(Chinese/Japanese/Korean).
8590
86-
The solution to this problem is to subset the fonts used in the document and
87-
only embed the glyphs actually used. This gets both vector text and small
88-
files sizes. Computing the subset of the font required and writing the new
89-
(reduced) font are both complex problem and thus Matplotlib relies on
90-
`fontTools <https://fonttools.readthedocs.io/en/latest/>`__ and a vendored fork
91-
of ttconv.
92-
93-
Currently Type 3, Type 42, and TrueType fonts are subsetted. Type 1 fonts are not.
91+
To keep the output size reasonable while using vector fonts,
92+
Matplotlib embeds only the glyphs that are actually used in the document.
93+
This is known as font subsetting.
94+
Computing the font subset and writing the reduced font are both complex problems,
95+
which Matplotlib solves in most cases by using the
96+
`fontTools <https://fonttools.readthedocs.io/en/latest/>`__ library.
9497
9598
Core Fonts
9699
^^^^^^^^^^

lib/matplotlib/cbook.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2228,6 +2228,9 @@ def _g_sig_digits(value, delta):
22282228
Return the number of significant digits to %g-format *value*, assuming that
22292229
it is known with an error of *delta*.
22302230
"""
2231+
# For inf or nan, the precision doesn't matter.
2232+
if not math.isfinite(value):
2233+
return 0
22312234
if delta == 0:
22322235
if value == 0:
22332236
# if both value and delta are 0, np.spacing below returns 5e-324
@@ -2241,11 +2244,10 @@ def _g_sig_digits(value, delta):
22412244
# digits before the decimal point (floor(log10(45.67)) + 1 = 2): the total
22422245
# is 4 significant digits. A value of 0 contributes 1 "digit" before the
22432246
# decimal point.
2244-
# For inf or nan, the precision doesn't matter.
22452247
return max(
22462248
0,
22472249
(math.floor(math.log10(abs(value))) + 1 if value else 1)
2248-
- math.floor(math.log10(delta))) if math.isfinite(value) else 0
2250+
- math.floor(math.log10(delta)))
22492251

22502252

22512253
def _unikey_or_keysym_to_mplkey(unikey, keysym):

lib/matplotlib/sphinxext/plot_directive.py

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@
4747
4848
The ``.. plot::`` directive supports the following options:
4949
50+
``:filename-prefix:`` : str
51+
The base name (without the extension) of the outputted image and script
52+
files. The default is to use the same name as the input script, or the
53+
name of the RST document if no script is provided. The filename-prefix for
54+
each plot directive must be unique.
55+
5056
``:format:`` : {'python', 'doctest'}
5157
The format of the input. If unset, the format is auto-detected.
5258
@@ -163,8 +169,10 @@
163169
be customized by changing the *plot_template*. See the source of
164170
:doc:`/api/sphinxext_plot_directive_api` for the templates defined in *TEMPLATE*
165171
and *TEMPLATE_SRCSET*.
172+
166173
"""
167174

175+
from collections import defaultdict
168176
import contextlib
169177
import doctest
170178
from io import StringIO
@@ -182,6 +190,7 @@
182190
from docutils.parsers.rst.directives.images import Image
183191
import jinja2 # Sphinx dependency.
184192

193+
from sphinx.environment.collectors import EnvironmentCollector
185194
from sphinx.errors import ExtensionError
186195

187196
import matplotlib
@@ -265,6 +274,7 @@ class PlotDirective(Directive):
265274
'scale': directives.nonnegative_int,
266275
'align': Image.align,
267276
'class': directives.class_option,
277+
'filename-prefix': directives.unchanged,
268278
'include-source': _option_boolean,
269279
'show-source-link': _option_boolean,
270280
'format': _option_format,
@@ -312,9 +322,35 @@ def setup(app):
312322
app.connect('build-finished', _copy_css_file)
313323
metadata = {'parallel_read_safe': True, 'parallel_write_safe': True,
314324
'version': matplotlib.__version__}
325+
app.connect('builder-inited', init_filename_registry)
326+
app.add_env_collector(_FilenameCollector)
315327
return metadata
316328

317329

330+
# -----------------------------------------------------------------------------
331+
# Handle Duplicate Filenames
332+
# -----------------------------------------------------------------------------
333+
334+
def init_filename_registry(app):
335+
env = app.builder.env
336+
if not hasattr(env, 'mpl_plot_image_basenames'):
337+
env.mpl_plot_image_basenames = defaultdict(set)
338+
339+
340+
class _FilenameCollector(EnvironmentCollector):
341+
def process_doc(self, app, doctree):
342+
pass
343+
344+
def clear_doc(self, app, env, docname):
345+
if docname in env.mpl_plot_image_basenames:
346+
del env.mpl_plot_image_basenames[docname]
347+
348+
def merge_other(self, app, env, docnames, other):
349+
for docname in other.mpl_plot_image_basenames:
350+
env.mpl_plot_image_basenames[docname].update(
351+
other.mpl_plot_image_basenames[docname])
352+
353+
318354
# -----------------------------------------------------------------------------
319355
# Doctest handling
320356
# -----------------------------------------------------------------------------
@@ -600,6 +636,25 @@ def _parse_srcset(entries):
600636
return srcset
601637

602638

639+
def check_output_base_name(env, output_base):
640+
docname = env.docname
641+
642+
if '.' in output_base or '/' in output_base or '\\' in output_base:
643+
raise PlotError(
644+
f"The filename-prefix '{output_base}' is invalid. "
645+
f"It must not contain dots or slashes.")
646+
647+
for d in env.mpl_plot_image_basenames:
648+
if output_base in env.mpl_plot_image_basenames[d]:
649+
if d == docname:
650+
raise PlotError(
651+
f"The filename-prefix {output_base!r} is used multiple times.")
652+
raise PlotError(f"The filename-prefix {output_base!r} is used multiple"
653+
f"times (it is also used in {env.doc2path(d)}).")
654+
655+
env.mpl_plot_image_basenames[docname].add(output_base)
656+
657+
603658
def render_figures(code, code_path, output_dir, output_base, context,
604659
function_name, config, context_reset=False,
605660
close_figs=False,
@@ -722,7 +777,8 @@ def render_figures(code, code_path, output_dir, output_base, context,
722777

723778
def run(arguments, content, options, state_machine, state, lineno):
724779
document = state_machine.document
725-
config = document.settings.env.config
780+
env = document.settings.env
781+
config = env.config
726782
nofigs = 'nofigs' in options
727783

728784
if config.plot_srcset and setup.app.builder.name == 'singlehtml':
@@ -734,6 +790,7 @@ def run(arguments, content, options, state_machine, state, lineno):
734790

735791
options.setdefault('include-source', config.plot_include_source)
736792
options.setdefault('show-source-link', config.plot_html_show_source_link)
793+
options.setdefault('filename-prefix', None)
737794

738795
if 'class' in options:
739796
# classes are parsed into a list of string, and output by simply
@@ -775,14 +832,22 @@ def run(arguments, content, options, state_machine, state, lineno):
775832
function_name = None
776833

777834
code = Path(source_file_name).read_text(encoding='utf-8')
778-
output_base = os.path.basename(source_file_name)
835+
if options['filename-prefix']:
836+
output_base = options['filename-prefix']
837+
check_output_base_name(env, output_base)
838+
else:
839+
output_base = os.path.basename(source_file_name)
779840
else:
780841
source_file_name = rst_file
781842
code = textwrap.dedent("\n".join(map(str, content)))
782-
counter = document.attributes.get('_plot_counter', 0) + 1
783-
document.attributes['_plot_counter'] = counter
784-
base, ext = os.path.splitext(os.path.basename(source_file_name))
785-
output_base = '%s-%d.py' % (base, counter)
843+
if options['filename-prefix']:
844+
output_base = options['filename-prefix']
845+
check_output_base_name(env, output_base)
846+
else:
847+
base, ext = os.path.splitext(os.path.basename(source_file_name))
848+
counter = document.attributes.get('_plot_counter', 0) + 1
849+
document.attributes['_plot_counter'] = counter
850+
output_base = '%s-%d.py' % (base, counter)
786851
function_name = None
787852
caption = options.get('caption', '')
788853

@@ -846,7 +911,7 @@ def run(arguments, content, options, state_machine, state, lineno):
846911

847912
# save script (if necessary)
848913
if options['show-source-link']:
849-
Path(build_dir, output_base + source_ext).write_text(
914+
Path(build_dir, output_base + (source_ext or '.py')).write_text(
850915
doctest.script_from_examples(code)
851916
if source_file_name == rst_file and is_doctest
852917
else code,
@@ -906,7 +971,7 @@ def run(arguments, content, options, state_machine, state, lineno):
906971
# Not-None src_name signals the need for a source download in the
907972
# generated html
908973
if j == 0 and options['show-source-link']:
909-
src_name = output_base + source_ext
974+
src_name = output_base + (source_ext or '.py')
910975
else:
911976
src_name = None
912977
if config.plot_srcset:

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy