diff --git a/.appveyor.yml b/.appveyor.yml index a637fe545466..c3fcb0ea9591 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -17,7 +17,7 @@ skip_commits: clone_depth: 50 -image: Visual Studio 2019 +image: Visual Studio 2022 environment: diff --git a/.github/workflows/cibuildwheel.yml b/.github/workflows/cibuildwheel.yml index 57f1c71c5047..15ec0e405400 100644 --- a/.github/workflows/cibuildwheel.yml +++ b/.github/workflows/cibuildwheel.yml @@ -140,19 +140,31 @@ jobs: name: cibw-sdist path: dist/ + - name: Build wheels for CPython 3.14 + uses: pypa/cibuildwheel@95d2f3a92fbf80abe066b09418bbf128a8923df2 # v3.0.1 + with: + package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} + env: + CIBW_BUILD: "cp314-* cp314t-*" + CIBW_ENABLE: "cpython-freethreading cpython-prerelease" + CIBW_ARCHS: ${{ matrix.cibw_archs }} + CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28 + CIBW_BEFORE_TEST: >- + python -m pip install + --index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple + --upgrade --pre --only-binary=:all: contourpy numpy pillow + - name: Build wheels for CPython 3.13 - uses: pypa/cibuildwheel@5f22145df44122af0f5a201f93cf0207171beca7 # v3.0.0 + uses: pypa/cibuildwheel@95d2f3a92fbf80abe066b09418bbf128a8923df2 # v3.0.1 with: package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} env: CIBW_BUILD: "cp313-* cp313t-*" CIBW_ENABLE: cpython-freethreading - # No free-threading wheels available for aarch64 on Pillow. - CIBW_TEST_SKIP: "cp313t-manylinux_aarch64" CIBW_ARCHS: ${{ matrix.cibw_archs }} - name: Build wheels for CPython 3.12 - uses: pypa/cibuildwheel@5f22145df44122af0f5a201f93cf0207171beca7 # v3.0.0 + uses: pypa/cibuildwheel@95d2f3a92fbf80abe066b09418bbf128a8923df2 # v3.0.1 with: package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} env: @@ -160,24 +172,21 @@ jobs: CIBW_ARCHS: ${{ matrix.cibw_archs }} - name: Build wheels for CPython 3.11 - uses: pypa/cibuildwheel@5f22145df44122af0f5a201f93cf0207171beca7 # v3.0.0 + uses: pypa/cibuildwheel@95d2f3a92fbf80abe066b09418bbf128a8923df2 # v3.0.1 with: package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} env: CIBW_BUILD: "cp311-*" CIBW_ARCHS: ${{ matrix.cibw_archs }} - - name: Build wheels for PyPy - uses: pypa/cibuildwheel@5f22145df44122af0f5a201f93cf0207171beca7 # v3.0.0 + uses: pypa/cibuildwheel@95d2f3a92fbf80abe066b09418bbf128a8923df2 # v3.0.1 with: package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} env: CIBW_BUILD: "pp311-*" CIBW_ARCHS: ${{ matrix.cibw_archs }} CIBW_ENABLE: pypy - # No wheels available for Pillow with pp311 yet. - CIBW_TEST_SKIP: "pp311*" if: matrix.cibw_archs != 'aarch64' && matrix.os != 'windows-latest' - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index c05454884b55..d6d1eba02560 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -32,7 +32,7 @@ jobs: persist-credentials: false - name: Initialize CodeQL - uses: github/codeql-action/init@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0 + uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 with: languages: ${{ matrix.language }} @@ -43,4 +43,4 @@ jobs: pip install --user -v . - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0 + uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml index 4a5b79c0538e..a52343c5d22c 100644 --- a/.github/workflows/cygwin.yml +++ b/.github/workflows/cygwin.yml @@ -84,7 +84,7 @@ jobs: fetch-depth: 0 persist-credentials: false - - uses: cygwin/cygwin-install-action@f61179d72284ceddc397ed07ddb444d82bf9e559 # v5 + - uses: cygwin/cygwin-install-action@f2009323764960f80959895c7bc3bb30210afe4d # v6 with: packages: >- ccache gcc-g++ gdb git graphviz libcairo-devel libffi-devel diff --git a/ci/schemas/conda-environment.json b/ci/schemas/conda-environment.json index 458676942a44..fb1e821778c3 100644 --- a/ci/schemas/conda-environment.json +++ b/ci/schemas/conda-environment.json @@ -1,6 +1,6 @@ { "title": "conda environment file", - "description": "Support for conda's enviroment.yml files (e.g. `conda env export > environment.yml`)", + "description": "Support for conda's environment.yml files (e.g. `conda env export > environment.yml`)", "id": "https://raw.githubusercontent.com/Microsoft/vscode-python/main/schemas/conda-environment.json", "$schema": "http://json-schema.org/draft-04/schema#", "definitions": { diff --git a/doc/api/colors_api.rst b/doc/api/colors_api.rst index 6b02f723d74d..49a42c8f9601 100644 --- a/doc/api/colors_api.rst +++ b/doc/api/colors_api.rst @@ -21,6 +21,7 @@ Color norms :toctree: _as_gen/ :template: autosummary.rst + Norm Normalize NoNorm AsinhNorm diff --git a/doc/api/next_api_changes/behavior/30272-ES.rst b/doc/api/next_api_changes/behavior/30272-ES.rst new file mode 100644 index 000000000000..5a03f9bc7972 --- /dev/null +++ b/doc/api/next_api_changes/behavior/30272-ES.rst @@ -0,0 +1,2 @@ +``font_manager.findfont`` logs if selected font weight does not match requested +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/api/prev_api_changes/api_changes_0.90.1.rst b/doc/api/prev_api_changes/api_changes_0.90.1.rst index 89311d4ed102..8caef5e35ced 100644 --- a/doc/api/prev_api_changes/api_changes_0.90.1.rst +++ b/doc/api/prev_api_changes/api_changes_0.90.1.rst @@ -32,7 +32,7 @@ Changes for 0.90.1 named units.ConversionInterface.convert. Axes.errorbar uses Axes.vlines and Axes.hlines to draw its error - limits int he vertical and horizontal direction. As you'll see + limits in the vertical and horizontal direction. As you'll see in the changes below, these functions now return a LineCollection rather than a list of lines. The new return signature for errorbar is ylins, caplines, errorcollections where diff --git a/doc/api/prev_api_changes/api_changes_1.5.0.rst b/doc/api/prev_api_changes/api_changes_1.5.0.rst index b482d8bd7acd..513971098b93 100644 --- a/doc/api/prev_api_changes/api_changes_1.5.0.rst +++ b/doc/api/prev_api_changes/api_changes_1.5.0.rst @@ -189,7 +189,7 @@ algorithm that was not necessarily applicable to custom Axes. Three new private methods, ``matplotlib.axes._base._AxesBase._get_view``, ``matplotlib.axes._base._AxesBase._set_view``, and ``matplotlib.axes._base._AxesBase._set_view_from_bbox``, allow for custom -*Axes* classes to override the pan and zoom algorithms. Implementors of +*Axes* classes to override the pan and zoom algorithms. Implementers of custom *Axes* who override these methods may provide suitable behaviour for both pan and zoom as well as the view navigation buttons on the interactive toolbars. diff --git a/doc/devel/MEP/MEP14.rst b/doc/devel/MEP/MEP14.rst index 2c696adf8a58..d79d3c2d3115 100644 --- a/doc/devel/MEP/MEP14.rst +++ b/doc/devel/MEP/MEP14.rst @@ -78,7 +78,7 @@ number of other projects: - `Microsoft DirectWrite`_ - `Apple Core Text`_ -.. _pango: https://pango.gnome.org +.. _pango: https://github.com/GNOME/pango .. _harfbuzz: https://github.com/harfbuzz/harfbuzz .. _QtTextLayout: https://doc.qt.io/archives/qt-4.8/qtextlayout.html .. _Microsoft DirectWrite: https://docs.microsoft.com/en-ca/windows/win32/directwrite/introducing-directwrite diff --git a/doc/devel/tag_guidelines.rst b/doc/devel/tag_guidelines.rst index 2c80065982bc..2ff77d5279d5 100644 --- a/doc/devel/tag_guidelines.rst +++ b/doc/devel/tag_guidelines.rst @@ -61,7 +61,7 @@ Proposing new tags 1. Review existing tag list, looking out for similar entries (i.e. ``axes`` and ``axis``). 2. If a relevant tag or subcategory does not yet exist, propose it. Each tag is two parts: ``subcategory: tag``. Tags should be one or two words. -3. New tags should be be added when they are relevant to existing gallery entries too. +3. New tags should be added when they are relevant to existing gallery entries too. Avoid tags that will link to only a single gallery entry. 4. Tags can recreate other forms of organization. diff --git a/doc/missing-references.json b/doc/missing-references.json index efe676afbb85..1a3693c990e5 100644 --- a/doc/missing-references.json +++ b/doc/missing-references.json @@ -122,8 +122,12 @@ "doc/api/_as_gen/mpl_toolkits.axisartist.floating_axes.rst:32::1" ], "numpy.float64": [ + "doc/docstring of matplotlib.ft2font.pybind11_detail_function_record_v1_system_libstdcpp_gxx_abi_1xxx_use_cxx11_abi_1.set_text:1", "doc/docstring of matplotlib.ft2font.PyCapsule.set_text:1" ], + "numpy.typing.NDArray": [ + "doc/docstring of matplotlib.ft2font.pybind11_detail_function_record_v1_system_libstdcpp_gxx_abi_1xxx_use_cxx11_abi_1.set_text:1" + ], "numpy.uint8": [ ":1" ] diff --git a/doc/sphinxext/redirect_from.py b/doc/sphinxext/redirect_from.py index 37b56373a3bf..329352b3a3c8 100644 --- a/doc/sphinxext/redirect_from.py +++ b/doc/sphinxext/redirect_from.py @@ -94,10 +94,15 @@ def run(self): domain = self.env.get_domain('redirect_from') current_doc = self.env.path2doc(self.state.document.current_source) redirected_reldoc, _ = self.env.relfn2path(redirected_doc, current_doc) - if redirected_reldoc in domain.redirects: + if ( + redirected_reldoc in domain.redirects + and domain.redirects[redirected_reldoc] != current_doc + ): raise ValueError( f"{redirected_reldoc} is already noted as redirecting to " - f"{domain.redirects[redirected_reldoc]}") + f"{domain.redirects[redirected_reldoc]}\n" + f"Cannot also redirect it to {current_doc}" + ) domain.redirects[redirected_reldoc] = current_doc return [] diff --git a/doc/users/faq.rst b/doc/users/faq.rst index b08bd75cee4e..c6bbc5ca8d87 100644 --- a/doc/users/faq.rst +++ b/doc/users/faq.rst @@ -281,8 +281,23 @@ locators as desired because the two axes are independent. Generate images without having a window appear ---------------------------------------------- -Simply do not call `~matplotlib.pyplot.show`, and directly save the figure to -the desired format:: +The recommended approach since matplotlib 3.1 is to explicitly create a Figure +instance:: + + from matplotlib.figure import Figure + fig = Figure() + ax = fig.subplots() + ax.plot([1, 2, 3]) + fig.savefig('myfig.png') + +This prevents any interaction with GUI frameworks and the window manager. + +It's alternatively still possible to use the pyplot interface. Instead of +calling `matplotlib.pyplot.show`, call `matplotlib.pyplot.savefig`. + +Additionally, you must ensure to close the figure after saving it. Not +closing the figure is a memory leak, because pyplot keeps references +to all not-yet-shown figures:: import matplotlib.pyplot as plt plt.plot([1, 2, 3]) diff --git a/doc/users/prev_whats_new/changelog.rst b/doc/users/prev_whats_new/changelog.rst index 8f505e4fdd37..93fd4df2200a 100644 --- a/doc/users/prev_whats_new/changelog.rst +++ b/doc/users/prev_whats_new/changelog.rst @@ -1689,7 +1689,7 @@ recent changes, please refer to the :doc:`/users/release_notes`. required by the experimental traited config and are somewhat out of date. If needed, install them independently, see http://code.enthought.com/pages/traits.html and - http://www.voidspace.org.uk/python/configobj.html + https://configobj.readthedocs.io/en/latest/ 2008-12-12 Added support to assign labels to histograms of multiple data. - MM diff --git a/doc/users/prev_whats_new/github_stats_3.0.0.rst b/doc/users/prev_whats_new/github_stats_3.0.0.rst index 0e9c4b3b588d..cae3ee9b570d 100644 --- a/doc/users/prev_whats_new/github_stats_3.0.0.rst +++ b/doc/users/prev_whats_new/github_stats_3.0.0.rst @@ -595,7 +595,7 @@ Pull Requests (598): * :ghpull:`11757`: PGF backend output text color even if black * :ghpull:`11751`: Remove the unused 'verbose' option from setupext. * :ghpull:`9084`: Require calling a _BoundMethodProxy to get the underlying callable. -* :ghpull:`11752`: Fix section level of Previous Whats New +* :ghpull:`11752`: Fix section level of Previous What's New * :ghpull:`10513`: Replace most uses of getfilesystemencoding by os.fs{en,de}code. * :ghpull:`11739`: fix tight_layout bug #11737 * :ghpull:`11744`: minor doc update on axes_grid1's inset_axes @@ -899,7 +899,7 @@ Pull Requests (598): * :ghpull:`11075`: Drop alpha channel when saving comparison failure diff image. * :ghpull:`9022`: Help tool * :ghpull:`11045`: Help tool. -* :ghpull:`11076`: Don't create texput.{aux,log} in rootdir everytime tests are run. +* :ghpull:`11076`: Don't create texput.{aux,log} in rootdir every time tests are run * :ghpull:`11073`: py3fication of some tests. * :ghpull:`11074`: bytes % args is back since py3.5 * :ghpull:`11066`: Use chained comparisons where reasonable. @@ -1138,7 +1138,7 @@ Issues (123): * :ghissue:`11373`: Passing an incorrectly sized colour list to scatter should raise a relevant error * :ghissue:`11756`: pgf backend doesn't set color of text when the color is black * :ghissue:`11766`: test_axes.py::test_csd_freqs failing with numpy 1.15.0 on macOS -* :ghissue:`11750`: previous whats new is overindented on "what's new in mpl3.0 page" +* :ghissue:`11750`: previous what's new is overindented on "what's new in mpl3.0 page" * :ghissue:`11728`: Qt5 Segfaults on window resize * :ghissue:`11709`: Repaint region is wrong on Retina display with Qt5 * :ghissue:`11578`: wx segfaulting on OSX travis tests @@ -1149,7 +1149,7 @@ Issues (123): * :ghissue:`11607`: AttributeError: 'QEvent' object has no attribute 'pos' * :ghissue:`11486`: Colorbar does not render with PowerNorm and min extend when using imshow * :ghissue:`11582`: wx segfault -* :ghissue:`11515`: using 'sharex' once in 'subplots' function can affect subsequent calles to 'subplots' +* :ghissue:`11515`: using 'sharex' once in 'subplots' function can affect subsequent calls to 'subplots' * :ghissue:`10269`: input() blocks any rendering and event handling * :ghissue:`10345`: Python 3.4 with Matplotlib 1.5 vs Python 3.6 with Matplotlib 2.1 * :ghissue:`10443`: Drop use of pytz dependency in next major release diff --git a/doc/users/prev_whats_new/github_stats_3.0.1.rst b/doc/users/prev_whats_new/github_stats_3.0.1.rst index 95e899d1a9de..8ebc7f5f11c1 100644 --- a/doc/users/prev_whats_new/github_stats_3.0.1.rst +++ b/doc/users/prev_whats_new/github_stats_3.0.1.rst @@ -150,7 +150,7 @@ Pull Requests (127): * :ghpull:`12230`: Backport PR #12213 on branch v3.0.x (Change win32InstalledFonts return value) * :ghpull:`12213`: Change win32InstalledFonts return value * :ghpull:`12223`: Backport PR #11688 on branch v3.0.x (Don't draw axis (spines, ticks, labels) twice when using parasite axes.) -* :ghpull:`12224`: Backport PR #12207 on branch v3.0.x (FIX: dont' check for interactive framework if none required) +* :ghpull:`12224`: Backport PR #12207 on branch v3.0.x (FIX: don't check for interactive framework if none required) * :ghpull:`12207`: FIX: don't check for interactive framework if none required * :ghpull:`11688`: Don't draw axis (spines, ticks, labels) twice when using parasite axes. * :ghpull:`12205`: Backport PR #12186 on branch v3.0.x (DOC: fix API note about get_tightbbox) diff --git a/doc/users/prev_whats_new/github_stats_3.0.2.rst b/doc/users/prev_whats_new/github_stats_3.0.2.rst index c5caed404b62..6b4ef3071f1c 100644 --- a/doc/users/prev_whats_new/github_stats_3.0.2.rst +++ b/doc/users/prev_whats_new/github_stats_3.0.2.rst @@ -83,7 +83,7 @@ Pull Requests (224): * :ghpull:`12670`: FIX: add setter for hold to un-break basemap * :ghpull:`12693`: Workaround Text3D breaking tight_layout() * :ghpull:`12727`: Reorder API docs: separate file per module -* :ghpull:`12738`: Add unobtrusive depreaction note to the first line of the docstring. +* :ghpull:`12738`: Add unobtrusive deprecation note to the first line of the docstring * :ghpull:`12740`: DOC: constrained layout guide (fix: Spacing with colorbars) * :ghpull:`11663`: Refactor color parsing of Axes.scatter * :ghpull:`12736`: Move deprecation note to end of docstring @@ -263,7 +263,7 @@ Pull Requests (224): * :ghpull:`12227`: Use (float, float) as parameter type for 2D positions * :ghpull:`12199`: Allow disabling specific mouse actions in blocking_input * :ghpull:`12213`: Change win32InstalledFonts return value -* :ghpull:`12207`: FIX: dont' check for interactive framework if none required +* :ghpull:`12207`: FIX: don't check for interactive framework if none required * :ghpull:`11688`: Don't draw axis (spines, ticks, labels) twice when using parasite axes. * :ghpull:`12210`: Axes.tick_params() argument checking * :ghpull:`12211`: Fix typo diff --git a/doc/users/prev_whats_new/github_stats_3.1.0.rst b/doc/users/prev_whats_new/github_stats_3.1.0.rst index 97bee1af56b8..a0fb2692fdbb 100644 --- a/doc/users/prev_whats_new/github_stats_3.1.0.rst +++ b/doc/users/prev_whats_new/github_stats_3.1.0.rst @@ -871,7 +871,7 @@ Pull Requests (918): * :ghpull:`12749`: Move toolmanager warning from logging to warning. * :ghpull:`12598`: Support Cn colors with n>=10. * :ghpull:`12727`: Reorder API docs: separate file per module -* :ghpull:`12738`: Add unobtrusive depreaction note to the first line of the docstring. +* :ghpull:`12738`: Add unobtrusive deprecation note to the first line of the docstring * :ghpull:`11663`: Refactor color parsing of Axes.scatter * :ghpull:`12736`: Move deprecation note to end of docstring * :ghpull:`12704`: Rename tkinter import from Tk to tk. diff --git a/doc/users/prev_whats_new/github_stats_3.1.2.rst b/doc/users/prev_whats_new/github_stats_3.1.2.rst index e1ed84e26372..d8476cb5c3a8 100644 --- a/doc/users/prev_whats_new/github_stats_3.1.2.rst +++ b/doc/users/prev_whats_new/github_stats_3.1.2.rst @@ -172,7 +172,7 @@ Issues (28): * :ghissue:`15162`: axes.bar fails when x is int-indexed pandas.Series * :ghissue:`15103`: Colorbar for imshow messes interactive cursor with masked data * :ghissue:`8744`: ConnectionPatch hidden by plots -* :ghissue:`14950`: plt.ioff() not supressing figure generation +* :ghissue:`14950`: plt.ioff() not suppressing figure generation * :ghissue:`14959`: Typo in Docs * :ghissue:`14902`: from matplotlib import animation UnicodeDecodeError * :ghissue:`14897`: New yticks behavior in 3.1.1 vs 3.1.0 diff --git a/doc/users/prev_whats_new/github_stats_3.1.3.rst b/doc/users/prev_whats_new/github_stats_3.1.3.rst index b4706569df02..f8c1afb0e177 100644 --- a/doc/users/prev_whats_new/github_stats_3.1.3.rst +++ b/doc/users/prev_whats_new/github_stats_3.1.3.rst @@ -71,8 +71,8 @@ Pull Requests (45): * :ghpull:`15757`: Backport PR #15751 on branch v3.1.x (Modernize FAQ entry for plt.show().) * :ghpull:`15735`: Cleanup some mplot3d docstrings. * :ghpull:`15753`: Backport PR #15661 on branch v3.1.x (Document scope of 3D scatter depthshading.) -* :ghpull:`15741`: Backport PR #15729 on branch v3.1.x (Catch correct parse errror type for dateutil >= 2.8.1) -* :ghpull:`15729`: Catch correct parse errror type for dateutil >= 2.8.1 +* :ghpull:`15741`: Backport PR #15729 on branch v3.1.x (Catch correct parse error type for dateutil >= 2.8.1) +* :ghpull:`15729`: Catch correct parse error type for dateutil >= 2.8.1 * :ghpull:`15737`: Fix env override in WebAgg backend test. * :ghpull:`15244`: Change documentation format of rcParams defaults diff --git a/doc/users/prev_whats_new/github_stats_3.2.0.rst b/doc/users/prev_whats_new/github_stats_3.2.0.rst index 3cb3fce5de52..4efdb191494d 100644 --- a/doc/users/prev_whats_new/github_stats_3.2.0.rst +++ b/doc/users/prev_whats_new/github_stats_3.2.0.rst @@ -264,12 +264,12 @@ Pull Requests (839): * :ghpull:`16112`: CI: Fail when failed to install dependencies * :ghpull:`16119`: Backport PR #16065 on branch v3.2.x (Nicer formatting of community aspects on front page) * :ghpull:`16074`: Backport PR #16061 on branch v3.2.x (Fix deprecation message for axes_grid1.colorbar.) -* :ghpull:`16093`: Backport PR #16079 on branch v3.2.x (Fix restuctured text formatting) +* :ghpull:`16093`: Backport PR #16079 on branch v3.2.x (Fix restructured text formatting) * :ghpull:`16094`: Backport PR #16080 on branch v3.2.x (Cleanup docstrings in backend_bases.py) * :ghpull:`16086`: FIX: use supported attribute to check pillow version * :ghpull:`16084`: Backport PR #16077 on branch v3.2.x (Fix some typos) * :ghpull:`16077`: Fix some typos -* :ghpull:`16079`: Fix restuctured text formatting +* :ghpull:`16079`: Fix restructured text formatting * :ghpull:`16080`: Cleanup docstrings in backend_bases.py * :ghpull:`16061`: Fix deprecation message for axes_grid1.colorbar. * :ghpull:`16006`: Ignore pos in StrCategoryFormatter.__call__ to display correct label in the preview window @@ -811,7 +811,7 @@ Pull Requests (839): * :ghpull:`14310`: Update to Bounding Box for Qt5 FigureCanvasATAgg.paintEvent() * :ghpull:`14380`: Inline $MPLLOCALFREETYPE/$PYTEST_ADDOPTS/$NPROC in .travis.yml. * :ghpull:`14413`: MAINT: small improvements to the pdf backend -* :ghpull:`14452`: MAINT: Minor cleanup to make functions more self consisntent +* :ghpull:`14452`: MAINT: Minor cleanup to make functions more self consistent * :ghpull:`14441`: Misc. docstring cleanups. * :ghpull:`14440`: Interpolations example * :ghpull:`14402`: Prefer ``mpl.get_data_path()``, and support Paths in FontProperties. @@ -827,7 +827,7 @@ Pull Requests (839): * :ghpull:`14311`: travis: add c code coverage measurements * :ghpull:`14393`: Remove remaining unicode-strings markers. * :ghpull:`14391`: Remove explicit inheritance from object -* :ghpull:`14343`: acquiring and releaseing keypresslock when textbox is being activated +* :ghpull:`14343`: acquiring and releasing keypresslock when textbox is being activated * :ghpull:`14353`: Register flaky pytest marker. * :ghpull:`14373`: Properly hide __has_include to support C++<17 compilers. * :ghpull:`14378`: Remove setup_method diff --git a/doc/users/prev_whats_new/github_stats_3.3.0.rst b/doc/users/prev_whats_new/github_stats_3.3.0.rst index c2e6cd132c2d..45813659b890 100644 --- a/doc/users/prev_whats_new/github_stats_3.3.0.rst +++ b/doc/users/prev_whats_new/github_stats_3.3.0.rst @@ -254,7 +254,7 @@ Pull Requests (1066): * :ghpull:`17617`: Rewrite pdf test to use check_figures_equal. * :ghpull:`17654`: Small fixes to recent What's New * :ghpull:`17649`: MNT: make _setattr_cm more forgiving -* :ghpull:`17644`: Doc 33 whats new consolidation +* :ghpull:`17644`: Doc 33 what's new consolidation * :ghpull:`17647`: Fix example in docstring of cbook._unfold. * :ghpull:`10187`: DOC: add a blitting tutorial * :ghpull:`17471`: Removed idiomatic constructs from interactive figures docs @@ -306,7 +306,7 @@ Pull Requests (1066): * :ghpull:`17540`: Fix help window on GTK. * :ghpull:`17535`: Update docs on subplot2grid / SubplotBase * :ghpull:`17510`: Fix exception handling in FT2Font init. -* :ghpull:`16953`: Changed 'colors' paramater in PyPlot vlines/hlines and Axes vlines/hlines to default to configured rcParams 'lines.color' option +* :ghpull:`16953`: Changed 'colors' parameter in PyPlot vlines/hlines and Axes vlines/hlines to default to configured rcParams 'lines.color' option * :ghpull:`17459`: Use light icons on dark themes for wx and gtk, too. * :ghpull:`17539`: Use symbolic icons for buttons in GTK toolbar. * :ghpull:`15435`: Reuse png metadata handling of imsave() in FigureCanvasAgg.print_png(). @@ -469,7 +469,7 @@ Pull Requests (1066): * :ghpull:`15008`: ENH: add variable epoch * :ghpull:`17260`: Text Rotation Example: Correct roation_mode typo * :ghpull:`17258`: Improve info logged by tex subsystem. -* :ghpull:`17211`: Deprecate support for running svg converter from path contaning newline. +* :ghpull:`17211`: Deprecate support for running svg converter from path containing newline. * :ghpull:`17078`: Improve nbAgg & WebAgg toolbars * :ghpull:`17191`: Inline unsampled-image path; remove renderer kwarg from _check_unsampled_image. * :ghpull:`17213`: Replace use of Bbox.bounds by appropriate properties. @@ -604,7 +604,7 @@ Pull Requests (1066): * :ghpull:`16823`: Dedupe implementation of axes grid switching in toolmanager. * :ghpull:`16951`: Cleanup dates docstrings. * :ghpull:`16769`: Fix some small style issues -* :ghpull:`16936`: FIX: Plot is now rendered with correct inital value +* :ghpull:`16936`: FIX: Plot is now rendered with correct initial value * :ghpull:`16937`: Making sure to keep over/under/bad in cmap resample/reverse. * :ghpull:`16915`: Tighten/cleanup wx backend. * :ghpull:`16923`: Test the macosx backend on Travis. @@ -860,7 +860,7 @@ Pull Requests (1066): * :ghpull:`16439`: Rework pylab docstring. * :ghpull:`16441`: Rework pylab docstring. * :ghpull:`16442`: Expire deprecation of \stackrel. -* :ghpull:`16365`: TST: test_acorr (replaced image comparison with figure comparion) +* :ghpull:`16365`: TST: test_acorr (replaced image comparison with figure comparison) * :ghpull:`16206`: Expire deprecation of \stackrel * :ghpull:`16437`: Rework pylab docstring. * :ghpull:`8896`: Fix mplot3d projection @@ -898,7 +898,7 @@ Pull Requests (1066): * :ghpull:`16304`: Simplify Legend.get_children. * :ghpull:`16309`: Remove duplicated computations in Axes.get_tightbbox. * :ghpull:`16314`: Avoid repeatedly warning about too many figures open. -* :ghpull:`16319`: Put doc for XAxis befor YAxis and likewise for XTick, YTick. +* :ghpull:`16319`: Put doc for XAxis before YAxis and likewise for XTick, YTick. * :ghpull:`16313`: Cleanup constrainedlayout_guide. * :ghpull:`16312`: Remove unnecessary Legend._approx_text_height. * :ghpull:`16307`: Cleanup axes_demo. @@ -991,7 +991,7 @@ Pull Requests (1066): * :ghpull:`16078`: Refactor a bit animation start/save interaction. * :ghpull:`16081`: Delay resolution of animation extra_args. * :ghpull:`16088`: Use C++ true/false in ttconv. -* :ghpull:`16082`: Defaut to writing animation frames to a temporary directory. +* :ghpull:`16082`: Default to writing animation frames to a temporary directory. * :ghpull:`16070`: Make animation blit cache robust against 3d viewpoint changes. * :ghpull:`5056`: MNT: more control of colorbar with CountourSet * :ghpull:`16051`: Deprecate parameters to colorbar which have no effect. @@ -1133,7 +1133,7 @@ Pull Requests (1066): * :ghpull:`15645`: Bump minimal numpy version to 1.12. * :ghpull:`15646`: Hide sphinx-gallery config comments * :ghpull:`15642`: Remove interpolation="nearest" from most examples. -* :ghpull:`15671`: Don't mention tcl in tkagg commments anymore. +* :ghpull:`15671`: Don't mention tcl in tkagg comments anymore. * :ghpull:`15607`: Simplify tk loader. * :ghpull:`15651`: Simplify axes_pad handling in axes_grid. * :ghpull:`15652`: Remove mention of Enthought Canopy from the docs. @@ -1400,7 +1400,7 @@ Issues (198): * :ghissue:`16299`: The interactive polar plot animation's axis label won't scale. * :ghissue:`15182`: More tests ``ConciseDateFormatter`` needed * :ghissue:`16140`: Unclear Documentation for get_xticklabels -* :ghissue:`16147`: pp.hist parmeter 'density' does not scale data appropriately +* :ghissue:`16147`: pp.hist parameter 'density' does not scale data appropriately * :ghissue:`16069`: matplotlib glitch when rotating interactively a 3d animation * :ghissue:`14603`: Scatterplot: should vmin/vmax be ignored when a norm is specified? * :ghissue:`15730`: Setting lines.marker = s in matplotlibrc also sets markers in boxplots @@ -1423,7 +1423,7 @@ Issues (198): * :ghissue:`15089`: Coerce MxNx1 images into MxN images for imshow * :ghissue:`5253`: abline() - for drawing arbitrary lines on a plot, given specifications. * :ghissue:`15165`: Switch to requiring Pillow rather than having our own png wrapper? -* :ghissue:`15280`: Add pull request checklist to Reviewers Guidlines +* :ghissue:`15280`: Add pull request checklist to Reviewers Guidelines * :ghissue:`15289`: cbook.warn_deprecated() should warn with a MatplotlibDeprecationWarning not a UserWarning * :ghissue:`15285`: DOC: make copy right year auto-update * :ghissue:`15059`: fig.add_axes() with no arguments silently does nothing diff --git a/doc/users/prev_whats_new/github_stats_3.3.1.rst b/doc/users/prev_whats_new/github_stats_3.3.1.rst index 3fa2d39a4d90..dc8e9996313f 100644 --- a/doc/users/prev_whats_new/github_stats_3.3.1.rst +++ b/doc/users/prev_whats_new/github_stats_3.3.1.rst @@ -102,16 +102,16 @@ Pull Requests (73): * :ghpull:`17995`: Avoid using Bbox machinery in Path.get_extents; special case polylines. * :ghpull:`17994`: Special case degree-1 Bezier curves. * :ghpull:`17990`: Manual backport of pr 17983 on v3.3.x -* :ghpull:`17984`: Backport PR #17972 on branch v3.3.x (Fix PyPy compatiblity issue) +* :ghpull:`17984`: Backport PR #17972 on branch v3.3.x (Fix PyPy compatibility issue) * :ghpull:`17985`: Backport PR #17976 on branch v3.3.x (Fixed #17970 - Docstrings should not accessed with -OO) * :ghpull:`17983`: FIX: undeprecate and update num2epoch/epoch2num * :ghpull:`17976`: Fixed #17970 - Docstrings should not accessed with -OO -* :ghpull:`17972`: Fix PyPy compatiblity issue +* :ghpull:`17972`: Fix PyPy compatibility issue Issues (25): * :ghissue:`18234`: _reshape_2D function behavior changed, breaks hist for some cases in 3.3.0 -* :ghissue:`18232`: different behaviour between 3.3.0 and 3.2.2 (and earlier) for ploting in a Tk canvas +* :ghissue:`18232`: different behaviour between 3.3.0 and 3.2.2 (and earlier) for plotting in a Tk canvas * :ghissue:`18212`: Updated WxAgg NavigationToolbar2 breaks custom toolbars * :ghissue:`18129`: Error reading png image from URL with imread in matplotlib 3.3 * :ghissue:`18163`: Figure cannot be closed if it has associated Agg canvas diff --git a/doc/users/prev_whats_new/github_stats_3.4.0.rst b/doc/users/prev_whats_new/github_stats_3.4.0.rst index b2568058b455..fb6f0044d139 100644 --- a/doc/users/prev_whats_new/github_stats_3.4.0.rst +++ b/doc/users/prev_whats_new/github_stats_3.4.0.rst @@ -426,9 +426,9 @@ Pull Requests (772): * :ghpull:`19207`: Fix Grouper example code * :ghpull:`19204`: Clarify Date Format Example * :ghpull:`19200`: Fix incorrect statement regarding test images cache size. -* :ghpull:`19198`: Fix link in contrbuting docs +* :ghpull:`19198`: Fix link in contributing docs * :ghpull:`19196`: Fix PR welcome action -* :ghpull:`19188`: Cleanup comparision between X11/CSS4 and xkcd colors +* :ghpull:`19188`: Cleanup comparison between X11/CSS4 and xkcd colors * :ghpull:`19194`: Fix trivial quiver doc typo. * :ghpull:`19180`: Fix Artist.remove_callback() * :ghpull:`19192`: Fixed part of Issue - #19100, changed documentation for axisartist @@ -472,7 +472,7 @@ Pull Requests (772): * :ghpull:`19127`: Cleanups to webagg & friends. * :ghpull:`19122`: FIX/DOC - make Text doscstring interp more easily searchable * :ghpull:`19106`: Support setting rcParams["image.cmap"] to Colormap instances. -* :ghpull:`19085`: FIX: update a transfrom from transFigure to transSubfigure +* :ghpull:`19085`: FIX: update a transform from transFigure to transSubfigure * :ghpull:`19117`: Rename a confusing variable. * :ghpull:`18647`: Axes.axline: implement support transform argument (for points but not slope) * :ghpull:`16220`: Fix interaction with unpickled 3d plots. @@ -701,7 +701,7 @@ Pull Requests (772): * :ghpull:`18564`: Prepare for merging SubplotBase into AxesBase. * :ghpull:`15127`: ENH/API: improvements to register_cmap * :ghpull:`18576`: DOC: prefer colormap over color map -* :ghpull:`18340`: Colorbar grid postion +* :ghpull:`18340`: Colorbar grid position * :ghpull:`18568`: Added Reporting to code_of_conduct.md * :ghpull:`18555`: Convert _math_style_dict into an Enum. * :ghpull:`18567`: Replace subplot(ijk) calls by subplots(i, j) @@ -759,7 +759,7 @@ Pull Requests (772): * :ghpull:`18449`: Remove the private Axes._set_position. * :ghpull:`18460`: DOC: example gray level in 'Specifying Colors' tutorial * :ghpull:`18426`: plot directive: caption-option -* :ghpull:`18444`: Support doubleclick in webagg/nbagg +* :ghpull:`18444`: Support double-click in webagg/nbagg * :ghpull:`12518`: Example showing scale-invariant angle arc * :ghpull:`18446`: Normalize properties passed to ToolHandles. * :ghpull:`18445`: Warn if an animation is gc'd before doing anything. @@ -808,9 +808,9 @@ Pull Requests (772): * :ghpull:`17901`: DOC: Autoreformating of backend/\*.py * :ghpull:`17291`: Normalize gridspec ratios to lists in the setter. * :ghpull:`18226`: Use CallbackRegistry in Widgets and some related cleanup -* :ghpull:`18203`: Force locator and formatter inheritence +* :ghpull:`18203`: Force locator and formatter inheritance * :ghpull:`18279`: boxplot: Add conf_intervals reference to notch docs. -* :ghpull:`18276`: Fix autoscaling to exclude inifinite data limits when possible. +* :ghpull:`18276`: Fix autoscaling to exclude infinite data limits when possible. * :ghpull:`18261`: Migrate tk backend tests into subprocesses * :ghpull:`17961`: DOCS: Remove How-to: Contributing * :ghpull:`18201`: Remove mpl.colors deprecations for 3.4 @@ -964,7 +964,7 @@ Pull Requests (772): * :ghpull:`17697`: Add description examples/pyplots/pyplot simple.py * :ghpull:`17694`: CI: Only skip devdocs deploy if PR is to this repo. * :ghpull:`17691`: ci: Print out reasons for not deploying docs. -* :ghpull:`17099`: Make Spines accessable by the attributes. +* :ghpull:`17099`: Make Spines accessible by the attributes Issues (204): @@ -1044,7 +1044,7 @@ Issues (204): * :ghissue:`19099`: axisartist axis_direction bug * :ghissue:`19171`: 3D surface example bug for non-square grid * :ghissue:`18112`: set_{x,y,z}bound 3d limits are not persistent upon interactive rotation -* :ghissue:`19078`: _update_patch_limits should not use CLOSEPOLY verticies for updating +* :ghissue:`19078`: _update_patch_limits should not use CLOSEPOLY vertices for updating * :ghissue:`16123`: test_dpi_ratio_change fails on Windows/Qt5Agg * :ghissue:`15796`: [DOC] PDF build of matplotlib own documentation crashes with LaTeX error "too deeply nested" * :ghissue:`19091`: 3D Axes don't work in SubFigures @@ -1091,13 +1091,13 @@ Issues (204): * :ghissue:`18641`: Conversion cache cleaning is broken with xdist * :ghissue:`15614`: named color examples need borders * :ghissue:`5519`: The linestyle 'None', ' ' and '' not supported by PathPatch. -* :ghissue:`17487`: Polygon selector with useblit=True - polygon dissapears +* :ghissue:`17487`: Polygon selector with useblit=True - polygon disappears * :ghissue:`17476`: RectangleSelector fails to clear itself after being toggled inactive and then back to active. * :ghissue:`18600`: plt.errorbar raises error when given marker= * :ghissue:`18355`: Optional components required to build docs aren't documented * :ghissue:`18428`: small bug in the mtplotlib gallery * :ghissue:`4438`: inconsistent behaviour of the errorevery option in pyplot.errorbar() to the markevery keyword -* :ghissue:`5823`: pleas dont include the Google Analytics tracking in the off-line doc +* :ghissue:`5823`: please don't include the Google Analytics tracking in the off-line doc * :ghissue:`13035`: Path3DCollection from 3D scatter cannot set_color * :ghissue:`9725`: scatter - set_facecolors is not working on Axes3D * :ghissue:`3370`: Patch3DCollection doesn't update color after calling set_color @@ -1123,12 +1123,12 @@ Issues (204): * :ghissue:`17712`: constrained_layout fails on suptitle+colorbars+some figure sizes * :ghissue:`14638`: colorbar.make_axes doesn't anchor in constrained_layout * :ghissue:`18299`: New configure_subplots behaves badly on TkAgg backend -* :ghissue:`18300`: Remove the examples category "Our Favorite Recipies" +* :ghissue:`18300`: Remove the examples category "Our Favorite Recipes" * :ghissue:`18077`: Imshow breaks if given a unyt_array input * :ghissue:`7074`: Using a linestyle cycler with plt.errorbar results in strange plots * :ghissue:`18236`: FuncAnimation fails to display with interval 0 on Tkagg backend * :ghissue:`8107`: invalid command name "..._on_timer" in FuncAnimation for (too) small interval -* :ghissue:`18272`: Add CI Intervall to boxplot notch documentation +* :ghissue:`18272`: Add CI Interval to boxplot notch documentation * :ghissue:`18137`: axhspan() in empty plots changes the xlimits of plots sharing the X axis * :ghissue:`18246`: test_never_update is flaky * :ghissue:`5856`: Horizontal stem plot @@ -1146,7 +1146,7 @@ Issues (204): * :ghissue:`12198`: axvline incorrectly tries to handle unitized ymin, ymax * :ghissue:`9139`: Python3 matplotlib 2.0.2 with Times New Roman misses unicode minus sign in pdf * :ghissue:`5970`: pyplot.scatter raises obscure error when mistakenly passed a third string param -* :ghissue:`17936`: documenattion and behavior do not match for suppressing (PDF) metadata +* :ghissue:`17936`: documentation and behavior do not match for suppressing (PDF) metadata * :ghissue:`17932`: latex textrm does not work in Cairo backend * :ghissue:`17714`: Universal fullscreen command * :ghissue:`4584`: ColorbarBase draws edges in slightly wrong positions. @@ -1161,7 +1161,7 @@ Issues (204): * :ghissue:`15821`: Should constrained_layout work as plt.figure() argument? * :ghissue:`15616`: Colormaps should have a ``_repr_html_`` that is an image of the colormap * :ghissue:`17579`: ``BoundaryNorm`` yield a ``ZeroDivisionError: division by zero`` -* :ghissue:`17652`: NEP 29 : Stop support fro Python 3.6 soon ? +* :ghissue:`17652`: NEP 29 : Stop support for Python 3.6 soon ? * :ghissue:`11095`: Repeated plot calls with xunits=None throws exception * :ghissue:`17733`: Rename "array" (and perhaps "fields") section of Axes API * :ghissue:`15610`: Link to most recent DevDocs when installing from Master Source diff --git a/doc/users/prev_whats_new/github_stats_3.4.2.rst b/doc/users/prev_whats_new/github_stats_3.4.2.rst index 22b4797c2fc2..d16a69b43151 100644 --- a/doc/users/prev_whats_new/github_stats_3.4.2.rst +++ b/doc/users/prev_whats_new/github_stats_3.4.2.rst @@ -146,7 +146,7 @@ Issues (21): * :ghissue:`19960`: Failed to init RangeSlider with valinit attribute * :ghissue:`19736`: subplot_mosaic axes are not added in consistent order * :ghissue:`19979`: Blank EPS figures if plot contains 'd' -* :ghissue:`19938`: unuseful deprecation warning figbox +* :ghissue:`19938`: useless deprecation warning figbox * :ghissue:`19958`: subfigures missing bbox_inches attribute in inline backend * :ghissue:`19936`: Errorbars elinewidth raise error when numpy array * :ghissue:`19879`: Using "drawstyle" raises AttributeError in errorbar, when yerr is specified. diff --git a/doc/users/prev_whats_new/github_stats_3.4.3.rst b/doc/users/prev_whats_new/github_stats_3.4.3.rst index b248bf69b6ef..ff98041e2d72 100644 --- a/doc/users/prev_whats_new/github_stats_3.4.3.rst +++ b/doc/users/prev_whats_new/github_stats_3.4.3.rst @@ -119,7 +119,7 @@ Issues (22): * :ghissue:`20628`: Out-of-bounds read leads to crash or broken TrueType fonts * :ghissue:`20612`: Broken EPS for Type 42 STIX * :ghissue:`19982`: regression for 3.4.x - ax.figbox replacement incompatible to all version including 3.3.4 -* :ghissue:`19938`: unuseful deprecation warning figbox +* :ghissue:`19938`: useless deprecation warning figbox * :ghissue:`16400`: Inconsistent behavior between Normalizers when input is Dataframe * :ghissue:`20583`: Lost class descriptions since 3.4 docs * :ghissue:`20551`: set_segments(get_segments()) makes lines coarse diff --git a/doc/users/prev_whats_new/github_stats_3.5.0.rst b/doc/users/prev_whats_new/github_stats_3.5.0.rst index bde4d917b38b..c39b614e7bad 100644 --- a/doc/users/prev_whats_new/github_stats_3.5.0.rst +++ b/doc/users/prev_whats_new/github_stats_3.5.0.rst @@ -1121,7 +1121,7 @@ Issues (187): * :ghissue:`20847`: [Bug]: Contourf not filling contours. * :ghissue:`21300`: [Bug]: zooming in on contour plot gives false extra contour lines * :ghissue:`21466`: [Bug]: EPS export shows hidden tick labels when using tex for text rendering -* :ghissue:`21463`: [Bug]: Plotting lables with Greek latters in math mode produces Parsing error when plt.show() runs +* :ghissue:`21463`: [Bug]: Plotting labels with Greek latters in math mode produces Parsing error when plt.show() runs * :ghissue:`20534`: Document formatting for sections * :ghissue:`21246`: [Doc]: Install info takes up too much room on new front page * :ghissue:`21432`: [Doc]: Double clicking parameter name also highlights next item of text @@ -1157,7 +1157,7 @@ Issues (187): * :ghissue:`16251`: API changes are too hard to find in the rendered docs * :ghissue:`20770`: [Doc]: How to replicate behaviour of ``plt.gca(projection=...)``? * :ghissue:`17052`: Colorbar update error with clim change in multi_image.py example -* :ghissue:`4387`: make ``Normalize`` objects notifiy scalar-mappables on changes +* :ghissue:`4387`: make ``Normalize`` objects notify scalar-mappables on changes * :ghissue:`20001`: rename fig.draw_no_output * :ghissue:`20936`: [Bug]: edgecolor 'auto' doesn't work properly * :ghissue:`20909`: [Bug]: Animation error message @@ -1241,7 +1241,7 @@ Issues (187): * :ghissue:`17508`: Quadmesh.set_array should validate dimensions * :ghissue:`20372`: Incorrect axes positioning in axes_grid.Grid with direction='column' * :ghissue:`19419`: Dev version hard to check -* :ghissue:`17310`: Matplotlib git master version fails to pass serveral pytest's tests. +* :ghissue:`17310`: Matplotlib git master version fails to pass several pytest's tests * :ghissue:`7742`: plot_date() after axhline() doesn't rescale axes * :ghissue:`20322`: QuadMesh default for shading inadvertently changed. * :ghissue:`9653`: SVG savefig + LaTeX extremely slow on macOS diff --git a/doc/users/prev_whats_new/github_stats_3.5.1.rst b/doc/users/prev_whats_new/github_stats_3.5.1.rst index 7eb37b769d6c..626cf319c23c 100644 --- a/doc/users/prev_whats_new/github_stats_3.5.1.rst +++ b/doc/users/prev_whats_new/github_stats_3.5.1.rst @@ -131,7 +131,7 @@ Issues (29): * :ghissue:`21803`: [Bug]: using ``set_offsets`` on scatter object raises TypeError * :ghissue:`21839`: [Bug]: Top of plot clipped when using Subfigures without suptitle * :ghissue:`21841`: [Bug]: Wrong tick labels and colorbar of discrete normalizer -* :ghissue:`21783`: [MNT]: wheel of 3.5.0 apears to depend on setuptools-scm which apears to be unintentional +* :ghissue:`21783`: [MNT]: wheel of 3.5.0 appears to depend on setuptools-scm which appears to be unintentional * :ghissue:`21733`: [Bug]: Possible bug on arrows in annotation * :ghissue:`21749`: [Bug]: Regression on ``tight_layout`` when manually adding axes for colorbars * :ghissue:`19197`: Unexpected error after using Figure.canvas.draw on macosx backend diff --git a/doc/users/prev_whats_new/whats_new_3.10.0.rst b/doc/users/prev_whats_new/whats_new_3.10.0.rst index 06282cedad9a..f1231be53cc4 100644 --- a/doc/users/prev_whats_new/whats_new_3.10.0.rst +++ b/doc/users/prev_whats_new/whats_new_3.10.0.rst @@ -329,7 +329,9 @@ In the following example the norm and cmap are changed on multiple plots simulta colorizer.vmax = 2 colorizer.cmap = 'RdBu' -All plotting methods that use a data-to-color pipeline now create a colorizer object if one is not provided. This can be re-used by subsequent artists such that they will share a single data-to-color pipeline: +All plotting methods that use a data-to-color pipeline now create a colorizer object if +one is not provided. This can be reused by subsequent artists such that they will share +a single data-to-color pipeline: .. plot:: :include-source: true diff --git a/doc/users/prev_whats_new/whats_new_3.8.0.rst b/doc/users/prev_whats_new/whats_new_3.8.0.rst index 88f987172adb..fe1d5f7a7952 100644 --- a/doc/users/prev_whats_new/whats_new_3.8.0.rst +++ b/doc/users/prev_whats_new/whats_new_3.8.0.rst @@ -359,7 +359,7 @@ The following delimiter names have been supported earlier, but can now be sized * ``\leftparen`` and ``\rightparen`` There are really no obvious advantages in using these. -Instead, they are are added for completeness. +Instead, they are added for completeness. ``mathtext`` documentation improvements --------------------------------------- @@ -513,7 +513,7 @@ Plot Directive now can make responsive images with "srcset" The plot sphinx directive (``matplotlib.sphinxext.plot_directive``, invoked in rst as ``.. plot::``) can be configured to automatically make higher res -figures and add these to the the built html docs. In ``conf.py``:: +figures and add these to the built html docs. In ``conf.py``:: extensions = [ ... diff --git a/galleries/examples/README.txt b/galleries/examples/README.txt index 31d4beae578d..363494ac7e6b 100644 --- a/galleries/examples/README.txt +++ b/galleries/examples/README.txt @@ -13,7 +13,7 @@ and source code. For longer tutorials, see our :ref:`tutorials page `. You can also find :ref:`external resources ` and -a :ref:`FAQ ` in our :ref:`user guide `. +a :ref:`FAQ ` in our :ref:`user guide `. .. admonition:: Tagging! diff --git a/galleries/examples/scales/custom_scale.py b/galleries/examples/scales/custom_scale.py index 0eedb16ec5cf..1b6bdd6f3e09 100644 --- a/galleries/examples/scales/custom_scale.py +++ b/galleries/examples/scales/custom_scale.py @@ -22,7 +22,7 @@ * You want to override the default locators and formatters for the axis (``set_default_locators_and_formatters`` below). - * You want to limit the range of the the axis (``limit_range_for_scale`` below). + * You want to limit the range of the axis (``limit_range_for_scale`` below). """ diff --git a/galleries/examples/ticks/date_formatters_locators.py b/galleries/examples/ticks/date_formatters_locators.py index 39492168242f..8c3b24bb4c26 100644 --- a/galleries/examples/ticks/date_formatters_locators.py +++ b/galleries/examples/ticks/date_formatters_locators.py @@ -12,6 +12,7 @@ import matplotlib.pyplot as plt import numpy as np +# While these appear unused directly, they are used from eval'd strings. from matplotlib.dates import (FR, MO, MONTHLY, SA, SU, TH, TU, WE, AutoDateFormatter, AutoDateLocator, ConciseDateFormatter, DateFormatter, DayLocator, diff --git a/galleries/tutorials/images.py b/galleries/tutorials/images.py index 0867f7b6d672..a7c474dab40b 100644 --- a/galleries/tutorials/images.py +++ b/galleries/tutorials/images.py @@ -33,8 +33,8 @@ In [1]: %matplotlib inline -This turns on inline plotting, where plot graphics will appear in your -notebook. This has important implications for interactivity. For inline plotting, commands in +This turns on inline plotting, where plot graphics will appear in your notebook. This +has important implications for interactivity. For inline plotting, commands in cells below the cell that outputs a plot will not affect the plot. For example, changing the colormap is not possible from cells below the cell that creates a plot. However, for other backends, such as Qt, that open a separate window, diff --git a/galleries/tutorials/index.rst b/galleries/tutorials/index.rst index ace37dcb6f57..48187a862a2e 100644 --- a/galleries/tutorials/index.rst +++ b/galleries/tutorials/index.rst @@ -7,7 +7,7 @@ This page contains a few tutorials for using Matplotlib. For the old tutorials, For shorter examples, see our :ref:`examples page `. You can also find :ref:`external resources ` and -a :ref:`FAQ ` in our :ref:`user guide `. +a :ref:`FAQ ` in our :ref:`user guide `. .. raw:: html diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 8ffd002d43bc..e98e8ea07502 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -400,12 +400,15 @@ def impl(args, regex, min_ver=None, ignore_exit_code=False): try: output = subprocess.check_output( args, stderr=subprocess.STDOUT, - text=True, errors="replace") + text=True, errors="replace", timeout=30) except subprocess.CalledProcessError as _cpe: if ignore_exit_code: output = _cpe.output else: raise ExecutableNotFoundError(str(_cpe)) from _cpe + except subprocess.TimeoutExpired as _te: + msg = f"Timed out running {cbook._pformat_subprocess(args)}" + raise ExecutableNotFoundError(msg) from _te except OSError as _ose: raise ExecutableNotFoundError(str(_ose)) from _ose match = re.search(regex, output) @@ -799,13 +802,13 @@ def find_all(self, pattern): """ pattern_re = re.compile(pattern) - return RcParams((key, value) - for key, value in self.items() - if pattern_re.search(key)) + return self.__class__( + (key, value) for key, value in self.items() if pattern_re.search(key) + ) def copy(self): """Copy this RcParams instance.""" - rccopy = RcParams() + rccopy = self.__class__() for k in self: # Skip deprecations and revalidation. rccopy._set(k, self._get(k)) return rccopy diff --git a/lib/matplotlib/_api/__init__.pyi b/lib/matplotlib/_api/__init__.pyi index 9bf67110bb54..c8ea814fc13d 100644 --- a/lib/matplotlib/_api/__init__.pyi +++ b/lib/matplotlib/_api/__init__.pyi @@ -1,6 +1,6 @@ from collections.abc import Callable, Generator, Iterable, Mapping, Sequence from typing import Any, TypeVar, overload -from typing_extensions import Self # < Py 3.11 +from typing import Self from numpy.typing import NDArray diff --git a/lib/matplotlib/artist.py b/lib/matplotlib/artist.py index fd35b312835a..eaaae43e283a 100644 --- a/lib/matplotlib/artist.py +++ b/lib/matplotlib/artist.py @@ -321,13 +321,12 @@ def stale(self, val): def get_window_extent(self, renderer=None): """ - Get the artist's bounding box in display space. + Get the artist's bounding box in display space, ignoring clipping. The bounding box's width and height are non-negative. - Subclasses should override for inclusion in the bounding box - "tight" calculation. Default is to return an empty bounding - box at 0, 0. + Subclasses should override for inclusion in the bounding box "tight" + calculation. Default is to return an empty bounding box at 0, 0. .. warning:: @@ -341,28 +340,40 @@ def get_window_extent(self, renderer=None): screen render incorrectly when saved to file. To get accurate results you may need to manually call - `matplotlib.figure.Figure.savefig` or - `matplotlib.figure.Figure.draw_without_rendering` to have Matplotlib - compute the rendered size. + `~.Figure.savefig` or `~.Figure.draw_without_rendering` to have + Matplotlib compute the rendered size. + Parameters + ---------- + renderer : `~matplotlib.backend_bases.RendererBase`, optional + Renderer used to draw the figure (i.e. ``fig.canvas.get_renderer()``). + + See Also + -------- + .Artist.get_tightbbox : + Get the artist bounding box, taking clipping into account. """ return Bbox([[0, 0], [0, 0]]) def get_tightbbox(self, renderer=None): """ - Like `.Artist.get_window_extent`, but includes any clipping. + Get the artist's bounding box in display space, taking clipping into account. Parameters ---------- - renderer : `~matplotlib.backend_bases.RendererBase` subclass, optional - renderer that will be used to draw the figures (i.e. - ``fig.canvas.get_renderer()``) + renderer : `~matplotlib.backend_bases.RendererBase`, optional + Renderer used to draw the figure (i.e. ``fig.canvas.get_renderer()``). Returns ------- `.Bbox` or None - The enclosing bounding box (in figure pixel coordinates). - Returns None if clipping results in no intersection. + The enclosing bounding box (in figure pixel coordinates), or None + if clipping results in no intersection. + + See Also + -------- + .Artist.get_window_extent : + Get the artist bounding box, ignoring clipping. """ bbox = self.get_window_extent(renderer) if self.get_clip_on(): diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index b4ed7ae22d35..3e39bbd4acdc 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -2296,8 +2296,8 @@ def _parse_bar_color_args(self, kwargs): facecolor = mcolors.to_rgba_array(facecolor) except ValueError as err: raise ValueError( - "'facecolor' or 'color' argument must be a valid color or" - "sequence of colors." + "'facecolor' or 'color' argument must be a valid color or " + "sequence of colors." ) from err return facecolor, edgecolor @@ -3436,6 +3436,9 @@ def stem(self, *args, linefmt=None, markerfmt=None, basefmt=None, bottom=0, else: # horizontal heads, locs = self._process_unit_info([("x", heads), ("y", locs)]) + heads = cbook._check_1d(heads) + locs = cbook._check_1d(locs) + # resolve line format if linefmt is None: linefmt = args[0] if len(args) > 0 else "C0-" diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 1ce205ede613..e5175ea8761c 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -897,7 +897,7 @@ def _request_autoscale_view(self, axis="all", tight=None): Mark a single axis, or all of them, as stale wrt. autoscaling. No computation is performed until the next autoscaling; thus, separate - calls to control individual axises incur negligible performance cost. + calls to control individual `Axis`s incur negligible performance cost. Parameters ---------- @@ -4749,14 +4749,25 @@ def label_outer(self, remove_inner_ticks=False): self._label_outer_yaxis(skip_non_rectangular_axes=False, remove_inner_ticks=remove_inner_ticks) + def _get_subplotspec_with_optional_colorbar(self): + """ + Return the subplotspec for this Axes, except that if this Axes has been + moved to a subgridspec to make room for a colorbar, then return the + subplotspec that encloses both this Axes and the colorbar Axes. + """ + ss = self.get_subplotspec() + if any(cax.get_subplotspec() for cax in self._colorbars): + ss = ss.get_gridspec()._subplot_spec + return ss + def _label_outer_xaxis(self, *, skip_non_rectangular_axes, remove_inner_ticks=False): # see documentation in label_outer. if skip_non_rectangular_axes and not isinstance(self.patch, mpl.patches.Rectangle): return - ss = self.get_subplotspec() - if not ss: + ss = self._get_subplotspec_with_optional_colorbar() + if ss is None: return label_position = self.xaxis.get_label_position() if not ss.is_first_row(): # Remove top label/ticklabels/offsettext. @@ -4782,8 +4793,8 @@ def _label_outer_yaxis(self, *, skip_non_rectangular_axes, if skip_non_rectangular_axes and not isinstance(self.patch, mpl.patches.Rectangle): return - ss = self.get_subplotspec() - if not ss: + ss = self._get_subplotspec_with_optional_colorbar() + if ss is None: return label_position = self.yaxis.get_label_position() if not ss.is_first_col(): # Remove left label/ticklabels/offsettext. diff --git a/lib/matplotlib/axes/_base.pyi b/lib/matplotlib/axes/_base.pyi index 4933d0d1e236..cb538a49172a 100644 --- a/lib/matplotlib/axes/_base.pyi +++ b/lib/matplotlib/axes/_base.pyi @@ -225,7 +225,7 @@ class _AxesBase(martist.Artist): ymin: float | None = ..., ymax: float | None = ... ) -> tuple[float, float, float, float]: ... - def get_legend(self) -> Legend: ... + def get_legend(self) -> Legend | None: ... def get_images(self) -> list[AxesImage]: ... def get_lines(self) -> list[Line2D]: ... def get_xaxis(self) -> XAxis: ... diff --git a/lib/matplotlib/axis.pyi b/lib/matplotlib/axis.pyi index 6119b946fd7b..4bcfb1e1cfb7 100644 --- a/lib/matplotlib/axis.pyi +++ b/lib/matplotlib/axis.pyi @@ -1,7 +1,7 @@ from collections.abc import Callable, Iterable, Sequence import datetime from typing import Any, Literal, overload -from typing_extensions import Self # < Py 3.11 +from typing import Self import numpy as np from numpy.typing import ArrayLike diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 527d8c010710..626852f2aa34 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -1619,7 +1619,8 @@ def _allow_interrupt(prepare_notifier, handle_sigint): If SIGINT was indeed caught, after exiting the on_signal() function the interpreter reacts to the signal according to the handler function which had been set up by a signal.signal() call; here, we arrange to call the - backend-specific *handle_sigint* function. Finally, we call the old SIGINT + backend-specific *handle_sigint* function, passing the notifier object + as returned by prepare_notifier(). Finally, we call the old SIGINT handler with the same arguments that were given to our custom handler. We do this only if the old handler for SIGINT was not None, which means @@ -1629,7 +1630,7 @@ def _allow_interrupt(prepare_notifier, handle_sigint): Parameters ---------- prepare_notifier : Callable[[socket.socket], object] - handle_sigint : Callable[[], object] + handle_sigint : Callable[[object], object] """ old_sigint_handler = signal.getsignal(signal.SIGINT) @@ -1645,9 +1646,10 @@ def _allow_interrupt(prepare_notifier, handle_sigint): notifier = prepare_notifier(rsock) def save_args_and_handle_sigint(*args): - nonlocal handler_args + nonlocal handler_args, notifier handler_args = args - handle_sigint() + handle_sigint(notifier) + notifier = None signal.signal(signal.SIGINT, save_args_and_handle_sigint) try: diff --git a/lib/matplotlib/backend_bases.pyi b/lib/matplotlib/backend_bases.pyi index 24669bfb3aeb..c65d39415472 100644 --- a/lib/matplotlib/backend_bases.pyi +++ b/lib/matplotlib/backend_bases.pyi @@ -21,7 +21,18 @@ from matplotlib.transforms import Bbox, BboxBase, Transform, TransformedPath from collections.abc import Callable, Iterable, Sequence from typing import Any, IO, Literal, NamedTuple, TypeVar, overload from numpy.typing import ArrayLike -from .typing import ColorType, LineStyleType, CapStyleType, JoinStyleType +from .typing import ( + CapStyleType, + CloseEventType, + ColorType, + DrawEventType, + JoinStyleType, + KeyEventType, + LineStyleType, + MouseEventType, + PickEventType, + ResizeEventType, +) def register_backend( format: str, backend: str | type[FigureCanvasBase], description: str | None = ... @@ -354,37 +365,28 @@ class FigureCanvasBase: @overload def mpl_connect( self, - s: Literal[ - "button_press_event", - "motion_notify_event", - "scroll_event", - "figure_enter_event", - "figure_leave_event", - "axes_enter_event", - "axes_leave_event", - "button_release_event", - ], + s: MouseEventType, func: Callable[[MouseEvent], Any], ) -> int: ... @overload def mpl_connect( self, - s: Literal["key_press_event", "key_release_event"], + s: KeyEventType, func: Callable[[KeyEvent], Any], ) -> int: ... @overload - def mpl_connect(self, s: Literal["pick_event"], func: Callable[[PickEvent], Any]) -> int: ... + def mpl_connect(self, s: PickEventType, func: Callable[[PickEvent], Any]) -> int: ... @overload - def mpl_connect(self, s: Literal["resize_event"], func: Callable[[ResizeEvent], Any]) -> int: ... + def mpl_connect(self, s: ResizeEventType, func: Callable[[ResizeEvent], Any]) -> int: ... @overload - def mpl_connect(self, s: Literal["close_event"], func: Callable[[CloseEvent], Any]) -> int: ... + def mpl_connect(self, s: CloseEventType, func: Callable[[CloseEvent], Any]) -> int: ... @overload - def mpl_connect(self, s: str, func: Callable[[Event], Any]) -> int: ... + def mpl_connect(self, s: DrawEventType, func: Callable[[DrawEvent], Any]) -> int: ... def mpl_disconnect(self, cid: int) -> None: ... def new_timer( self, diff --git a/lib/matplotlib/backends/_backend_tk.py b/lib/matplotlib/backends/_backend_tk.py index 0bbff1379ffa..eaf868fd8bec 100644 --- a/lib/matplotlib/backends/_backend_tk.py +++ b/lib/matplotlib/backends/_backend_tk.py @@ -775,7 +775,7 @@ def _recolor_icon(image, color): image_data = np.asarray(image).copy() black_mask = (image_data[..., :3] == 0).all(axis=-1) image_data[black_mask, :3] = color - return Image.fromarray(image_data, mode="RGBA") + return Image.fromarray(image_data) # Use the high-resolution (48x48 px) icon if it exists and is needed with Image.open(path_large if (size > 24 and path_large.exists()) diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index f20bdffd4a3a..a75a8a86eb92 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -1784,7 +1784,7 @@ def _writeImg(self, data, id, smask=None): data[:, :, 2]) indices = np.argsort(palette24).astype(np.uint8) rgb8 = indices[np.searchsorted(palette24, rgb24, sorter=indices)] - img = Image.fromarray(rgb8, mode='P') + img = Image.fromarray(rgb8).convert("P") img.putpalette(palette) png_data, bit_depth, palette = self._writePng(img) if bit_depth is None or palette is None: diff --git a/lib/matplotlib/backends/backend_qt.py b/lib/matplotlib/backends/backend_qt.py index 5cde4866cad7..9089e982cea6 100644 --- a/lib/matplotlib/backends/backend_qt.py +++ b/lib/matplotlib/backends/backend_qt.py @@ -169,9 +169,14 @@ def _may_clear_sock(): # be forgiving about reading an empty socket. pass - return sn # Actually keep the notifier alive. - - def handle_sigint(): + # We return the QSocketNotifier so that the caller holds a reference, and we + # also explicitly clean it up in handle_sigint(). Without doing both, deletion + # of the socket notifier can happen prematurely or not at all. + return sn + + def handle_sigint(sn): + sn.deleteLater() + QtCore.QCoreApplication.sendPostedEvents(sn, QtCore.QEvent.Type.DeferredDelete) if hasattr(qapp_or_eventloop, 'closeAllWindows'): qapp_or_eventloop.closeAllWindows() qapp_or_eventloop.quit() diff --git a/lib/matplotlib/backends/web_backend/nbagg_uat.ipynb b/lib/matplotlib/backends/web_backend/nbagg_uat.ipynb index e9fc62bc2883..0513fee2b54c 100644 --- a/lib/matplotlib/backends/web_backend/nbagg_uat.ipynb +++ b/lib/matplotlib/backends/web_backend/nbagg_uat.ipynb @@ -309,7 +309,7 @@ "metadata": {}, "outputs": [], "source": [ - "from matplotlib.backends.backend_nbagg import new_figure_manager,show\n", + "from matplotlib.backends.backend_nbagg import new_figure_manager\n", "\n", "manager = new_figure_manager(1000)\n", "fig = manager.canvas.figure\n", @@ -341,15 +341,18 @@ "x = np.arange(0, 2*np.pi, 0.01) # x-array\n", "line, = ax.plot(x, np.sin(x))\n", "\n", + "\n", "def animate(i):\n", " line.set_ydata(np.sin(x+i/10.0)) # update the data\n", " return line,\n", "\n", - "#Init only required for blitting to give a clean slate.\n", + "\n", + "# Init only required for blitting to give a clean slate.\n", "def init():\n", " line.set_ydata(np.ma.array(x, mask=True))\n", " return line,\n", "\n", + "\n", "ani = animation.FuncAnimation(fig, animate, np.arange(1, 200), init_func=init,\n", " interval=100., blit=True)\n", "plt.show()" @@ -405,6 +408,8 @@ "ln, = ax.plot(x,y)\n", "evt = []\n", "colors = iter(itertools.cycle(['r', 'g', 'b', 'k', 'c']))\n", + "\n", + "\n", "def on_event(event):\n", " if event.name.startswith('key'):\n", " fig.suptitle('%s: %s' % (event.name, event.key))\n", @@ -417,6 +422,7 @@ " fig.canvas.draw()\n", " fig.canvas.draw_idle()\n", "\n", + "\n", "fig.canvas.mpl_connect('button_press_event', on_event)\n", "fig.canvas.mpl_connect('button_release_event', on_event)\n", "fig.canvas.mpl_connect('scroll_event', on_event)\n", @@ -448,10 +454,12 @@ "fig, ax = plt.subplots()\n", "text = ax.text(0.5, 0.5, '', ha='center')\n", "\n", + "\n", "def update(text):\n", " text.set(text=time.ctime())\n", " text.axes.figure.canvas.draw()\n", - " \n", + "\n", + "\n", "timer = fig.canvas.new_timer(500, [(update, [text], {})])\n", "timer.start()\n", "plt.show()" @@ -471,7 +479,7 @@ "outputs": [], "source": [ "fig, ax = plt.subplots()\n", - "text = ax.text(0.5, 0.5, '', ha='center') \n", + "text = ax.text(0.5, 0.5, '', ha='center')\n", "timer = fig.canvas.new_timer(500, [(update, [text], {})])\n", "\n", "timer.single_shot = True\n", @@ -578,11 +586,12 @@ "cnt = itertools.count()\n", "bg = None\n", "\n", + "\n", "def onclick_handle(event):\n", " \"\"\"Should draw elevating green line on each mouse click\"\"\"\n", " global bg\n", " if bg is None:\n", - " bg = ax.figure.canvas.copy_from_bbox(ax.bbox) \n", + " bg = ax.figure.canvas.copy_from_bbox(ax.bbox)\n", " ax.figure.canvas.restore_region(bg)\n", "\n", " cur_y = (next(cnt) % 10) * 0.1\n", @@ -590,6 +599,7 @@ " ax.draw_artist(ln)\n", " ax.figure.canvas.blit(ax.bbox)\n", "\n", + "\n", "fig, ax = plt.subplots()\n", "ax.plot([0, 1], [0, 1], 'r')\n", "ln, = ax.plot([0, 1], [0, 0], 'g', animated=True)\n", @@ -598,13 +608,6 @@ "\n", "ax.figure.canvas.mpl_connect('button_press_event', onclick_handle)" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/lib/matplotlib/cbook.pyi b/lib/matplotlib/cbook.pyi index 6c2d9c303eb2..ad14841463e8 100644 --- a/lib/matplotlib/cbook.pyi +++ b/lib/matplotlib/cbook.pyi @@ -14,10 +14,10 @@ from typing import ( Generic, IO, Literal, - Sequence, TypeVar, overload, ) +from collections.abc import Sequence _T = TypeVar("_T") diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index 19bdbe605d88..4348f02cfc34 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -1455,8 +1455,7 @@ def make_axes(parents, location=None, orientation=None, fraction=0.15, cax = fig.add_axes(pbcb, label="") for a in parents: - # tell the parent it has a colorbar - a._colorbars += [cax] + a._colorbars.append(cax) # tell the parent it has a colorbar cax._colorbar_info = dict( parents=parents, location=location, @@ -1549,6 +1548,7 @@ def make_axes_gridspec(parent, *, location=None, orientation=None, fig = parent.get_figure() cax = fig.add_subplot(ss_cb, label="") + parent._colorbars.append(cax) # tell the parent it has a colorbar cax.set_anchor(anchor) cax.set_box_aspect(aspect) cax.set_aspect('auto') diff --git a/lib/matplotlib/colorizer.py b/lib/matplotlib/colorizer.py index b4223f389804..92a6e4ea4c4f 100644 --- a/lib/matplotlib/colorizer.py +++ b/lib/matplotlib/colorizer.py @@ -90,7 +90,7 @@ def norm(self): @norm.setter def norm(self, norm): - _api.check_isinstance((colors.Normalize, str, None), norm=norm) + _api.check_isinstance((colors.Norm, str, None), norm=norm) if norm is None: norm = colors.Normalize() elif isinstance(norm, str): diff --git a/lib/matplotlib/colorizer.pyi b/lib/matplotlib/colorizer.pyi index f35ebe5295e4..9a5a73415d83 100644 --- a/lib/matplotlib/colorizer.pyi +++ b/lib/matplotlib/colorizer.pyi @@ -10,12 +10,12 @@ class Colorizer: def __init__( self, cmap: str | colors.Colormap | None = ..., - norm: str | colors.Normalize | None = ..., + norm: str | colors.Norm | None = ..., ) -> None: ... @property - def norm(self) -> colors.Normalize: ... + def norm(self) -> colors.Norm: ... @norm.setter - def norm(self, norm: colors.Normalize | str | None) -> None: ... + def norm(self, norm: colors.Norm | str | None) -> None: ... def to_rgba( self, x: np.ndarray, @@ -63,10 +63,10 @@ class _ColorizerInterface: def get_cmap(self) -> colors.Colormap: ... def set_cmap(self, cmap: str | colors.Colormap) -> None: ... @property - def norm(self) -> colors.Normalize: ... + def norm(self) -> colors.Norm: ... @norm.setter - def norm(self, norm: colors.Normalize | str | None) -> None: ... - def set_norm(self, norm: colors.Normalize | str | None) -> None: ... + def norm(self, norm: colors.Norm | str | None) -> None: ... + def set_norm(self, norm: colors.Norm | str | None) -> None: ... def autoscale(self) -> None: ... def autoscale_None(self) -> None: ... @@ -74,7 +74,7 @@ class _ColorizerInterface: class _ScalarMappable(_ColorizerInterface): def __init__( self, - norm: colors.Normalize | None = ..., + norm: colors.Norm | None = ..., cmap: str | colors.Colormap | None = ..., *, colorizer: Colorizer | None = ..., diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index 254e2c1a203b..a09b4f3d4f5c 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -41,6 +41,7 @@ import base64 from collections.abc import Sequence, Mapping +from abc import ABC, abstractmethod import functools import importlib import inspect @@ -2257,7 +2258,87 @@ def _init(self): self._isinit = True -class Normalize: +class Norm(ABC): + """ + Abstract base class for normalizations. + + Subclasses include `Normalize` which maps from a scalar to + a scalar. However, this class makes no such requirement, and subclasses may + support the normalization of multiple variates simultaneously, with + separate normalization for each variate. + """ + + def __init__(self): + self.callbacks = cbook.CallbackRegistry(signals=["changed"]) + + @property + @abstractmethod + def vmin(self): + """Lower limit of the input data interval; maps to 0.""" + pass + + @property + @abstractmethod + def vmax(self): + """Upper limit of the input data interval; maps to 1.""" + pass + + @property + @abstractmethod + def clip(self): + """ + Determines the behavior for mapping values outside the range ``[vmin, vmax]``. + + See the *clip* parameter in `.Normalize`. + """ + pass + + @abstractmethod + def __call__(self, value, clip=None): + """ + Normalize the data and return the normalized data. + + Parameters + ---------- + value + Data to normalize. + clip : bool, optional + See the description of the parameter *clip* in `.Normalize`. + + If ``None``, defaults to ``self.clip`` (which defaults to + ``False``). + + Notes + ----- + If not already initialized, ``self.vmin`` and ``self.vmax`` are + initialized using ``self.autoscale_None(value)``. + """ + pass + + @abstractmethod + def autoscale(self, A): + """Set *vmin*, *vmax* to min, max of *A*.""" + pass + + @abstractmethod + def autoscale_None(self, A): + """If *vmin* or *vmax* are not set, use the min/max of *A* to set them.""" + pass + + @abstractmethod + def scaled(self): + """Return whether *vmin* and *vmax* are both set.""" + pass + + def _changed(self): + """ + Call this whenever the norm is changed to notify all the + callback listeners to the 'changed' signal. + """ + self.callbacks.process('changed') + + +class Normalize(Norm): """ A class which, when called, maps values within the interval ``[vmin, vmax]`` linearly to the interval ``[0.0, 1.0]``. The mapping of @@ -2307,15 +2388,15 @@ def __init__(self, vmin=None, vmax=None, clip=False): ----- If ``vmin == vmax``, input data will be mapped to 0. """ + super().__init__() self._vmin = _sanitize_extrema(vmin) self._vmax = _sanitize_extrema(vmax) self._clip = clip self._scale = None - self.callbacks = cbook.CallbackRegistry(signals=["changed"]) @property def vmin(self): - """Lower limit of the input data interval; maps to 0.""" + # docstring inherited return self._vmin @vmin.setter @@ -2327,7 +2408,7 @@ def vmin(self, value): @property def vmax(self): - """Upper limit of the input data interval; maps to 1.""" + # docstring inherited return self._vmax @vmax.setter @@ -2339,11 +2420,7 @@ def vmax(self, value): @property def clip(self): - """ - Determines the behavior for mapping values outside the range ``[vmin, vmax]``. - - See the *clip* parameter in `.Normalize`. - """ + # docstring inherited return self._clip @clip.setter @@ -2352,13 +2429,6 @@ def clip(self, value): self._clip = value self._changed() - def _changed(self): - """ - Call this whenever the norm is changed to notify all the - callback listeners to the 'changed' signal. - """ - self.callbacks.process('changed') - @staticmethod def process_value(value): """ @@ -2400,24 +2470,7 @@ def process_value(value): return result, is_scalar def __call__(self, value, clip=None): - """ - Normalize the data and return the normalized data. - - Parameters - ---------- - value - Data to normalize. - clip : bool, optional - See the description of the parameter *clip* in `.Normalize`. - - If ``None``, defaults to ``self.clip`` (which defaults to - ``False``). - - Notes - ----- - If not already initialized, ``self.vmin`` and ``self.vmax`` are - initialized using ``self.autoscale_None(value)``. - """ + # docstring inherited if clip is None: clip = self.clip @@ -2468,7 +2521,7 @@ def inverse(self, value): return vmin + value * (vmax - vmin) def autoscale(self, A): - """Set *vmin*, *vmax* to min, max of *A*.""" + # docstring inherited with self.callbacks.blocked(): # Pause callbacks while we are updating so we only get # a single update signal at the end @@ -2477,7 +2530,7 @@ def autoscale(self, A): self._changed() def autoscale_None(self, A): - """If *vmin* or *vmax* are not set, use the min/max of *A* to set them.""" + # docstring inherited A = np.asanyarray(A) if isinstance(A, np.ma.MaskedArray): @@ -2491,7 +2544,7 @@ def autoscale_None(self, A): self.vmax = A.max() def scaled(self): - """Return whether *vmin* and *vmax* are both set.""" + # docstring inherited return self.vmin is not None and self.vmax is not None @@ -2775,7 +2828,7 @@ def _make_norm_from_scale( unlike to arbitrary lambdas. """ - class Norm(base_norm_cls): + class ScaleNorm(base_norm_cls): def __reduce__(self): cls = type(self) # If the class is toplevel-accessible, it is possible to directly @@ -2855,15 +2908,15 @@ def autoscale_None(self, A): return super().autoscale_None(in_trf_domain) if base_norm_cls is Normalize: - Norm.__name__ = f"{scale_cls.__name__}Norm" - Norm.__qualname__ = f"{scale_cls.__qualname__}Norm" + ScaleNorm.__name__ = f"{scale_cls.__name__}Norm" + ScaleNorm.__qualname__ = f"{scale_cls.__qualname__}Norm" else: - Norm.__name__ = base_norm_cls.__name__ - Norm.__qualname__ = base_norm_cls.__qualname__ - Norm.__module__ = base_norm_cls.__module__ - Norm.__doc__ = base_norm_cls.__doc__ + ScaleNorm.__name__ = base_norm_cls.__name__ + ScaleNorm.__qualname__ = base_norm_cls.__qualname__ + ScaleNorm.__module__ = base_norm_cls.__module__ + ScaleNorm.__doc__ = base_norm_cls.__doc__ - return Norm + return ScaleNorm def _create_empty_object_of_class(cls): diff --git a/lib/matplotlib/colors.pyi b/lib/matplotlib/colors.pyi index eadd759bcaa3..cdc6e5e7d89f 100644 --- a/lib/matplotlib/colors.pyi +++ b/lib/matplotlib/colors.pyi @@ -1,4 +1,5 @@ from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence +from abc import ABC, abstractmethod from matplotlib import cbook, scale import re @@ -249,8 +250,29 @@ class BivarColormapFromImage(BivarColormap): origin: Sequence[float] = ..., name: str = ... ) -> None: ... -class Normalize: +class Norm(ABC): callbacks: cbook.CallbackRegistry + def __init__(self) -> None: ... + @property + @abstractmethod + def vmin(self) -> float | tuple[float] | None: ... + @property + @abstractmethod + def vmax(self) -> float | tuple[float] | None: ... + @property + @abstractmethod + def clip(self) -> bool | tuple[bool]: ... + @abstractmethod + def __call__(self, value: np.ndarray, clip: bool | None = ...) -> ArrayLike: ... + @abstractmethod + def autoscale(self, A: ArrayLike) -> None: ... + @abstractmethod + def autoscale_None(self, A: ArrayLike) -> None: ... + @abstractmethod + def scaled(self) -> bool: ... + + +class Normalize(Norm): def __init__( self, vmin: float | None = ..., vmax: float | None = ..., clip: bool = ... ) -> None: ... diff --git a/lib/matplotlib/dviread.pyi b/lib/matplotlib/dviread.pyi index 12a9215b5308..82c0238d39d1 100644 --- a/lib/matplotlib/dviread.pyi +++ b/lib/matplotlib/dviread.pyi @@ -6,7 +6,7 @@ from enum import Enum from collections.abc import Generator from typing import NamedTuple -from typing_extensions import Self # < Py 3.11 +from typing import Self class _dvistate(Enum): pre = ... diff --git a/lib/matplotlib/figure.pyi b/lib/matplotlib/figure.pyi index e7c5175d8af9..61dc79619a80 100644 --- a/lib/matplotlib/figure.pyi +++ b/lib/matplotlib/figure.pyi @@ -89,19 +89,20 @@ class FigureBase(Artist): # TODO: docstring indicates SubplotSpec a valid arg, but none of the listed signatures appear to be that @overload - def add_subplot(self, *args, projection: Literal["3d"], **kwargs) -> Axes3D: ... + def add_subplot(self, *args: Any, projection: Literal["3d"], **kwargs: Any) -> Axes3D: ... @overload def add_subplot( - self, nrows: int, ncols: int, index: int | tuple[int, int], **kwargs + self, nrows: int, ncols: int, index: int | tuple[int, int], **kwargs: Any ) -> Axes: ... @overload - def add_subplot(self, pos: int, **kwargs) -> Axes: ... + def add_subplot(self, pos: int, **kwargs: Any) -> Axes: ... @overload - def add_subplot(self, ax: Axes, **kwargs) -> Axes: ... + def add_subplot(self, ax: Axes, **kwargs: Any) -> Axes: ... @overload - def add_subplot(self, ax: SubplotSpec, **kwargs) -> Axes: ... + def add_subplot(self, ax: SubplotSpec, **kwargs: Any) -> Axes: ... @overload - def add_subplot(self, **kwargs) -> Axes: ... + def add_subplot(self, **kwargs: Any) -> Axes: ... + @overload def subplots( self, diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index 2db98b75ab2e..ab6b495631de 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -35,7 +35,7 @@ from io import BytesIO import json import logging -from numbers import Number +from numbers import Integral import os from pathlib import Path import plistlib @@ -172,6 +172,10 @@ ] +def _normalize_weight(weight): + return weight if isinstance(weight, Integral) else weight_dict[weight] + + def get_fontext_synonyms(fontext): """ Return a list of file extensions that are synonyms for @@ -1256,8 +1260,8 @@ def score_weight(self, weight1, weight2): # exact match of the weight names, e.g. weight1 == weight2 == "regular" if cbook._str_equal(weight1, weight2): return 0.0 - w1 = weight1 if isinstance(weight1, Number) else weight_dict[weight1] - w2 = weight2 if isinstance(weight2, Number) else weight_dict[weight2] + w1 = _normalize_weight(weight1) + w2 = _normalize_weight(weight2) return 0.95 * (abs(w1 - w2) / 1000) + 0.05 def score_size(self, size1, size2): @@ -1480,6 +1484,10 @@ def _findfont_cached(self, prop, fontext, directory, fallback_to_default, best_font = font if score == 0: break + if best_font is not None and (_normalize_weight(prop.get_weight()) != + _normalize_weight(best_font.weight)): + _log.warning('findfont: Failed to find font weight %s, now using %s.', + prop.get_weight(), best_font.weight) if best_font is None or best_score >= 10.0: if fallback_to_default: diff --git a/lib/matplotlib/font_manager.pyi b/lib/matplotlib/font_manager.pyi index c64ddea3e073..e865f67384cd 100644 --- a/lib/matplotlib/font_manager.pyi +++ b/lib/matplotlib/font_manager.pyi @@ -1,14 +1,13 @@ +from collections.abc import Iterable from dataclasses import dataclass +from numbers import Integral import os +from pathlib import Path +from typing import Any, Literal from matplotlib._afm import AFM from matplotlib import ft2font -from pathlib import Path - -from collections.abc import Iterable -from typing import Any, Literal - font_scalings: dict[str | None, float] stretch_dict: dict[str, int] weight_dict: dict[str, int] @@ -19,6 +18,7 @@ MSUserFontDirectories: list[str] X11FontDirectories: list[str] OSXFontDirectories: list[str] +def _normalize_weight(weight: str | Integral) -> Integral: ... def get_fontext_synonyms(fontext: str) -> list[str]: ... def list_fonts(directory: str, extensions: Iterable[str]) -> list[str]: ... def win32FontDirectory() -> str: ... diff --git a/lib/matplotlib/legend_handler.py b/lib/matplotlib/legend_handler.py index 263945b050d0..65a78891b17f 100644 --- a/lib/matplotlib/legend_handler.py +++ b/lib/matplotlib/legend_handler.py @@ -799,7 +799,6 @@ def get_first(prop_array): legend_handle.set_linewidth(get_first(orig_handle.get_linewidths())) legend_handle.set_linestyle(get_first(orig_handle.get_linestyles())) legend_handle.set_transform(get_first(orig_handle.get_transforms())) - legend_handle.set_figure(orig_handle.get_figure()) # Alpha is already taken into account by the color attributes. def create_artists(self, legend, orig_handle, diff --git a/lib/matplotlib/mlab.py b/lib/matplotlib/mlab.py index f538b79e44f0..b4b4c3f96828 100644 --- a/lib/matplotlib/mlab.py +++ b/lib/matplotlib/mlab.py @@ -48,7 +48,8 @@ """ import functools -from numbers import Number +from numbers import Integral, Number +import sys import numpy as np @@ -210,6 +211,23 @@ def detrend_linear(y): return y - (b*x + a) +def _stride_windows(x, n, noverlap=0): + x = np.asarray(x) + + _api.check_isinstance(Integral, n=n, noverlap=noverlap) + if not (1 <= n <= x.size and n < noverlap): + raise ValueError(f'n ({n}) and noverlap ({noverlap}) must be positive integers ' + f'with n < noverlap and n <= x.size ({x.size})') + + if n == 1 and noverlap == 0: + return x[np.newaxis] + + step = n - noverlap + shape = (n, (x.shape[-1]-noverlap)//step) + strides = (x.strides[0], step*x.strides[0]) + return np.lib.stride_tricks.as_strided(x, shape=shape, strides=strides) + + def _spectral_helper(x, y=None, NFFT=None, Fs=None, detrend_func=None, window=None, noverlap=None, pad_to=None, sides=None, scale_by_freq=None, mode=None): @@ -304,8 +322,12 @@ def _spectral_helper(x, y=None, NFFT=None, Fs=None, detrend_func=None, raise ValueError( "The window length must match the data's first dimension") - result = np.lib.stride_tricks.sliding_window_view( - x, NFFT, axis=0)[::NFFT - noverlap].T + if sys.maxsize > 2**32: + result = np.lib.stride_tricks.sliding_window_view( + x, NFFT, axis=0)[::NFFT - noverlap].T + else: + # The NumPy version on 32-bit will OOM, so use old implementation. + result = _stride_windows(x, NFFT, noverlap=noverlap) result = detrend(result, detrend_func, axis=0) result = result * window.reshape((-1, 1)) result = np.fft.fft(result, n=pad_to, axis=0)[:numFreqs, :] @@ -313,8 +335,12 @@ def _spectral_helper(x, y=None, NFFT=None, Fs=None, detrend_func=None, if not same_data: # if same_data is False, mode must be 'psd' - resultY = np.lib.stride_tricks.sliding_window_view( - y, NFFT, axis=0)[::NFFT - noverlap].T + if sys.maxsize > 2**32: + resultY = np.lib.stride_tricks.sliding_window_view( + y, NFFT, axis=0)[::NFFT - noverlap].T + else: + # The NumPy version on 32-bit will OOM, so use old implementation. + resultY = _stride_windows(y, NFFT, noverlap=noverlap) resultY = detrend(resultY, detrend_func, axis=0) resultY = resultY * window.reshape((-1, 1)) resultY = np.fft.fft(resultY, n=pad_to, axis=0)[:numFreqs, :] diff --git a/lib/matplotlib/mpl-data/matplotlibrc b/lib/matplotlib/mpl-data/matplotlibrc index ec649560ba3b..ccc5de5e372c 100644 --- a/lib/matplotlib/mpl-data/matplotlibrc +++ b/lib/matplotlib/mpl-data/matplotlibrc @@ -761,7 +761,7 @@ #svg.fonttype: path # How to handle SVG fonts: # path: Embed characters as paths -- supported # by most SVG renderers - # None: Assume fonts are installed on the + # none: Assume fonts are installed on the # machine where the SVG will be viewed. #svg.hashsalt: None # If not None, use this string as hash salt instead of uuid4 #svg.id: None # If not None, use this string as the value for the `id` diff --git a/lib/matplotlib/offsetbox.py b/lib/matplotlib/offsetbox.py index 1e07125cdc2a..974cc4f2db05 100644 --- a/lib/matplotlib/offsetbox.py +++ b/lib/matplotlib/offsetbox.py @@ -201,7 +201,7 @@ def _get_aligned_offsets(yspans, height, align="baseline"): class OffsetBox(martist.Artist): """ - The OffsetBox is a simple container artist. + A simple container artist. The child artists are meant to be drawn at a relative position to its parent. @@ -826,17 +826,18 @@ def draw(self, renderer): class AuxTransformBox(OffsetBox): """ - Offset Box with the aux_transform. Its children will be - transformed with the aux_transform first then will be - offsetted. The absolute coordinate of the aux_transform is meaning - as it will be automatically adjust so that the left-lower corner - of the bounding box of children will be set to (0, 0) before the - offset transform. - - It is similar to drawing area, except that the extent of the box - is not predetermined but calculated from the window extent of its - children. Furthermore, the extent of the children will be - calculated in the transformed coordinate. + An OffsetBox with an auxiliary transform. + + All child artists are first transformed with *aux_transform*, then + translated with an offset (the same for all children) so the bounding + box of the children matches the drawn box. (In other words, adding an + arbitrary translation to *aux_transform* has no effect as it will be + cancelled out by the later offsetting.) + + `AuxTransformBox` is similar to `.DrawingArea`, except that the extent of + the box is not predetermined but calculated from the window extent of its + children, and the extent of the children will be calculated in the + transformed coordinate. """ def __init__(self, aux_transform): self.aux_transform = aux_transform @@ -853,10 +854,7 @@ def add_artist(self, a): self.stale = True def get_transform(self): - """ - Return the :class:`~matplotlib.transforms.Transform` applied - to the children - """ + """Return the `.Transform` applied to the children.""" return (self.aux_transform + self.ref_offset_transform + self.offset_transform) @@ -908,7 +906,7 @@ def draw(self, renderer): class AnchoredOffsetbox(OffsetBox): """ - An offset box placed according to location *loc*. + An OffsetBox placed according to location *loc*. AnchoredOffsetbox has a single child. When multiple children are needed, use an extra OffsetBox to enclose them. By default, the offset box is diff --git a/lib/matplotlib/patches.py b/lib/matplotlib/patches.py index 63453d416b99..477eee9f5a7a 100644 --- a/lib/matplotlib/patches.py +++ b/lib/matplotlib/patches.py @@ -1538,7 +1538,7 @@ def _make_verts(self): length = distance else: length = distance + head_length - if not length: + if np.size(length) == 0: self.verts = np.empty([0, 2]) # display nothing if empty else: # start by drawing horizontal arrow, point at (0, 0) diff --git a/lib/matplotlib/projections/polar.py b/lib/matplotlib/projections/polar.py index 948b3a6e704f..00b400e0977b 100644 --- a/lib/matplotlib/projections/polar.py +++ b/lib/matplotlib/projections/polar.py @@ -679,20 +679,15 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.sticky_edges.y.append(0) - def _wrap_locator_formatter(self): - self.set_major_locator(RadialLocator(self.get_major_locator(), - self.axes)) - self.isDefault_majloc = True + def set_major_locator(self, locator): + if not isinstance(locator, RadialLocator): + locator = RadialLocator(locator, self.axes) + super().set_major_locator(locator) def clear(self): # docstring inherited super().clear() self.set_ticks_position('none') - self._wrap_locator_formatter() - - def _set_scale(self, value, **kwargs): - super()._set_scale(value, **kwargs) - self._wrap_locator_formatter() def _is_full_circle_deg(thetamin, thetamax): @@ -817,6 +812,10 @@ def _init_axis(self): self.xaxis = ThetaAxis(self, clear=False) self.yaxis = RadialAxis(self, clear=False) self.spines['polar'].register_axis(self.yaxis) + inner_spine = self.spines.get('inner', None) + if inner_spine is not None: + # Subclasses may not have inner spine. + inner_spine.register_axis(self.yaxis) def _set_lim_and_transforms(self): # A view limit where the minimum radius can be locked if the user @@ -961,7 +960,9 @@ def draw(self, renderer): thetamin, thetamax = np.rad2deg(self._realViewLim.intervalx) if thetamin > thetamax: thetamin, thetamax = thetamax, thetamin - rmin, rmax = ((self._realViewLim.intervaly - self.get_rorigin()) * + rscale_tr = self.yaxis.get_transform() + rmin, rmax = ((rscale_tr.transform(self._realViewLim.intervaly) - + rscale_tr.transform(self.get_rorigin())) * self.get_rsign()) if isinstance(self.patch, mpatches.Wedge): # Backwards-compatibility: Any subclassed Axes might override the @@ -1242,19 +1243,11 @@ def set_rlabel_position(self, value): """ self._r_label_position.clear().translate(np.deg2rad(value), 0.0) - def set_yscale(self, *args, **kwargs): - super().set_yscale(*args, **kwargs) - self.yaxis.set_major_locator( - self.RadialLocator(self.yaxis.get_major_locator(), self)) - def set_rscale(self, *args, **kwargs): return Axes.set_yscale(self, *args, **kwargs) def set_rticks(self, *args, **kwargs): - result = Axes.set_yticks(self, *args, **kwargs) - self.yaxis.set_major_locator( - self.RadialLocator(self.yaxis.get_major_locator(), self)) - return result + return Axes.set_yticks(self, *args, **kwargs) def set_thetagrids(self, angles, labels=None, fmt=None, **kwargs): """ diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index d77b06115268..1f9c4606af27 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -50,7 +50,7 @@ import sys import threading import time -from typing import TYPE_CHECKING, cast, overload +from typing import IO, TYPE_CHECKING, cast, overload from cycler import cycler # noqa: F401 import matplotlib @@ -100,7 +100,14 @@ import matplotlib.backend_bases from matplotlib.axis import Tick from matplotlib.axes._base import _AxesBase - from matplotlib.backend_bases import Event + from matplotlib.backend_bases import ( + CloseEvent, + DrawEvent, + KeyEvent, + MouseEvent, + PickEvent, + ResizeEvent, + ) from matplotlib.cm import ScalarMappable from matplotlib.contour import ContourSet, QuadContourSet from matplotlib.collections import ( @@ -126,11 +133,17 @@ from matplotlib.quiver import Barbs, Quiver, QuiverKey from matplotlib.scale import ScaleBase from matplotlib.typing import ( + CloseEventType, ColorType, CoordsType, + DrawEventType, HashableList, + KeyEventType, LineStyleType, MarkerType, + MouseEventType, + PickEventType, + ResizeEventType, ) from matplotlib.widgets import SubplotTool @@ -338,8 +351,8 @@ def uninstall_repl_displayhook() -> None: # Ensure this appears in the pyplot docs. @_copy_docstring_and_deprecators(matplotlib.set_loglevel) -def set_loglevel(*args, **kwargs) -> None: - return matplotlib.set_loglevel(*args, **kwargs) +def set_loglevel(level: str) -> None: + return matplotlib.set_loglevel(level) @_copy_docstring_and_deprecators(Artist.findobj) @@ -1175,8 +1188,32 @@ def get_current_fig_manager() -> FigureManagerBase | None: return gcf().canvas.manager +@overload +def connect(s: MouseEventType, func: Callable[[MouseEvent], Any]) -> int: ... + + +@overload +def connect(s: KeyEventType, func: Callable[[KeyEvent], Any]) -> int: ... + + +@overload +def connect(s: PickEventType, func: Callable[[PickEvent], Any]) -> int: ... + + +@overload +def connect(s: ResizeEventType, func: Callable[[ResizeEvent], Any]) -> int: ... + + +@overload +def connect(s: CloseEventType, func: Callable[[CloseEvent], Any]) -> int: ... + + +@overload +def connect(s: DrawEventType, func: Callable[[DrawEvent], Any]) -> int: ... + + @_copy_docstring_and_deprecators(FigureCanvasBase.mpl_connect) -def connect(s: str, func: Callable[[Event], Any]) -> int: +def connect(s, func) -> int: return gcf().canvas.mpl_connect(s, func) @@ -1259,11 +1296,11 @@ def draw() -> None: @_copy_docstring_and_deprecators(Figure.savefig) -def savefig(*args, **kwargs) -> None: +def savefig(fname: str | os.PathLike | IO, **kwargs) -> None: fig = gcf() # savefig default implementation has no return, so mypy is unhappy # presumably this is here because subclasses can return? - res = fig.savefig(*args, **kwargs) # type: ignore[func-returns-value] + res = fig.savefig(fname, **kwargs) # type: ignore[func-returns-value] fig.canvas.draw_idle() # Need this if 'transparent=True', to reset colors. return res diff --git a/lib/matplotlib/sankey.pyi b/lib/matplotlib/sankey.pyi index 33565b998a9c..083d590559ca 100644 --- a/lib/matplotlib/sankey.pyi +++ b/lib/matplotlib/sankey.pyi @@ -2,7 +2,7 @@ from matplotlib.axes import Axes from collections.abc import Callable, Iterable from typing import Any -from typing_extensions import Self # < Py 3.11 +from typing import Self import numpy as np diff --git a/lib/matplotlib/spines.py b/lib/matplotlib/spines.py index 7e77a393f2a2..9732a2f3347a 100644 --- a/lib/matplotlib/spines.py +++ b/lib/matplotlib/spines.py @@ -265,11 +265,17 @@ def _adjust_location(self): self._path = mpath.Path.arc(np.rad2deg(low), np.rad2deg(high)) if self.spine_type == 'bottom': - rmin, rmax = self.axes.viewLim.intervaly + if self.axis is None: + tr = mtransforms.IdentityTransform() + else: + tr = self.axis.get_transform() + rmin, rmax = tr.transform(self.axes.viewLim.intervaly) try: rorigin = self.axes.get_rorigin() except AttributeError: rorigin = rmin + else: + rorigin = tr.transform(rorigin) scaled_diameter = (rmin - rorigin) / (rmax - rorigin) self._height = scaled_diameter self._width = scaled_diameter diff --git a/lib/matplotlib/style/__init__.py b/lib/matplotlib/style/__init__.py index a202cfe08b20..80c6de00a18d 100644 --- a/lib/matplotlib/style/__init__.py +++ b/lib/matplotlib/style/__init__.py @@ -6,7 +6,8 @@ ``context`` Context manager to use a style sheet temporarily. ``available`` - List available style sheets. + List available style sheets. Underscore-prefixed names are considered private and + not listed, though may still be accessed directly from ``library``. ``library`` A dictionary of style names and matplotlib settings. """ @@ -245,8 +246,8 @@ def update_nested_dict(main_dict, new_dict): def reload_library(): """Reload the style library.""" library.clear() - library.update(_update_user_library(_base_library)) - available[:] = sorted(library.keys()) + library.update(_update_user_library(_base_library.copy())) + available[:] = sorted(name for name in library if not name.startswith('_')) reload_library() diff --git a/lib/matplotlib/tests/baseline_images/test_colors/test_norm_abc.png b/lib/matplotlib/tests/baseline_images/test_colors/test_norm_abc.png new file mode 100644 index 000000000000..077365674ac2 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_colors/test_norm_abc.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_alignment.png b/lib/matplotlib/tests/baseline_images/test_polar/polar_alignment.png index e979e7ebb6b8..7a12c5d5c783 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_polar/polar_alignment.png and b/lib/matplotlib/tests/baseline_images/test_polar/polar_alignment.png differ diff --git a/lib/matplotlib/tests/test_api.py b/lib/matplotlib/tests/test_api.py index f04604c14cce..58e7986bfce6 100644 --- a/lib/matplotlib/tests/test_api.py +++ b/lib/matplotlib/tests/test_api.py @@ -13,7 +13,7 @@ if typing.TYPE_CHECKING: - from typing_extensions import Self + from typing import Self T = TypeVar('T') diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index db0629de99b5..c96173e340f7 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -3420,6 +3420,20 @@ def test_stackplot_hatching(fig_ref, fig_test): ax_ref.set_ylim(0, 70) +def test_stackplot_subfig_legend(): + # Smoke test for https://github.com/matplotlib/matplotlib/issues/30158 + + fig = plt.figure() + subfigs = fig.subfigures(nrows=1, ncols=2) + + for _fig in subfigs: + ax = _fig.subplots(nrows=1, ncols=1) + ax.stackplot([3, 4], [[1, 2]], labels=['a']) + + fig.legend() + fig.draw_without_rendering() + + def _bxp_test_helper( stats_kwargs={}, transform_stats=lambda s: s, bxp_kwargs={}): np.random.seed(937) @@ -4744,6 +4758,11 @@ def _assert_equal(stem_container, expected): _assert_equal(ax.stem(y, linefmt='r--'), expected=([0, 1, 2], y)) _assert_equal(ax.stem(y, 'r--'), expected=([0, 1, 2], y)) + with pytest.raises(ValueError): + ax.stem([[y]]) + with pytest.raises(ValueError): + ax.stem([[x]], y) + def test_stem_markerfmt(): """Test that stem(..., markerfmt=...) produces the intended markers.""" diff --git a/lib/matplotlib/tests/test_backends_interactive.py b/lib/matplotlib/tests/test_backends_interactive.py index a27783fa4be1..9f8522a9df4a 100644 --- a/lib/matplotlib/tests/test_backends_interactive.py +++ b/lib/matplotlib/tests/test_backends_interactive.py @@ -649,12 +649,9 @@ def _impl_test_interactive_timers(): # milliseconds, which the mac framework interprets as singleshot. # We only want singleshot if we specify that ourselves, otherwise we want # a repeating timer - import os from unittest.mock import Mock import matplotlib.pyplot as plt - # increase pause duration on CI to let things spin up - # particularly relevant for gtk3cairo - pause_time = 2 if os.getenv("CI") else 0.5 + pause_time = 0.5 fig = plt.figure() plt.pause(pause_time) timer = fig.canvas.new_timer(0.1) diff --git a/lib/matplotlib/tests/test_colors.py b/lib/matplotlib/tests/test_colors.py index 71b7816f0c04..73de50408401 100644 --- a/lib/matplotlib/tests/test_colors.py +++ b/lib/matplotlib/tests/test_colors.py @@ -7,6 +7,7 @@ from PIL import Image import pytest import base64 +import platform from numpy.testing import assert_array_equal, assert_array_almost_equal @@ -1829,3 +1830,49 @@ def test_LinearSegmentedColormap_from_list_value_color_tuple(): cmap([value for value, _ in value_color_tuples]), to_rgba_array([color for _, color in value_color_tuples]), ) + + +@image_comparison(['test_norm_abc.png'], remove_text=True, + tol=0 if platform.machine() == 'x86_64' else 0.05) +def test_norm_abc(): + + class CustomHalfNorm(mcolors.Norm): + def __init__(self): + super().__init__() + + @property + def vmin(self): + return 0 + + @property + def vmax(self): + return 1 + + @property + def clip(self): + return False + + def __call__(self, value, clip=None): + return value / 2 + + def inverse(self, value): + return 2 * value + + def autoscale(self, A): + pass + + def autoscale_None(self, A): + pass + + def scaled(self): + return True + + fig, axes = plt.subplots(2,2) + + r = np.linspace(-1, 3, 16*16).reshape((16,16)) + norm = CustomHalfNorm() + colorizer = mpl.colorizer.Colorizer(cmap='viridis', norm=norm) + c = axes[0,0].imshow(r, colorizer=colorizer) + axes[0,1].pcolor(r, colorizer=colorizer) + axes[1,0].contour(r, colorizer=colorizer) + axes[1,1].contourf(r, colorizer=colorizer) diff --git a/lib/matplotlib/tests/test_font_manager.py b/lib/matplotlib/tests/test_font_manager.py index 97ee8672b1d4..24421b8e30b3 100644 --- a/lib/matplotlib/tests/test_font_manager.py +++ b/lib/matplotlib/tests/test_font_manager.py @@ -15,7 +15,8 @@ from matplotlib.font_manager import ( findfont, findSystemFonts, FontEntry, FontProperties, fontManager, json_dump, json_load, get_font, is_opentype_cff_font, - MSUserFontDirectories, _get_fontconfig_fonts, ttfFontProperty) + MSUserFontDirectories, ttfFontProperty, + _get_fontconfig_fonts, _normalize_weight) from matplotlib import cbook, ft2font, pyplot as plt, rc_context, figure as mfigure from matplotlib.testing import subprocess_run_helper, subprocess_run_for_testing @@ -407,3 +408,29 @@ def test_fontproperties_init_deprecation(): # Since this case is not covered by docs, I've refrained from jumping # extra hoops to detect this possible API misuse. FontProperties(family="serif-24:style=oblique:weight=bold") + + +def test_normalize_weights(): + assert _normalize_weight(300) == 300 # passthrough + assert _normalize_weight('ultralight') == 100 + assert _normalize_weight('light') == 200 + assert _normalize_weight('normal') == 400 + assert _normalize_weight('regular') == 400 + assert _normalize_weight('book') == 400 + assert _normalize_weight('medium') == 500 + assert _normalize_weight('roman') == 500 + assert _normalize_weight('semibold') == 600 + assert _normalize_weight('demibold') == 600 + assert _normalize_weight('demi') == 600 + assert _normalize_weight('bold') == 700 + assert _normalize_weight('heavy') == 800 + assert _normalize_weight('extra bold') == 800 + assert _normalize_weight('black') == 900 + with pytest.raises(KeyError): + _normalize_weight('invalid') + + +def test_font_match_warning(caplog): + findfont(FontProperties(family=["DejaVu Sans"], weight=750)) + logs = [rec.message for rec in caplog.records] + assert 'findfont: Failed to find font weight 750, now using 700.' in logs diff --git a/lib/matplotlib/tests/test_matplotlib.py b/lib/matplotlib/tests/test_matplotlib.py index 37b41fafdb78..d0a3f8c617e1 100644 --- a/lib/matplotlib/tests/test_matplotlib.py +++ b/lib/matplotlib/tests/test_matplotlib.py @@ -1,6 +1,7 @@ import os import subprocess import sys +from unittest.mock import patch import pytest @@ -80,3 +81,16 @@ def test_importable_with__OO(): [sys.executable, "-OO", "-c", program], env={**os.environ, "MPLBACKEND": ""}, check=True ) + + +@patch('matplotlib.subprocess.check_output') +def test_get_executable_info_timeout(mock_check_output): + """ + Test that _get_executable_info raises ExecutableNotFoundError if the + command times out. + """ + + mock_check_output.side_effect = subprocess.TimeoutExpired(cmd=['mock'], timeout=30) + + with pytest.raises(matplotlib.ExecutableNotFoundError, match='Timed out'): + matplotlib._get_executable_info.__wrapped__('inkscape') diff --git a/lib/matplotlib/tests/test_patches.py b/lib/matplotlib/tests/test_patches.py index 4ed9222eb95e..d69a9dad4337 100644 --- a/lib/matplotlib/tests/test_patches.py +++ b/lib/matplotlib/tests/test_patches.py @@ -1093,3 +1093,9 @@ def test_facecolor_none_edgecolor_force_edgecolor(): rcParams['patch.edgecolor'] = 'red' rect = Rectangle((0, 0), 1, 1, facecolor="none") assert mcolors.same_color(rect.get_edgecolor(), rcParams['patch.edgecolor']) + + +def test_empty_fancyarrow(): + fig, ax = plt.subplots() + arrow = ax.arrow([], [], [], []) + assert arrow is not None diff --git a/lib/matplotlib/tests/test_polar.py b/lib/matplotlib/tests/test_polar.py index 31e8cdd89a21..4f9e63380490 100644 --- a/lib/matplotlib/tests/test_polar.py +++ b/lib/matplotlib/tests/test_polar.py @@ -3,8 +3,10 @@ import pytest import matplotlib as mpl +from matplotlib.projections.polar import RadialLocator from matplotlib import pyplot as plt from matplotlib.testing.decorators import image_comparison, check_figures_equal +import matplotlib.ticker as mticker @image_comparison(['polar_axes.png'], style='default', tol=0.012) @@ -482,6 +484,26 @@ def test_polar_log(): ax.plot(np.linspace(0, 2 * np.pi, n), np.logspace(0, 2, n)) +@check_figures_equal() +def test_polar_log_rorigin(fig_ref, fig_test): + # Test that equivalent linear and log radial settings give the same axes patch + # and spines. + ax_ref = fig_ref.add_subplot(projection='polar', facecolor='red') + ax_ref.set_rlim(0, 2) + ax_ref.set_rorigin(-3) + ax_ref.set_rticks(np.linspace(0, 2, 5)) + + ax_test = fig_test.add_subplot(projection='polar', facecolor='red') + ax_test.set_rscale('log') + ax_test.set_rlim(1, 100) + ax_test.set_rorigin(10**-3) + ax_test.set_rticks(np.logspace(0, 2, 5)) + + for ax in ax_ref, ax_test: + # Radial tick labels should be the only difference, so turn them off. + ax.tick_params(labelleft=False) + + def test_polar_neg_theta_lims(): fig = plt.figure() ax = fig.add_subplot(projection='polar') @@ -526,3 +548,43 @@ def test_radial_limits_behavior(): # negative data also autoscales to negative limits ax.plot([1, 2], [-1, -2]) assert ax.get_ylim() == (-2, 2) + + +def test_radial_locator_wrapping(): + # Check that the locator is always wrapped inside a RadialLocator + # and that RaidialAxis.isDefault_majloc is set correctly. + fig, ax = plt.subplots(subplot_kw={'projection': 'polar'}) + assert ax.yaxis.isDefault_majloc + assert isinstance(ax.yaxis.get_major_locator(), RadialLocator) + + # set an explicit locator + locator = mticker.MaxNLocator(3) + ax.yaxis.set_major_locator(locator) + assert not ax.yaxis.isDefault_majloc + assert isinstance(ax.yaxis.get_major_locator(), RadialLocator) + assert ax.yaxis.get_major_locator().base is locator + + ax.clear() # reset to the default locator + assert ax.yaxis.isDefault_majloc + assert isinstance(ax.yaxis.get_major_locator(), RadialLocator) + + ax.set_rticks([0, 1, 2, 3]) # implicitly sets a FixedLocator + assert not ax.yaxis.isDefault_majloc # because of the fixed ticks + assert isinstance(ax.yaxis.get_major_locator(), RadialLocator) + assert isinstance(ax.yaxis.get_major_locator().base, mticker.FixedLocator) + + ax.clear() + + ax.set_rgrids([0, 1, 2, 3]) # implicitly sets a FixedLocator + assert not ax.yaxis.isDefault_majloc # because of the fixed ticks + assert isinstance(ax.yaxis.get_major_locator(), RadialLocator) + assert isinstance(ax.yaxis.get_major_locator().base, mticker.FixedLocator) + + ax.clear() + + ax.set_yscale("log") # implicitly sets a LogLocator + # Note that the LogLocator is still considered the default locator + # for the log scale + assert ax.yaxis.isDefault_majloc + assert isinstance(ax.yaxis.get_major_locator(), RadialLocator) + assert isinstance(ax.yaxis.get_major_locator().base, mticker.LogLocator) diff --git a/lib/matplotlib/tests/test_pyplot.py b/lib/matplotlib/tests/test_pyplot.py index ab713707bace..55f7c33cb52e 100644 --- a/lib/matplotlib/tests/test_pyplot.py +++ b/lib/matplotlib/tests/test_pyplot.py @@ -1,4 +1,5 @@ import difflib +import inspect import numpy as np import sys @@ -484,3 +485,26 @@ def test_matshow(): # Smoke test that matshow does not ask for a new figsize on the existing figure plt.matshow(arr, fignum=fig.number) + + +def assert_same_signature(func1, func2): + """ + Assert that `func1` and `func2` have the same arguments, + i.e. same parameter count, names and kinds. + + :param func1: First function to check + :param func2: Second function to check + """ + params1 = inspect.signature(func1).parameters + params2 = inspect.signature(func2).parameters + + assert len(params1) == len(params2) + assert all([ + params1[p].name == params2[p].name and + params1[p].kind == params2[p].kind + for p in params1 + ]) + + +def test_setloglevel_signature(): + assert_same_signature(plt.set_loglevel, mpl.set_loglevel) diff --git a/lib/matplotlib/tests/test_spines.py b/lib/matplotlib/tests/test_spines.py index 353aede00298..d6ddcabb6878 100644 --- a/lib/matplotlib/tests/test_spines.py +++ b/lib/matplotlib/tests/test_spines.py @@ -154,3 +154,15 @@ def test_spines_black_axes(): ax.set_xticks([]) ax.set_yticks([]) ax.set_facecolor((0, 0, 0)) + + +def test_arc_spine_inner_no_axis(): + # Backcompat: smoke test that inner arc spine does not need a registered + # axis in order to be drawn + fig = plt.figure() + ax = fig.add_subplot(projection="polar") + inner_spine = ax.spines["inner"] + inner_spine.register_axis(None) + assert ax.spines["inner"].axis is None + + fig.draw_without_rendering() diff --git a/lib/matplotlib/tests/test_style.py b/lib/matplotlib/tests/test_style.py index 4d76a4ecfa8b..7b54f1141720 100644 --- a/lib/matplotlib/tests/test_style.py +++ b/lib/matplotlib/tests/test_style.py @@ -21,6 +21,7 @@ def temp_style(style_name, settings=None): if not settings: settings = DUMMY_SETTINGS temp_file = f'{style_name}.mplstyle' + orig_library_paths = style.USER_LIBRARY_PATHS try: with TemporaryDirectory() as tmpdir: # Write style settings to file in the tmpdir. @@ -32,6 +33,7 @@ def temp_style(style_name, settings=None): style.reload_library() yield finally: + style.USER_LIBRARY_PATHS = orig_library_paths style.reload_library() @@ -46,8 +48,17 @@ def test_invalid_rc_warning_includes_filename(caplog): def test_available(): - with temp_style('_test_', DUMMY_SETTINGS): - assert '_test_' in style.available + # Private name should not be listed in available but still usable. + assert '_classic_test_patch' not in style.available + assert '_classic_test_patch' in style.library + + with temp_style('_test_', DUMMY_SETTINGS), temp_style('dummy', DUMMY_SETTINGS): + assert 'dummy' in style.available + assert 'dummy' in style.library + assert '_test_' not in style.available + assert '_test_' in style.library + assert 'dummy' not in style.available + assert '_test_' not in style.available def test_use(): diff --git a/lib/matplotlib/tests/test_subplots.py b/lib/matplotlib/tests/test_subplots.py index a899110ac77a..0f00a88aa72d 100644 --- a/lib/matplotlib/tests/test_subplots.py +++ b/lib/matplotlib/tests/test_subplots.py @@ -4,6 +4,7 @@ import numpy as np import pytest +import matplotlib as mpl from matplotlib.axes import Axes, SubplotBase import matplotlib.pyplot as plt from matplotlib.testing.decorators import check_figures_equal, image_comparison @@ -111,10 +112,15 @@ def test_shared(): @pytest.mark.parametrize('remove_ticks', [True, False]) -def test_label_outer(remove_ticks): - f, axs = plt.subplots(2, 2, sharex=True, sharey=True) +@pytest.mark.parametrize('layout_engine', ['none', 'tight', 'constrained']) +@pytest.mark.parametrize('with_colorbar', [True, False]) +def test_label_outer(remove_ticks, layout_engine, with_colorbar): + fig = plt.figure(layout=layout_engine) + axs = fig.subplots(2, 2, sharex=True, sharey=True) for ax in axs.flat: ax.set(xlabel="foo", ylabel="bar") + if with_colorbar: + fig.colorbar(mpl.cm.ScalarMappable(), ax=ax) ax.label_outer(remove_inner_ticks=remove_ticks) check_ticklabel_visible( axs.flat, [False, False, True, True], [True, False, True, False]) diff --git a/lib/matplotlib/tests/test_triangulation.py b/lib/matplotlib/tests/test_triangulation.py index 337443eb1e27..ae065a231fd9 100644 --- a/lib/matplotlib/tests/test_triangulation.py +++ b/lib/matplotlib/tests/test_triangulation.py @@ -612,7 +612,7 @@ def test_triinterpcubic_cg_solver(): # 1) A commonly used test involves a 2d Poisson matrix. def poisson_sparse_matrix(n, m): """ - Return the sparse, (n*m, n*m) matrix in coo format resulting from the + Return the sparse, (n*m, n*m) matrix in COO format resulting from the discretisation of the 2-dimensional Poisson equation according to a finite difference numerical scheme on a uniform (n, m) grid. """ diff --git a/lib/matplotlib/tests/test_widgets.py b/lib/matplotlib/tests/test_widgets.py index 808863fd6a94..3da678076489 100644 --- a/lib/matplotlib/tests/test_widgets.py +++ b/lib/matplotlib/tests/test_widgets.py @@ -3,7 +3,7 @@ import operator from unittest import mock -from matplotlib.backend_bases import MouseEvent +from matplotlib.backend_bases import MouseEvent, DrawEvent import matplotlib.colors as mcolors import matplotlib.widgets as widgets import matplotlib.pyplot as plt @@ -1757,3 +1757,26 @@ def test_MultiCursor(horizOn, vertOn): assert l.get_xdata() == (.5, .5) for l in multi.hlines: assert l.get_ydata() == (.25, .25) + + +def test_parent_axes_removal(): + + fig, (ax_radio, ax_checks) = plt.subplots(1, 2) + + radio = widgets.RadioButtons(ax_radio, ['1', '2'], 0) + checks = widgets.CheckButtons(ax_checks, ['1', '2'], [True, False]) + + ax_checks.remove() + ax_radio.remove() + with io.BytesIO() as out: + # verify that saving does not raise + fig.savefig(out, format='raw') + + # verify that this method which is triggered by a draw_event callback when + # blitting is enabled does not raise. Calling private methods is simpler + # than trying to force blitting to be enabled with Agg or use a GUI + # framework. + renderer = fig._get_renderer() + evt = DrawEvent('draw_event', fig.canvas, renderer) + radio._clear(evt) + checks._clear(evt) diff --git a/lib/matplotlib/tri/_triinterpolate.py b/lib/matplotlib/tri/_triinterpolate.py index 90ad6cf3a76c..2dc62770c7ed 100644 --- a/lib/matplotlib/tri/_triinterpolate.py +++ b/lib/matplotlib/tri/_triinterpolate.py @@ -928,7 +928,7 @@ def get_Kff_and_Ff(self, J, ecc, triangles, Uc): Returns ------- - (Kff_rows, Kff_cols, Kff_vals) Kff matrix in coo format - Duplicate + (Kff_rows, Kff_cols, Kff_vals) Kff matrix in COO format - Duplicate (row, col) entries must be summed. Ff: force vector - dim npts * 3 """ @@ -961,12 +961,12 @@ def get_Kff_and_Ff(self, J, ecc, triangles, Uc): # [ Kcf Kff ] # * As F = K x U one gets straightforwardly: Ff = - Kfc x Uc - # Computing Kff stiffness matrix in sparse coo format + # Computing Kff stiffness matrix in sparse COO format Kff_vals = np.ravel(K_elem[np.ix_(vec_range, f_dof, f_dof)]) Kff_rows = np.ravel(f_row_indices[np.ix_(vec_range, f_dof, f_dof)]) Kff_cols = np.ravel(f_col_indices[np.ix_(vec_range, f_dof, f_dof)]) - # Computing Ff force vector in sparse coo format + # Computing Ff force vector in sparse COO format Kfc_elem = K_elem[np.ix_(vec_range, f_dof, c_dof)] Uc_elem = np.expand_dims(Uc, axis=2) Ff_elem = -(Kfc_elem @ Uc_elem)[:, :, 0] @@ -1178,7 +1178,7 @@ def compute_dz(self): triangles = self._triangles Uc = self.z[self._triangles] - # Building stiffness matrix and force vector in coo format + # Building stiffness matrix and force vector in COO format Kff_rows, Kff_cols, Kff_vals, Ff = reference_element.get_Kff_and_Ff( J, eccs, triangles, Uc) @@ -1215,7 +1215,7 @@ def compute_dz(self): class _Sparse_Matrix_coo: def __init__(self, vals, rows, cols, shape): """ - Create a sparse matrix in coo format. + Create a sparse matrix in COO format. *vals*: arrays of values of non-null entries of the matrix *rows*: int arrays of rows of non-null entries of the matrix *cols*: int arrays of cols of non-null entries of the matrix diff --git a/lib/matplotlib/typing.py b/lib/matplotlib/typing.py index df192df76b33..270da6bcd5e0 100644 --- a/lib/matplotlib/typing.py +++ b/lib/matplotlib/typing.py @@ -12,7 +12,8 @@ """ from collections.abc import Hashable, Sequence import pathlib -from typing import Any, Callable, Literal, TypeAlias, TypeVar, Union +from typing import Any, Literal, TypeAlias, TypeVar, Union +from collections.abc import Callable from . import path from ._enums import JoinStyle, CapStyle @@ -69,7 +70,16 @@ ) """See :doc:`/gallery/lines_bars_and_markers/markevery_demo`.""" -MarkerType: TypeAlias = str | path.Path | MarkerStyle +MarkerType: TypeAlias = ( + path.Path | MarkerStyle | str | # str required for "$...$" marker + Literal[ + ".", ",", "o", "v", "^", "<", ">", + "1", "2", "3", "4", "8", "s", "p", + "P", "*", "h", "H", "+", "x", "X", + "D", "d", "|", "_", "none", " ", + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 + ] | list[tuple[int, int]] | tuple[int, Literal[0, 1, 2], int] +) """ Marker specification. See :doc:`/gallery/lines_bars_and_markers/marker_reference`. """ @@ -107,3 +117,33 @@ _HT = TypeVar("_HT", bound=Hashable) HashableList: TypeAlias = list[_HT | "HashableList[_HT]"] """A nested list of Hashable values.""" + +MouseEventType: TypeAlias = Literal[ + "button_press_event", + "button_release_event", + "motion_notify_event", + "scroll_event", + "figure_enter_event", + "figure_leave_event", + "axes_enter_event", + "axes_leave_event", +] + +KeyEventType: TypeAlias = Literal[ + "key_press_event", + "key_release_event" +] + +DrawEventType: TypeAlias = Literal["draw_event"] +PickEventType: TypeAlias = Literal["pick_event"] +ResizeEventType: TypeAlias = Literal["resize_event"] +CloseEventType: TypeAlias = Literal["close_event"] + +EventType: TypeAlias = Literal[ + MouseEventType, + KeyEventType, + DrawEventType, + PickEventType, + ResizeEventType, + CloseEventType, +] diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 9ded7c61ce2d..41e05e6d9a05 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -117,7 +117,9 @@ def __init__(self, ax): self.ax = ax self._cids = [] - canvas = property(lambda self: self.ax.get_figure(root=True).canvas) + canvas = property( + lambda self: getattr(self.ax.get_figure(root=True), 'canvas', None) + ) def connect_event(self, event, callback): """ @@ -144,6 +146,10 @@ def _get_data_coords(self, event): return ((event.xdata, event.ydata) if event.inaxes is self.ax else self.ax.transData.inverted().transform((event.x, event.y))) + def ignore(self, event): + # docstring inherited + return super().ignore(event) or self.canvas is None + class Button(AxesWidget): """ @@ -2181,7 +2187,9 @@ def connect_default_events(self): def ignore(self, event): # docstring inherited - if not self.active or not self.ax.get_visible(): + if super().ignore(event): + return True + if not self.ax.get_visible(): return True # If canvas was locked if not self.canvas.widgetlock.available(self): diff --git a/pyproject.toml b/pyproject.toml index b980e512769a..b06a5bcc5740 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -93,19 +93,13 @@ sections = "FUTURE,STDLIB,THIRDPARTY,PYDATA,FIRSTPARTY,LOCALFOLDER" force_sort_within_sections = true [tool.ruff] -exclude = [ - ".git", +extend-exclude = [ "build", "doc/gallery", "doc/tutorials", "tools/gh_api.py", - ".tox", - ".eggs", - # TODO: fix .ipynb files - "*.ipynb" ] line-length = 88 -target-version = "py311" [tool.ruff.lint] ignore = [ @@ -131,9 +125,7 @@ ignore = [ "D404", "D413", "D415", - "D416", "D417", - "E24", "E266", "E305", "E306", @@ -148,6 +140,7 @@ select = [ "E", "F", "W", + "UP035", # The following error codes require the preview mode to be enabled. "E201", "E202", @@ -174,15 +167,13 @@ convention = "numpy" [tool.ruff.lint.per-file-ignores] "*.pyi" = ["E501"] +"*.ipynb" = ["E402"] "doc/conf.py" = ["E402"] -"galleries/examples/animation/frame_grabbing_sgskip.py" = ["E402"] "galleries/examples/images_contours_and_fields/tricontour_demo.py" = ["E201"] "galleries/examples/images_contours_and_fields/tripcolor_demo.py" = ["E201"] "galleries/examples/images_contours_and_fields/triplot_demo.py" = ["E201"] "galleries/examples/lines_bars_and_markers/marker_reference.py" = ["E402"] -"galleries/examples/misc/print_stdout_sgskip.py" = ["E402"] "galleries/examples/misc/table_demo.py" = ["E201"] -"galleries/examples/style_sheets/bmh.py" = ["E501"] "galleries/examples/subplots_axes_and_figures/demo_constrained_layout.py" = ["E402"] "galleries/examples/text_labels_and_annotations/custom_legends.py" = ["E402"] "galleries/examples/ticks/date_concise_formatter.py" = ["E402"] @@ -211,21 +202,17 @@ convention = "numpy" "lib/mpl_toolkits/axisartist/angle_helper.py" = ["E221"] "lib/mpl_toolkits/mplot3d/proj3d.py" = ["E201"] -"galleries/users_explain/artists/paths.py" = ["E402"] "galleries/users_explain/quick_start.py" = ["E402"] "galleries/users_explain/artists/patheffects_guide.py" = ["E402"] -"galleries/users_explain/artists/transforms_tutorial.py" = ["E402", "E501"] -"galleries/users_explain/colors/colormaps.py" = ["E501"] +"galleries/users_explain/artists/transforms_tutorial.py" = ["E402"] "galleries/users_explain/colors/colors.py" = ["E402"] "galleries/tutorials/artists.py" = ["E402"] "galleries/users_explain/axes/constrainedlayout_guide.py" = ["E402"] "galleries/users_explain/axes/legend_guide.py" = ["E402"] "galleries/users_explain/axes/tight_layout_guide.py" = ["E402"] "galleries/users_explain/animations/animations.py" = ["E501"] -"galleries/tutorials/images.py" = ["E501"] "galleries/tutorials/pyplot.py" = ["E402", "E501"] "galleries/users_explain/text/annotations.py" = ["E402", "E501"] -"galleries/users_explain/text/mathtext.py" = ["E501"] "galleries/users_explain/text/text_intro.py" = ["E402"] "galleries/users_explain/text/text_props.py" = ["E501"] @@ -236,18 +223,12 @@ enable_error_code = [ "redundant-expr", "truthy-bool", ] -enable_incomplete_feature = [ - "Unpack", -] exclude = [ #stubtest ".*/matplotlib/(sphinxext|backends|pylab|testing/jpl_units)", #mypy precommit "galleries/", "doc/", - "lib/matplotlib/backends/", - "lib/matplotlib/sphinxext", - "lib/matplotlib/testing/jpl_units", "lib/mpl_toolkits/", #removing tests causes errors in backends "lib/matplotlib/tests/", diff --git a/src/_backend_agg.h b/src/_backend_agg.h index 6eb54e485e86..1ac3d4c06b13 100644 --- a/src/_backend_agg.h +++ b/src/_backend_agg.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "agg_alpha_mask_u8.h" @@ -123,9 +124,6 @@ class RendererAgg typedef agg::renderer_base renderer_base_alpha_mask_type; typedef agg::renderer_scanline_aa_solid renderer_alpha_mask_type; - /* TODO: Remove facepair_t */ - typedef std::pair facepair_t; - RendererAgg(unsigned int width, unsigned int height, double dpi); virtual ~RendererAgg(); @@ -248,7 +246,7 @@ class RendererAgg bool render_clippath(mpl::PathIterator &clippath, const agg::trans_affine &clippath_trans, e_snap_mode snap_mode); template - void _draw_path(PathIteratorType &path, bool has_clippath, const facepair_t &face, GCAgg &gc); + void _draw_path(PathIteratorType &path, bool has_clippath, const std::optional &face, GCAgg &gc); template inline void -RendererAgg::_draw_path(path_t &path, bool has_clippath, const facepair_t &face, GCAgg &gc) +RendererAgg::_draw_path(path_t &path, bool has_clippath, const std::optional &face, GCAgg &gc) { typedef agg::conv_stroke stroke_t; typedef agg::conv_dash dash_t; @@ -306,7 +304,7 @@ RendererAgg::_draw_path(path_t &path, bool has_clippath, const facepair_t &face, typedef agg::renderer_scanline_bin_solid amask_bin_renderer_type; // Render face - if (face.first) { + if (face) { theRasterizer.add_path(path); if (gc.isaa) { @@ -314,10 +312,10 @@ RendererAgg::_draw_path(path_t &path, bool has_clippath, const facepair_t &face, pixfmt_amask_type pfa(pixFmt, alphaMask); amask_ren_type r(pfa); amask_aa_renderer_type ren(r); - ren.color(face.second); + ren.color(*face); agg::render_scanlines(theRasterizer, scanlineAlphaMask, ren); } else { - rendererAA.color(face.second); + rendererAA.color(*face); agg::render_scanlines(theRasterizer, slineP8, rendererAA); } } else { @@ -325,10 +323,10 @@ RendererAgg::_draw_path(path_t &path, bool has_clippath, const facepair_t &face, pixfmt_amask_type pfa(pixFmt, alphaMask); amask_ren_type r(pfa); amask_bin_renderer_type ren(r); - ren.color(face.second); + ren.color(*face); agg::render_scanlines(theRasterizer, scanlineAlphaMask, ren); } else { - rendererBin.color(face.second); + rendererBin.color(*face); agg::render_scanlines(theRasterizer, slineP8, rendererBin); } } @@ -458,7 +456,10 @@ RendererAgg::draw_path(GCAgg &gc, PathIterator &path, agg::trans_affine &trans, typedef agg::conv_curve curve_t; typedef Sketch sketch_t; - facepair_t face(color.a != 0.0, color); + std::optional face; + if (color.a != 0.0) { + face = color; + } theRasterizer.reset_clipping(); rendererBase.reset_clipping(true); @@ -467,7 +468,7 @@ RendererAgg::draw_path(GCAgg &gc, PathIterator &path, agg::trans_affine &trans, trans *= agg::trans_affine_scaling(1.0, -1.0); trans *= agg::trans_affine_translation(0.0, (double)height); - bool clip = !face.first && !gc.has_hatchpath(); + bool clip = !face && !gc.has_hatchpath(); bool simplify = path.should_simplify() && clip; double snapping_linewidth = points_to_pixels(gc.linewidth); if (gc.color.a == 0.0) { @@ -529,7 +530,10 @@ inline void RendererAgg::draw_markers(GCAgg &gc, curve_t path_curve(path_snapped); path_curve.rewind(0); - facepair_t face(color.a != 0.0, color); + std::optional face; + if (color.a != 0.0) { + face = color; + } // maxim's suggestions for cached scanlines agg::scanline_storage_aa8 scanlines; @@ -541,7 +545,7 @@ inline void RendererAgg::draw_markers(GCAgg &gc, try { std::vector fillBuffer; - if (face.first) { + if (face) { theRasterizer.add_path(marker_path_curve); agg::render_scanlines(theRasterizer, slineP8, scanlines); fillBuffer.resize(scanlines.byte_size()); @@ -605,8 +609,8 @@ inline void RendererAgg::draw_markers(GCAgg &gc, amask_ren_type r(pfa); amask_aa_renderer_type ren(r); - if (face.first) { - ren.color(face.second); + if (face) { + ren.color(*face); sa.init(fillBuffer.data(), fillBuffer.size(), x, y); agg::render_scanlines(sa, sl, ren); } @@ -633,8 +637,8 @@ inline void RendererAgg::draw_markers(GCAgg &gc, continue; } - if (face.first) { - rendererAA.color(face.second); + if (face) { + rendererAA.color(*face); sa.init(fillBuffer.data(), fillBuffer.size(), x, y); agg::render_scanlines(sa, sl, rendererAA); } @@ -936,10 +940,9 @@ inline void RendererAgg::_draw_path_collection_generic(GCAgg &gc, // Set some defaults, assuming no face or edge gc.linewidth = 0.0; - facepair_t face; - face.first = Nfacecolors != 0; + std::optional face; agg::trans_affine trans; - bool do_clip = !face.first && !gc.has_hatchpath(); + bool do_clip = Nfacecolors == 0 && !gc.has_hatchpath(); for (int i = 0; i < (int)N; ++i) { typename PathGenerator::path_iterator path = path_generator(i); @@ -970,7 +973,7 @@ inline void RendererAgg::_draw_path_collection_generic(GCAgg &gc, if (Nfacecolors) { int ic = i % Nfacecolors; - face.second = agg::rgba(facecolors(ic, 0), facecolors(ic, 1), facecolors(ic, 2), facecolors(ic, 3)); + face.emplace(facecolors(ic, 0), facecolors(ic, 1), facecolors(ic, 2), facecolors(ic, 3)); } if (Nedgecolors) { diff --git a/src/_macosx.m b/src/_macosx.m index aa2a6e68cda5..1372157bc80d 100755 --- a/src/_macosx.m +++ b/src/_macosx.m @@ -258,7 +258,7 @@ static void lazy_init(void) { } static PyObject* -stop(PyObject* self) +stop(PyObject* self, PyObject* _ /* ignored */) { stopWithEvent(); Py_RETURN_NONE; @@ -1863,7 +1863,7 @@ - (void)flagsChanged:(NSEvent *)event "written on the file descriptor given as argument.")}, {"stop", (PyCFunction)stop, - METH_NOARGS, + METH_VARARGS, PyDoc_STR("Stop the NSApp.")}, {"show", (PyCFunction)show, diff --git a/src/ft2font.cpp b/src/ft2font.cpp index bdfa2873ca80..da1bd19dca57 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -1,5 +1,8 @@ /* -*- mode: c++; c-basic-offset: 4 -*- */ +#include "ft2font.h" +#include "mplutils.h" + #include #include #include @@ -9,9 +12,6 @@ #include #include -#include "ft2font.h" -#include "mplutils.h" - #ifndef M_PI #define M_PI 3.14159265358979323846264338328 #endif diff --git a/src/ft2font.h b/src/ft2font.h index 8db0239ed4fd..6676a7dd4818 100644 --- a/src/ft2font.h +++ b/src/ft2font.h @@ -6,6 +6,9 @@ #ifndef MPL_FT2FONT_H #define MPL_FT2FONT_H +#include +#include + #include #include #include @@ -22,8 +25,6 @@ extern "C" { #include FT_TRUETYPE_TABLES_H } -#include -#include namespace py = pybind11; // By definition, FT_FIXED as 2 16bit values stored in a single long.