From 3598222d904c92e9d184181d3f736be9d3a23922 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Wed, 1 Nov 2023 23:18:57 +0100 Subject: [PATCH 01/87] Backport PR #26703: moved communications guidelines from governance, updated and clarified process --- doc/_static/mpl.css | 19 +++ doc/devel/coding_guide.rst | 9 -- doc/devel/communication_guide.rst | 223 ++++++++++++++++++++++++++ doc/devel/index.rst | 1 + doc/devel/release_guide.rst | 25 ++- doc/users/project/code_of_conduct.rst | 2 +- doc/users/release_notes.rst | 1 + 7 files changed, 257 insertions(+), 23 deletions(-) create mode 100644 doc/devel/communication_guide.rst diff --git a/doc/_static/mpl.css b/doc/_static/mpl.css index 45ecb21d5511..a37fb76a0436 100644 --- a/doc/_static/mpl.css +++ b/doc/_static/mpl.css @@ -161,3 +161,22 @@ div.wide-table table th.stub { font-style: italic; font-size: large; } + + +.checklist { + list-style: none; + padding: 0; + margin: 0; +} +.checklist li { + margin-left: 24px; + padding-left: 23px; + margin-right: 6px; +} +.checklist li:before { + content: "\2610\2001"; + margin-left: -24px; +} +.checklist li p { + display: inline; +} diff --git a/doc/devel/coding_guide.rst b/doc/devel/coding_guide.rst index 49f35e3ad13e..0385d6d388f0 100644 --- a/doc/devel/coding_guide.rst +++ b/doc/devel/coding_guide.rst @@ -1,12 +1,3 @@ -.. raw:: html - - - .. _pr-guidelines: *********************** diff --git a/doc/devel/communication_guide.rst b/doc/devel/communication_guide.rst new file mode 100644 index 000000000000..b9fa301c0e46 --- /dev/null +++ b/doc/devel/communication_guide.rst @@ -0,0 +1,223 @@ +.. _communications-guidelines: + +======================== +Communication guidelines +======================== + +These guidelines are applicable when acting as a representative of Matplotlib, +for example at sprints or when giving official talks or tutorials, and in any +community venue managed by Matplotlib. + + +.. _communication-channels: + +Official communication channels +=============================== +The following venues are managed by Matplotlib maintainers and contributors: + +* library and docs: https://github.com/matplotlib/matplotlib +* forum: https://discourse.matplotlib.org/ +* chat: `https://matrix.to/#/#matplotlib:matrix.org `_ +* blog: https://blog.scientific-python.org/ + +.. _social-media: + +Social media +------------ + +Active social media +^^^^^^^^^^^^^^^^^^^ + +* https://twitter.com/matplotlib +* https://instagram.com/matplotart/ + +Official accounts +^^^^^^^^^^^^^^^^^ +* https://bsky.app/profile/matplotlib.bsky.social +* https://fosstodon.org/@matplotlib +* https://www.tiktok.com/@matplotart +* https://www.youtube.com/matplotlib + + +.. _mailing-lists: + +Mailing lists +------------- + +* `matplotlib-announce@python.org `_ +* `matplotlib-users@python.org `_ +* `matplotlib-devel@python.org `_ + +.. _social-media-coordination: + +Social media coordination +------------------------- +* Team mailing list: matplotlib-social@numfocus.org +* Public chat room: `https://matrix.to/#/#matplotlib_community:gitter.im `_ + + +Maintenance +----------- + +If you are interested in moderating the chat or forum or accessing the social +media accounts: + +* Matplotlib maintainers should reach out to the `community-manager`_. + +* Everyone else should send an email to matplotlib-social-admin@numfocus.org: + + * Introduce yourself - github handle and participation in the community. + * Describe the reason for wanting to moderate or contribute to social. + + +Content guidelines +================== + +Communication on official channels, such as the Matplotlib homepage or on +Matplotlib social accounts, should conform to the following standards. If you +are unsure if content that you would like to post or share meets these +guidelines, ask on the :ref:`social-media-coordination` channels before posting. + +General guidelines +------------------ + +* Focus on Matplotlib, 3rd party packages, and visualizations made with Matplotlib. +* These are also acceptable topics: + + * Visualization best practices and libraries. + * Projects and initiatives by NumFOCUS and Scientific Python. + * How to contribute to open source projects. + * Projects, such as scientific papers, that use Matplotlib. + +* No gratuitous disparaging of other visualization libraries and tools, but + criticism is acceptable so long as it serves a constructive purpose. + +* Follow communication best practices: + + * Do not share non-expert visualizations when it could cause harm: + + * e.g. https://twitter.com/matplotlib/status/1244178154618605568 + + * Clearly state when the visualization data/conclusions cannot be verified. + * Do not rely on machine translations for sensitive visualization. + +* Verify sourcing of content (especially on instagram & blog): + + * Instagram/blog: ensure mpl has right to repost/share content + * Make sure content is clearly cited: + + * e.g. a tutorial reworking an example must credit the original example + +* Limited self/corporate promotion is acceptable. + + * Should be no more than about a quarter of the content. + +Visual media guidelines +----------------------- + +Visual media, such as images and videos, must not violate the +:ref:`code of conduct `, nor any platform's rules. +Specifically: + +* Visual media must conform to the guidelines of all sites it may be posted on: + + * https://help.twitter.com/en/rules-and-policies/twitter-rules + * https://help.instagram.com/477434105621119 + +* Emphasize the visualization techniques demonstrated by the visual media. +* Clearly state that sharing is not an endorsement of the content. + + * e.g. bitcoin related visualizations + +Accessibility +^^^^^^^^^^^^^ + +Visual media in communications should be made as accessible as possible: + +* Add alt text to images and videos when the platform allows: + + * `alt text for data viz `_ + * `general alt text guide `_ + +* Warn on bright, strobing, images & turn off autoplay if possible. +* For images and videos made by the social media team: + + * Make graphic perceivable to people who cannot perceive color well due to + color-blindness, low vision, or any other reason. + + * Do not make bright, strobing images. + * More guidelines at https://webaim.org/techniques/images/. + + +Social media +============ + +Please follow these guidelines to maintain a consistent brand identity across +platforms. + +Persona +------- +On social media, Matplotlib: + +* Acts as a sentient visualization library, so talks about itself as a we, us, + our, and it. Avoids talking about itself in the 3rd person. Never uses 1st person. +* Is very earnest, eager to please, and aims to be patient & painfully oblivious + to snark and sarcasm. +* Gets over-excited over shiny visualizations - lots of emojis and the like - + and encourages folks to share their work. +* Highlights various parts of the library, especially the more obscure bits and + bobbles. +* Acknowledges that it is a sometimes frustrating tangle of bits & bobbles that + can confuse even the folks who work on it & signal boosts their confuzzlment. + + +Behavior +-------- +When acting as a representative of the library, keep responses polite and assume +user statements are in good faith unless they violate the :ref:`code of conduct `. + +Social graph +------------ + +Only follow **organizations and projects**, do not follow individual accounts for +any reason, even maintainers/project leads/famous Python people! + +Following these types of accounts is encouraged: + +* NumFocus and Scientific Python projects +* 3rd party packages +* Visualization related projects and organizations +* Open Source community projects +* Sponsors + +Recurring campaigns +------------------- + +Typically the social media accounts will promote the following: + +* Matplotlib releases: + + * Highlight new features & major deprecations + * Link to download/install instructions + * Ask folks to try it out. + +* `third party packages `_ +* NumFocus/Scientific Python/open source visualization project releases +* GSOC/GSOD recruiting and progress + +Retired campaigns +^^^^^^^^^^^^^^^^^ +* John Hunter Excellence in Plotting, submission and winners + + +Changing the guidelines +======================= + +As the person tasked with implementing communications, the `community-manager`_ +should be alerted to proposed changes to the communications guidelines. Similarly, +specific platform guidelines (e.g. twitter, instagram) should be reviewed by the +person responsible for that platform, when different from the community manager. +If there is no consensus, decisions about communications guidelines revert to +the community manager. + +.. _community-manager: https://matplotlib.org/governance/people.html#deputy-project-leads diff --git a/doc/devel/index.rst b/doc/devel/index.rst index 2b358595255b..9537859c107a 100644 --- a/doc/devel/index.rst +++ b/doc/devel/index.rst @@ -167,6 +167,7 @@ Policies and guidelines :maxdepth: 1 release_guide + communication_guide min_dep_policy MEP/index diff --git a/doc/devel/release_guide.rst b/doc/devel/release_guide.rst index 2bbe589282a3..4adc4546e879 100644 --- a/doc/devel/release_guide.rst +++ b/doc/devel/release_guide.rst @@ -455,22 +455,21 @@ Due to branch protections for the ``main`` branch, this is merged via a standard request, though the PR cleanliness status check is expected to fail. The PR should not be squashed because the intent is to merge the branch histories. -Announcing -========== +Publicize this release +====================== -The final step is to announce the release to the world. A short -version of the release notes along with acknowledgments should be sent to +After the release is published to PyPI and conda, it should be announced +through our communication channels: -- matplotlib-users@python.org -- matplotlib-devel@python.org -- matplotlib-announce@python.org - -In addition, announcements should be made on social networks (e.g., Twitter via the -``@matplotlib`` account, any other via personal accounts). - -Add a release announcement to the ``mpl-brochure-site`` "News" section of -``docs/body.html``, linking to the discourse page for the announcement. +.. rst-class:: checklist +* Send a short version of the release notes and acknowledgments to all the :ref:`mailing-lists` +* Post highlights and link to :ref:`What's new ` on the + active :ref:`social media accounts ` +* Add a release announcement to the "News" section of + `matplotlib.org `_ by editing + ``docs/body.html``. Link to the auto-generated announcement discourse post, + which is in `Announcements > matplotlib-announcements `_. Conda packages ============== diff --git a/doc/users/project/code_of_conduct.rst b/doc/users/project/code_of_conduct.rst index 8deda909bd4c..ac8b268a4e30 100644 --- a/doc/users/project/code_of_conduct.rst +++ b/doc/users/project/code_of_conduct.rst @@ -1,4 +1,4 @@ -.. code_of_conduct +.. _code_of_conduct: ==================================== Contributor Covenant Code of Conduct diff --git a/doc/users/release_notes.rst b/doc/users/release_notes.rst index 12cb60d2fb0d..ca09d218cae4 100644 --- a/doc/users/release_notes.rst +++ b/doc/users/release_notes.rst @@ -1,6 +1,7 @@ .. redirect-from:: /api/api_changes_old .. redirect-from:: /users/whats_new_old +.. _release-notes: ============= Release notes From c80aaa1312ff2acad0d5f371df3fc184595dd241 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Thu, 2 Nov 2023 23:42:05 +0100 Subject: [PATCH 02/87] Backport PR #27249: DOC: reasoning for communications guidelines --- doc/devel/communication_guide.rst | 66 +++++++++++++++++++++++++------ doc/users/project/mission.rst | 2 + 2 files changed, 56 insertions(+), 12 deletions(-) diff --git a/doc/devel/communication_guide.rst b/doc/devel/communication_guide.rst index b9fa301c0e46..aed52be84e32 100644 --- a/doc/devel/communication_guide.rst +++ b/doc/devel/communication_guide.rst @@ -1,18 +1,41 @@ .. _communications-guidelines: -======================== -Communication guidelines -======================== +========================== +Community management guide +========================== -These guidelines are applicable when acting as a representative of Matplotlib, +These guidelines are applicable when **acting as a representative** of Matplotlib, for example at sprints or when giving official talks or tutorials, and in any community venue managed by Matplotlib. +Our approach to community engagement is foremost guided by our :ref:`mission-statement`: + +* We demonstrate that we care about visualization as a practice +* We deepen our practice and the community’s capacity to support users, + facilitate exploration, produce high quality visualizations, and be + understandable and extensible +* We showcase advanced use of the library without adding maintenance burden to + the documentation and recognize contributions that happen outside of the github + workflow. +* We use communications platforms to maintain relationships with contributors + who may no longer be active on GitHub, build relationships with potential + contributors, and connect with other projects and communities who use + Matplotlib. +* In prioritizing understandability and extensiblity, we recognize that people + using Matplotlib, in whatever capacity, are part of our community. Doing so + empowers our community members to build community with each other, for example + by creating educational resources, building third party tools, and building + informal mentoring networks. .. _communication-channels: Official communication channels =============================== +The Scientific Python community uses various communications platforms to stay +updated on new features and projects, to contribute by telling us what is on +their mind and suggest issues and bugs, and to showcase their use cases and the +tools they have built. + The following venues are managed by Matplotlib maintainers and contributors: * library and docs: https://github.com/matplotlib/matplotlib @@ -148,12 +171,32 @@ Visual media in communications should be made as accessible as possible: * Do not make bright, strobing images. * More guidelines at https://webaim.org/techniques/images/. +.. _social-media-brand: Social media ============ -Please follow these guidelines to maintain a consistent brand identity across -platforms. +Matplotlib aims for a single voice across all social media platforms to build and +maintain a consistent brand identity for Matplotlib as an organization. This +depersonalization is the norm on social media platforms because it enables +constructive and productive conversations; People generally feel more comfortable +giving negative and constructive feedback to a brand than to specific contributors. + +The current Matplotlib voice and persona aims to be kind, patient, supportive and +educational. This is so that it can de-escalate tensions and facilitate +constructive conversations; being perceived as negative or +argumentative can escalate very fast into long-lasting brand damage, being +perceived as personal leads to aggression and accusations faster than an +impersonal account, and being perceived as friendly and approachable leads to +higher engagement. Instead of speaking with a directive authority, which can be +intimidating and lead to negative engagement, it speaks as a peer or educator to +empower participation. The current voice encourages more input from folks we +engage with, and also makes it possible for folks who are not in the core team +to participate in managing the account. + +While the :ref:`brand identity ` is casual, the showcased +content is high quality, peer-led resource building. Please follow these +guidelines to maintain a consistent brand identity across platforms. Persona ------- @@ -213,11 +256,10 @@ Retired campaigns Changing the guidelines ======================= -As the person tasked with implementing communications, the `community-manager`_ -should be alerted to proposed changes to the communications guidelines. Similarly, -specific platform guidelines (e.g. twitter, instagram) should be reviewed by the -person responsible for that platform, when different from the community manager. -If there is no consensus, decisions about communications guidelines revert to -the community manager. +As the person tasked with implementing these guidelines, the `community-manager`_ +should be alerted to proposed changes. Similarly, specific platform guidelines +(e.g. twitter, instagram) should be reviewed by the person responsible for that +platform, when different from the community manager. If there is no consensus, +decisions about guidelines revert to the community manager. .. _community-manager: https://matplotlib.org/governance/people.html#deputy-project-leads diff --git a/doc/users/project/mission.rst b/doc/users/project/mission.rst index 431da04e4891..438d2fce8b1d 100644 --- a/doc/users/project/mission.rst +++ b/doc/users/project/mission.rst @@ -1,3 +1,5 @@ +.. _mission-statement: + Mission Statement ================= From af7343c67cc93f0e1028d49e487a03c206d087fe Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Thu, 2 Nov 2023 23:42:05 +0100 Subject: [PATCH 03/87] Backport PR #27249: DOC: reasoning for communications guidelines --- doc/devel/communication_guide.rst | 66 +++++++++++++++++++++++++------ doc/users/project/mission.rst | 2 + 2 files changed, 56 insertions(+), 12 deletions(-) diff --git a/doc/devel/communication_guide.rst b/doc/devel/communication_guide.rst index b9fa301c0e46..aed52be84e32 100644 --- a/doc/devel/communication_guide.rst +++ b/doc/devel/communication_guide.rst @@ -1,18 +1,41 @@ .. _communications-guidelines: -======================== -Communication guidelines -======================== +========================== +Community management guide +========================== -These guidelines are applicable when acting as a representative of Matplotlib, +These guidelines are applicable when **acting as a representative** of Matplotlib, for example at sprints or when giving official talks or tutorials, and in any community venue managed by Matplotlib. +Our approach to community engagement is foremost guided by our :ref:`mission-statement`: + +* We demonstrate that we care about visualization as a practice +* We deepen our practice and the community’s capacity to support users, + facilitate exploration, produce high quality visualizations, and be + understandable and extensible +* We showcase advanced use of the library without adding maintenance burden to + the documentation and recognize contributions that happen outside of the github + workflow. +* We use communications platforms to maintain relationships with contributors + who may no longer be active on GitHub, build relationships with potential + contributors, and connect with other projects and communities who use + Matplotlib. +* In prioritizing understandability and extensiblity, we recognize that people + using Matplotlib, in whatever capacity, are part of our community. Doing so + empowers our community members to build community with each other, for example + by creating educational resources, building third party tools, and building + informal mentoring networks. .. _communication-channels: Official communication channels =============================== +The Scientific Python community uses various communications platforms to stay +updated on new features and projects, to contribute by telling us what is on +their mind and suggest issues and bugs, and to showcase their use cases and the +tools they have built. + The following venues are managed by Matplotlib maintainers and contributors: * library and docs: https://github.com/matplotlib/matplotlib @@ -148,12 +171,32 @@ Visual media in communications should be made as accessible as possible: * Do not make bright, strobing images. * More guidelines at https://webaim.org/techniques/images/. +.. _social-media-brand: Social media ============ -Please follow these guidelines to maintain a consistent brand identity across -platforms. +Matplotlib aims for a single voice across all social media platforms to build and +maintain a consistent brand identity for Matplotlib as an organization. This +depersonalization is the norm on social media platforms because it enables +constructive and productive conversations; People generally feel more comfortable +giving negative and constructive feedback to a brand than to specific contributors. + +The current Matplotlib voice and persona aims to be kind, patient, supportive and +educational. This is so that it can de-escalate tensions and facilitate +constructive conversations; being perceived as negative or +argumentative can escalate very fast into long-lasting brand damage, being +perceived as personal leads to aggression and accusations faster than an +impersonal account, and being perceived as friendly and approachable leads to +higher engagement. Instead of speaking with a directive authority, which can be +intimidating and lead to negative engagement, it speaks as a peer or educator to +empower participation. The current voice encourages more input from folks we +engage with, and also makes it possible for folks who are not in the core team +to participate in managing the account. + +While the :ref:`brand identity ` is casual, the showcased +content is high quality, peer-led resource building. Please follow these +guidelines to maintain a consistent brand identity across platforms. Persona ------- @@ -213,11 +256,10 @@ Retired campaigns Changing the guidelines ======================= -As the person tasked with implementing communications, the `community-manager`_ -should be alerted to proposed changes to the communications guidelines. Similarly, -specific platform guidelines (e.g. twitter, instagram) should be reviewed by the -person responsible for that platform, when different from the community manager. -If there is no consensus, decisions about communications guidelines revert to -the community manager. +As the person tasked with implementing these guidelines, the `community-manager`_ +should be alerted to proposed changes. Similarly, specific platform guidelines +(e.g. twitter, instagram) should be reviewed by the person responsible for that +platform, when different from the community manager. If there is no consensus, +decisions about guidelines revert to the community manager. .. _community-manager: https://matplotlib.org/governance/people.html#deputy-project-leads diff --git a/doc/users/project/mission.rst b/doc/users/project/mission.rst index 431da04e4891..438d2fce8b1d 100644 --- a/doc/users/project/mission.rst +++ b/doc/users/project/mission.rst @@ -1,3 +1,5 @@ +.. _mission-statement: + Mission Statement ================= From a614491070d443801c3e9a2a221ce85117bb667f Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Fri, 3 Nov 2023 08:21:41 +0100 Subject: [PATCH 04/87] Backport PR #27213: DOC: consolidated coding guide and added naming conventions table --- doc/api/next_api_changes/README.rst | 12 +- doc/devel/coding_guide.rst | 214 ++++++------------------- doc/devel/contribute.rst | 237 ++++++++++++++++++++++++---- doc/devel/development_setup.rst | 2 + doc/devel/development_workflow.rst | 17 ++ doc/users/next_whats_new/README.rst | 12 +- 6 files changed, 292 insertions(+), 202 deletions(-) diff --git a/doc/api/next_api_changes/README.rst b/doc/api/next_api_changes/README.rst index de494911e6b2..75e70b456eb9 100644 --- a/doc/api/next_api_changes/README.rst +++ b/doc/api/next_api_changes/README.rst @@ -22,11 +22,17 @@ author's initials. Typically, each change will get its own file, but you may also amend existing files when suitable. The overall goal is a comprehensible documentation of the changes. -Please avoid using references in section titles, as it causes links to be -confusing in the table of contents. Instead, ensure that a reference is -included in the descriptive text. A typical entry could look like this:: +A typical entry could look like this:: Locators ~~~~~~~~ The unused `Locator.autoscale()` method is deprecated (pass the axis limits to `Locator.view_limits()` instead). + +Please avoid using references in section titles, as it causes links to be +confusing in the table of contents. Instead, ensure that a reference is +included in the descriptive text. + +.. NOTE + Lines 5-30 of this file are include in :ref:`api_whats_new`; + therefore, please check the doc build after changing this file. diff --git a/doc/devel/coding_guide.rst b/doc/devel/coding_guide.rst index 0385d6d388f0..22873020f103 100644 --- a/doc/devel/coding_guide.rst +++ b/doc/devel/coding_guide.rst @@ -8,144 +8,82 @@ Pull request guidelines `__ are the mechanism for contributing to Matplotlib's code and documentation. -It is recommended to check that your contribution complies with the following -rules before submitting a pull request: +We value contributions from people with all levels of experience. In particular, +if this is your first PR not everything has to be perfect. We'll guide you +through the PR process. Nevertheless, please try to follow our guidelines as well +as you can to help make the PR process quick and smooth. If your pull request is +incomplete or a work-in-progress, please mark it as a `draft pull requests `_ +on GitHub and specify what feedback from the developers would be helpful. -* If your pull request addresses an issue, please use the title to describe the - issue (e.g. "Add ability to plot timedeltas") and mention the issue number - in the pull request description to ensure that a link is created to the - original issue (e.g. "Closes #8869" or "Fixes #8869"). This will ensure the - original issue mentioned is automatically closed when your PR is merged. See - `the GitHub documentation - `__ - for more details. - -* Formatting should follow the recommendations of PEP8_, as enforced by - flake8_. Matplotlib modifies PEP8 to extend the maximum line length to 88 - characters. You can check flake8 compliance from the command line with :: - - python -m pip install flake8 - flake8 /path/to/module.py - - or your editor may provide integration with it. Note that Matplotlib - intentionally does not use the black_ auto-formatter (1__), in particular due - to its inability to understand the semantics of mathematical expressions - (2__, 3__). - - .. _PEP8: https://www.python.org/dev/peps/pep-0008/ - .. _flake8: https://flake8.pycqa.org/ - .. _black: https://black.readthedocs.io/ - .. __: https://github.com/matplotlib/matplotlib/issues/18796 - .. __: https://github.com/psf/black/issues/148 - .. __: https://github.com/psf/black/issues/1984 - -* All public methods should have informative docstrings with sample usage when - appropriate. Use the :ref:`docstring standards `. - -* For high-level plotting functions, consider adding a simple example either in - the ``Example`` section of the docstring or the - :ref:`examples gallery `. - -* Changes (both new features and bugfixes) should have good test coverage. See - :ref:`testing` for more details. - -* Import the following modules using the standard scipy conventions:: +Please be patient with reviewers. We try our best to respond quickly, but we have +limited bandwidth. If there is no feedback within a couple of days, please ping +us by posting a comment to your PR or reaching out on a :ref:`communication channel ` - import numpy as np - import numpy.ma as ma - import matplotlib as mpl - import matplotlib.pyplot as plt - import matplotlib.cbook as cbook - import matplotlib.patches as mpatches - In general, Matplotlib modules should **not** import `.rcParams` using ``from - matplotlib import rcParams``, but rather access it as ``mpl.rcParams``. This - is because some modules are imported very early, before the `.rcParams` - singleton is constructed. - -* If your change is a major new feature, add an entry to the ``What's new`` - section by adding a new file in ``doc/users/next_whats_new`` (see - :file:`doc/users/next_whats_new/README.rst` for more information). +Summary for pull request authors +================================ -* If you change the API in a backward-incompatible way, please document it in - :file:`doc/api/next_api_changes/behavior`, by adding a new file with the - naming convention ``99999-ABC.rst`` where the pull request number is followed - by the contributor's initials. (see :file:`doc/api/api_changes.rst` for more - information) +We recommend that you check that your contribution complies with the following +guidelines before submitting a pull request: -* If you add new public API or change public API, update or add the - corresponding type hints. Most often this is found in the corresponding - ``.pyi`` file for the ``.py`` file which was edited. Changes in ``pyplot.py`` - are type hinted inline. +.. rst-class:: checklist -* See below for additional points about :ref:`keyword-argument-processing`, if - applicable for your pull request. +* Changes, both new features and bugfixes, should have good test coverage. See + :ref:`testing` for more details. -.. note:: +* Update the :ref:`documentation ` if necessary. - The current state of the Matplotlib code base is not compliant with all - of these guidelines, but we expect that enforcing these constraints on all - new contributions will move the overall code base quality in the right - direction. +* All public methods should have informative docstrings with sample usage when + appropriate. Use the :ref:`docstring standards `. +* For high-level plotting functions, consider adding a small example to the + :ref:`examples gallery `. -.. seealso:: +* If you add a major new feature or change the API in a backward-incompatible + way, please document it as described in :ref:`new-changed-api` - * :ref:`coding_guidelines` - * :ref:`testing` - * :ref:`documenting-matplotlib` +* Code should follow our conventions as documented in our :ref:`coding_guidelines` +* When adding or changing public function signatures, add :ref:`type hints ` +* When adding keyword arguments, see our guide to :ref:`keyword-argument-processing`. -Summary for pull request authors -================================ +When opening a pull request on Github, please ensure that: -.. note:: +.. rst-class:: checklist - * We value contributions from people with all levels of experience. In - particular if this is your first PR not everything has to be perfect. - We'll guide you through the PR process. - * Nevertheless, please try to follow the guidelines below as well as you can to - help make the PR process quick and smooth. - * Be patient with reviewers. We try our best to respond quickly, but we - have limited bandwidth. If there is no feedback within a couple of days, - please ping us by posting a comment to your PR. +* Changes were made on a :ref:`feature branch `. -When making a PR, pay attention to: +* :ref:`pre-commit ` checks for spelling, formatting, etc pass -.. rst-class:: checklist +* The pull request targets the :ref:`main branch ` -* :ref:`Target the main branch `. -* Adhere to the :ref:`coding_guidelines`. -* Update the :ref:`documentation ` if necessary. -* Aim at making the PR as "ready-to-go" as you can. This helps to speed up - the review process. -* It is ok to open incomplete or work-in-progress PRs if you need help or - feedback from the developers. You may mark these as - `draft pull requests `_ - on GitHub. -* When updating your PR, instead of adding new commits to fix something, please - consider amending your initial commit(s) to keep the history clean. - You can achieve this by using +* If your pull request addresses an issue, please use the title to describe the + issue (e.g. "Add ability to plot timedeltas") and mention the issue number + in the pull request description to ensure that a link is created to the + original issue (e.g. "Closes #8869" or "Fixes #8869"). This will ensure the + original issue mentioned is automatically closed when your PR is merged. For more + details, see `linking an issue and pull request `__. - .. code-block:: bash +* :ref:`pr-automated-tests` pass - git commit --amend --no-edit - git push [your-remote-repo] [your-branch] --force-with-lease +For guidance on creating and managing a pull request, please see our +:ref:`contributing ` and :ref:`pull request workflow ` +guides. -See also :ref:`contributing` for how to make a PR. Summary for pull request reviewers ================================== .. redirect-from:: /devel/maintainer_workflow -.. note:: +**Please help review and merge PRs!** + +If you have commit rights, then you are trusted to use them. Please be patient +and `kind `__ with contributors. - * If you have commit rights, then you are trusted to use them. - **Please help review and merge PRs!** - * Be patient and `kind `__ with - contributors. +When reviewing, please ensure that the pull request satisfies the following +requirements before merging it: Content topics: @@ -196,61 +134,6 @@ Documentation * See :ref:`documenting-matplotlib` for our documentation style guide. -.. _release_notes: - -New features and API changes -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -When adding a major new feature or changing the API in a backward incompatible -way, please document it by including a versioning directive in the docstring -and adding an entry to the folder for either the what's new or API change notes. - -+-------------------+-----------------------------+----------------------------------+ -| for this addition | include this directive | create entry in this folder | -+===================+=============================+==================================+ -| new feature | ``.. versionadded:: 3.N`` | :file:`doc/users/next_whats_new/`| -+-------------------+-----------------------------+----------------------------------+ -| API change | ``.. versionchanged:: 3.N`` | :file:`doc/api/next_api_changes/`| -| | | | -| | | probably in ``behavior/`` | -+-------------------+-----------------------------+----------------------------------+ - -The directives should be placed at the end of a description block. For example:: - - class Foo: - """ - This is the summary. - - Followed by a longer description block. - - Consisting of multiple lines and paragraphs. - - .. versionadded:: 3.5 - - Parameters - ---------- - a : int - The first parameter. - b: bool, default: False - This was added later. - - .. versionadded:: 3.6 - """ - - def set_b(b): - """ - Set b. - - .. versionadded:: 3.6 - - Parameters - ---------- - b: bool - -For classes and functions, the directive should be placed before the -*Parameters* section. For parameters, the directive should be placed at the -end of the parameter description. The patch release version is omitted and -the directive should not be added to entire modules. - .. _pr-labels: Labels @@ -330,7 +213,8 @@ Merging Automated tests --------------- -Before being merged, a PR should pass the :ref:`automated-tests`. If you are unsure why a test is failing, ask on the PR or in our `chat space `_ +Before being merged, a PR should pass the :ref:`automated-tests`. If you are +unsure why a test is failing, ask on the PR or in our :ref:`communication-channels` .. _pr-squashing: diff --git a/doc/devel/contribute.rst b/doc/devel/contribute.rst index a9bfb0f816dd..ee78beca0781 100644 --- a/doc/devel/contribute.rst +++ b/doc/devel/contribute.rst @@ -367,27 +367,160 @@ project that leads to a scientific publication, please follow the Coding guidelines ================= -API changes ------------ +While the current state of the Matplotlib code base is not compliant with all +of these guidelines, our goal in enforcing these constraints on new +contributions is that it improves the readability and consistency of the code base +going forward. -API consistency and stability are of great value. Therefore, API changes -(e.g. signature changes, behavior changes, removals) will only be conducted -if the added benefit is worth the user effort for adapting. +PEP8, as enforced by flake8 +--------------------------- + +Formatting should follow the recommendations of PEP8_, as enforced by flake8_. +Matplotlib modifies PEP8 to extend the maximum line length to 88 +characters. You can check flake8 compliance from the command line with :: + + python -m pip install flake8 + flake8 /path/to/module.py + +or your editor may provide integration with it. Note that Matplotlib intentionally +does not use the black_ auto-formatter (1__), in particular due to its inability +to understand the semantics of mathematical expressions (2__, 3__). + +.. _PEP8: https://www.python.org/dev/peps/pep-0008/ +.. _flake8: https://flake8.pycqa.org/ +.. _black: https://black.readthedocs.io/ +.. __: https://github.com/matplotlib/matplotlib/issues/18796 +.. __: https://github.com/psf/black/issues/148 +.. __: https://github.com/psf/black/issues/1984 + + +Package imports +--------------- +Import the following modules using the standard scipy conventions:: + + import numpy as np + import numpy.ma as ma + import matplotlib as mpl + import matplotlib.pyplot as plt + import matplotlib.cbook as cbook + import matplotlib.patches as mpatches + +In general, Matplotlib modules should **not** import `.rcParams` using ``from +matplotlib import rcParams``, but rather access it as ``mpl.rcParams``. This +is because some modules are imported very early, before the `.rcParams` +singleton is constructed. + +Variable names +-------------- + +When feasible, please use our internal variable naming convention for objects +of a given class and objects of any child class: + ++------------------------------------+---------------+------------------------------------------+ +| base class | variable | multiples | ++====================================+===============+==========================================+ +| `~matplotlib.figure.FigureBase` | ``fig`` | | ++------------------------------------+---------------+------------------------------------------+ +| `~matplotlib.axes.Axes` | ``ax`` | | ++------------------------------------+---------------+------------------------------------------+ +| `~matplotlib.transforms.Transform` | ``trans`` | ``trans__`` | ++ + + + +| | | ``trans_`` when target is screen | ++------------------------------------+---------------+------------------------------------------+ + +Generally, denote more than one instance of the same class by adding suffixes to +the variable names. If a format isn't specified in the table, use numbers or +letters as appropriate. + + +.. _type-hints: + +Type hints +---------- + +If you add new public API or change public API, update or add the +corresponding `mypy `_ type hints. +We generally use `stub files +`_ +(``*.pyi``) to store the type information; for example ``colors.pyi`` contains +the type information for ``colors.py``. A notable exception is ``pyplot.py``, +which is type hinted inline. + +Type hints are checked by the mypy :ref:`pre-commit hook ` +and can often be verified using ``tools\stubtest.py`` and occasionally may +require the use of ``tools\check_typehints.py``. + + +.. _new-changed-api: -Because we are a visualization library our primary output is the final -visualization the user sees. Thus it is our :ref:`long standing -` policy that the appearance of the figure is part of the API -and any changes, either semantic or esthetic, will be treated as a -backwards-incompatible API change. +API changes and new features +---------------------------- +API consistency and stability are of great value; Therefore, API changes +(e.g. signature changes, behavior changes, removals) will only be conducted +if the added benefit is worth the effort of adapting existing code. + +Because we are a visualization library, our primary output is the final +visualization the user sees; therefore, the appearance of the figure is part of +the API and any changes, either semantic or :ref:`esthetic `, +are backwards-incompatible API changes. + +.. _api_whats_new: + +Announce changes, deprecations, and new features +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When adding or changing the API in a backward in-compatible way, please add the +appropriate :ref:`versioning directive ` and document it +for the release notes and add the entry to the appropriate folder: + ++-------------------+-----------------------------+----------------------------------------------+ +| addition | versioning directive | announcement folder | ++===================+=============================+==============================================+ +| new feature | ``.. versionadded:: 3.N`` | :file:`doc/users/next_whats_new/` | ++-------------------+-----------------------------+----------------------------------------------+ +| API change | ``.. versionchanged:: 3.N`` | :file:`doc/api/next_api_changes/[kind]` | ++-------------------+-----------------------------+----------------------------------------------+ + +API deprecations are first introduced and then expired. During the introduction +period, users are warned that the API *will* change in the future. +During the expiration period, code is changed as described in the notice posted +during the introductory period. + ++-----------+--------------------------------------------------+----------------------------------------------+ +| stage | required changes | announcement folder | ++===========+==================================================+==============================================+ +| introduce | :ref:`introduce deprecation ` | :file:`doc/api/next_api_changes/deprecation` | ++-----------+--------------------------------------------------+----------------------------------------------+ +| expire | :ref:`expire deprecation ` | :file:`doc/api/next_api_changes/[kind]` | ++-----------+--------------------------------------------------+----------------------------------------------+ + +For both change notes and what's new, please avoid using references in section +titles, as it causes links to be confusing in the table of contents. Instead, +ensure that a reference is included in the descriptive text. + +API Change Notes +"""""""""""""""" +.. include:: ../api/next_api_changes/README.rst + :start-line: 5 + :end-line: 31 + +What's new +"""""""""" +.. include:: ../users/next_whats_new/README.rst + :start-line: 5 + :end-line: 24 + + +Deprecation +^^^^^^^^^^^ API changes in Matplotlib have to be performed following the deprecation process -below, except in very rare circumstances as deemed necessary by the development team. -This ensures that users are notified before the change will take effect and thus -prevents unexpected breaking of code. +below, except in very rare circumstances as deemed necessary by the development +team. This ensures that users are notified before the change will take effect +and thus prevents unexpected breaking of code. Rules -^^^^^ - +""""" - Deprecations are targeted at the next point.release (e.g. 3.x) - Deprecated API is generally removed two point-releases after introduction of the deprecation. Longer deprecations can be imposed by core developers on @@ -398,12 +531,12 @@ Rules - If in doubt, decisions about API changes are finally made by the API consistency lead developer -Introducing -^^^^^^^^^^^ +.. _intro-deprecation: + +Introduce deprecation +""""""""""""""""""""" -#. Announce the deprecation in a new file - :file:`doc/api/next_api_changes/deprecations/99999-ABC.rst` where ``99999`` - is the pull request number and ``ABC`` are the contributor's initials. +#. Create :ref:`deprecation notice ` #. If possible, issue a `~matplotlib.MatplotlibDeprecationWarning` when the deprecated API is used. There are a number of helper tools for this: @@ -439,16 +572,13 @@ Introducing :file:`ci/mypy-stubtest-allowlist.txt` under a heading indicating the deprecation version number. -Expiring -^^^^^^^^ +.. _expire-deprecation: + +Expire deprecation +"""""""""""""""""" -#. Announce the API changes in a new file - :file:`doc/api/next_api_changes/[kind]/99999-ABC.rst` where ``99999`` - is the pull request number and ``ABC`` are the contributor's initials, and - ``[kind]`` is one of the folders :file:`behavior`, :file:`development`, - :file:`removals`. See :file:`doc/api/next_api_changes/README.rst` for more - information. For the content, you can usually copy the deprecation notice - and adapt it slightly. +#. Create :ref:`deprecation announcement `. For the content, + you can usually copy the deprecation notice and adapt it slightly. #. Change the code functionality and remove any related deprecation warnings. @@ -466,8 +596,8 @@ Expiring require that mechanism for deprecation. For removed items that were not in the stub file, only deleting from the allowlist is required. -Adding new API --------------- +Adding new API and features +^^^^^^^^^^^^^^^^^^^^^^^^^^^ Every new function, parameter and attribute that is not explicitly marked as private (i.e., starts with an underscore) becomes part of Matplotlib's public @@ -485,6 +615,51 @@ take particular care when adding new API: __ https://emptysqua.re/blog/api-evolution-the-right-way/#adding-parameters +.. _versioning-directives: + +Versioning directives +""""""""""""""""""""" + +When making a backward incompatible change, please add a versioning directive in +the docstring. The directives should be placed at the end of a description block. +For example:: + + class Foo: + """ + This is the summary. + + Followed by a longer description block. + + Consisting of multiple lines and paragraphs. + + .. versionadded:: 3.5 + + Parameters + ---------- + a : int + The first parameter. + b: bool, default: False + This was added later. + + .. versionadded:: 3.6 + """ + + def set_b(b): + """ + Set b. + + .. versionadded:: 3.6 + + Parameters + ---------- + b: bool + +For classes and functions, the directive should be placed before the +*Parameters* section. For parameters, the directive should be placed at the +end of the parameter description. The patch release version is omitted and +the directive should not be added to entire modules. + + New modules and files: installation ----------------------------------- diff --git a/doc/devel/development_setup.rst b/doc/devel/development_setup.rst index 9da1c5737a36..e18d85cebde0 100644 --- a/doc/devel/development_setup.rst +++ b/doc/devel/development_setup.rst @@ -195,6 +195,8 @@ so that when you make code or document related changes you are aware of the exis * Run test cases to verify installation :ref:`testing` * Verify documentation build :ref:`documenting-matplotlib` +.. _pre-commit-hooks: + Install pre-commit hooks ======================== `pre-commit `_ hooks save time in the review process by diff --git a/doc/devel/development_workflow.rst b/doc/devel/development_workflow.rst index 50170ee4ade3..3f4fe956e37e 100644 --- a/doc/devel/development_workflow.rst +++ b/doc/devel/development_workflow.rst @@ -151,6 +151,23 @@ If you don't think your request is ready to be merged, just say so in your pull request message and use the "Draft PR" feature of GitHub. This is a good way of getting some preliminary code review. +.. _update-pull-request: + +Update a pull request +===================== + +When updating your pull request after making revisions, instead of adding new +commits, please consider amending your initial commit(s) to keep the commit +history clean. + +You can achieve this by using + + .. code-block:: bash + + git commit -a --amend --no-edit + git push [your-remote-repo] [your-branch] --force-with-lease + + Manage commit history ===================== diff --git a/doc/users/next_whats_new/README.rst b/doc/users/next_whats_new/README.rst index e1b27ef97f1e..98b601ee32d8 100644 --- a/doc/users/next_whats_new/README.rst +++ b/doc/users/next_whats_new/README.rst @@ -11,9 +11,7 @@ something like :file:`cool_new_feature.rst` if you have added a brand new feature or something like :file:`updated_feature.rst` for extensions of existing features. -Please avoid using references in section titles, as it causes links to be -confusing in the table of contents. Instead, ensure that a reference is -included in the descriptive text. Include contents of the form: :: +Include contents of the form:: Section title for feature ------------------------- @@ -23,3 +21,11 @@ included in the descriptive text. Include contents of the form: :: A sub-section ~~~~~~~~~~~~~ + +Please avoid using references in section titles, as it causes links to be +confusing in the table of contents. Instead, ensure that a reference is +included in the descriptive text. + +.. NOTE + Lines 5-24 of this file are include in :ref:`api_whats_new`; + therefore, please check the doc build after changing this file. From ef107caf3951f164571e28237413aabe68a73c79 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Fri, 3 Nov 2023 08:21:41 +0100 Subject: [PATCH 05/87] Backport PR #27213: DOC: consolidated coding guide and added naming conventions table --- doc/api/next_api_changes/README.rst | 12 +- doc/devel/coding_guide.rst | 214 ++++++------------------- doc/devel/contribute.rst | 237 ++++++++++++++++++++++++---- doc/devel/development_setup.rst | 2 + doc/devel/development_workflow.rst | 17 ++ doc/users/next_whats_new/README.rst | 12 +- 6 files changed, 292 insertions(+), 202 deletions(-) diff --git a/doc/api/next_api_changes/README.rst b/doc/api/next_api_changes/README.rst index de494911e6b2..75e70b456eb9 100644 --- a/doc/api/next_api_changes/README.rst +++ b/doc/api/next_api_changes/README.rst @@ -22,11 +22,17 @@ author's initials. Typically, each change will get its own file, but you may also amend existing files when suitable. The overall goal is a comprehensible documentation of the changes. -Please avoid using references in section titles, as it causes links to be -confusing in the table of contents. Instead, ensure that a reference is -included in the descriptive text. A typical entry could look like this:: +A typical entry could look like this:: Locators ~~~~~~~~ The unused `Locator.autoscale()` method is deprecated (pass the axis limits to `Locator.view_limits()` instead). + +Please avoid using references in section titles, as it causes links to be +confusing in the table of contents. Instead, ensure that a reference is +included in the descriptive text. + +.. NOTE + Lines 5-30 of this file are include in :ref:`api_whats_new`; + therefore, please check the doc build after changing this file. diff --git a/doc/devel/coding_guide.rst b/doc/devel/coding_guide.rst index 0385d6d388f0..22873020f103 100644 --- a/doc/devel/coding_guide.rst +++ b/doc/devel/coding_guide.rst @@ -8,144 +8,82 @@ Pull request guidelines `__ are the mechanism for contributing to Matplotlib's code and documentation. -It is recommended to check that your contribution complies with the following -rules before submitting a pull request: +We value contributions from people with all levels of experience. In particular, +if this is your first PR not everything has to be perfect. We'll guide you +through the PR process. Nevertheless, please try to follow our guidelines as well +as you can to help make the PR process quick and smooth. If your pull request is +incomplete or a work-in-progress, please mark it as a `draft pull requests `_ +on GitHub and specify what feedback from the developers would be helpful. -* If your pull request addresses an issue, please use the title to describe the - issue (e.g. "Add ability to plot timedeltas") and mention the issue number - in the pull request description to ensure that a link is created to the - original issue (e.g. "Closes #8869" or "Fixes #8869"). This will ensure the - original issue mentioned is automatically closed when your PR is merged. See - `the GitHub documentation - `__ - for more details. - -* Formatting should follow the recommendations of PEP8_, as enforced by - flake8_. Matplotlib modifies PEP8 to extend the maximum line length to 88 - characters. You can check flake8 compliance from the command line with :: - - python -m pip install flake8 - flake8 /path/to/module.py - - or your editor may provide integration with it. Note that Matplotlib - intentionally does not use the black_ auto-formatter (1__), in particular due - to its inability to understand the semantics of mathematical expressions - (2__, 3__). - - .. _PEP8: https://www.python.org/dev/peps/pep-0008/ - .. _flake8: https://flake8.pycqa.org/ - .. _black: https://black.readthedocs.io/ - .. __: https://github.com/matplotlib/matplotlib/issues/18796 - .. __: https://github.com/psf/black/issues/148 - .. __: https://github.com/psf/black/issues/1984 - -* All public methods should have informative docstrings with sample usage when - appropriate. Use the :ref:`docstring standards `. - -* For high-level plotting functions, consider adding a simple example either in - the ``Example`` section of the docstring or the - :ref:`examples gallery `. - -* Changes (both new features and bugfixes) should have good test coverage. See - :ref:`testing` for more details. - -* Import the following modules using the standard scipy conventions:: +Please be patient with reviewers. We try our best to respond quickly, but we have +limited bandwidth. If there is no feedback within a couple of days, please ping +us by posting a comment to your PR or reaching out on a :ref:`communication channel ` - import numpy as np - import numpy.ma as ma - import matplotlib as mpl - import matplotlib.pyplot as plt - import matplotlib.cbook as cbook - import matplotlib.patches as mpatches - In general, Matplotlib modules should **not** import `.rcParams` using ``from - matplotlib import rcParams``, but rather access it as ``mpl.rcParams``. This - is because some modules are imported very early, before the `.rcParams` - singleton is constructed. - -* If your change is a major new feature, add an entry to the ``What's new`` - section by adding a new file in ``doc/users/next_whats_new`` (see - :file:`doc/users/next_whats_new/README.rst` for more information). +Summary for pull request authors +================================ -* If you change the API in a backward-incompatible way, please document it in - :file:`doc/api/next_api_changes/behavior`, by adding a new file with the - naming convention ``99999-ABC.rst`` where the pull request number is followed - by the contributor's initials. (see :file:`doc/api/api_changes.rst` for more - information) +We recommend that you check that your contribution complies with the following +guidelines before submitting a pull request: -* If you add new public API or change public API, update or add the - corresponding type hints. Most often this is found in the corresponding - ``.pyi`` file for the ``.py`` file which was edited. Changes in ``pyplot.py`` - are type hinted inline. +.. rst-class:: checklist -* See below for additional points about :ref:`keyword-argument-processing`, if - applicable for your pull request. +* Changes, both new features and bugfixes, should have good test coverage. See + :ref:`testing` for more details. -.. note:: +* Update the :ref:`documentation ` if necessary. - The current state of the Matplotlib code base is not compliant with all - of these guidelines, but we expect that enforcing these constraints on all - new contributions will move the overall code base quality in the right - direction. +* All public methods should have informative docstrings with sample usage when + appropriate. Use the :ref:`docstring standards `. +* For high-level plotting functions, consider adding a small example to the + :ref:`examples gallery `. -.. seealso:: +* If you add a major new feature or change the API in a backward-incompatible + way, please document it as described in :ref:`new-changed-api` - * :ref:`coding_guidelines` - * :ref:`testing` - * :ref:`documenting-matplotlib` +* Code should follow our conventions as documented in our :ref:`coding_guidelines` +* When adding or changing public function signatures, add :ref:`type hints ` +* When adding keyword arguments, see our guide to :ref:`keyword-argument-processing`. -Summary for pull request authors -================================ +When opening a pull request on Github, please ensure that: -.. note:: +.. rst-class:: checklist - * We value contributions from people with all levels of experience. In - particular if this is your first PR not everything has to be perfect. - We'll guide you through the PR process. - * Nevertheless, please try to follow the guidelines below as well as you can to - help make the PR process quick and smooth. - * Be patient with reviewers. We try our best to respond quickly, but we - have limited bandwidth. If there is no feedback within a couple of days, - please ping us by posting a comment to your PR. +* Changes were made on a :ref:`feature branch `. -When making a PR, pay attention to: +* :ref:`pre-commit ` checks for spelling, formatting, etc pass -.. rst-class:: checklist +* The pull request targets the :ref:`main branch ` -* :ref:`Target the main branch `. -* Adhere to the :ref:`coding_guidelines`. -* Update the :ref:`documentation ` if necessary. -* Aim at making the PR as "ready-to-go" as you can. This helps to speed up - the review process. -* It is ok to open incomplete or work-in-progress PRs if you need help or - feedback from the developers. You may mark these as - `draft pull requests `_ - on GitHub. -* When updating your PR, instead of adding new commits to fix something, please - consider amending your initial commit(s) to keep the history clean. - You can achieve this by using +* If your pull request addresses an issue, please use the title to describe the + issue (e.g. "Add ability to plot timedeltas") and mention the issue number + in the pull request description to ensure that a link is created to the + original issue (e.g. "Closes #8869" or "Fixes #8869"). This will ensure the + original issue mentioned is automatically closed when your PR is merged. For more + details, see `linking an issue and pull request `__. - .. code-block:: bash +* :ref:`pr-automated-tests` pass - git commit --amend --no-edit - git push [your-remote-repo] [your-branch] --force-with-lease +For guidance on creating and managing a pull request, please see our +:ref:`contributing ` and :ref:`pull request workflow ` +guides. -See also :ref:`contributing` for how to make a PR. Summary for pull request reviewers ================================== .. redirect-from:: /devel/maintainer_workflow -.. note:: +**Please help review and merge PRs!** + +If you have commit rights, then you are trusted to use them. Please be patient +and `kind `__ with contributors. - * If you have commit rights, then you are trusted to use them. - **Please help review and merge PRs!** - * Be patient and `kind `__ with - contributors. +When reviewing, please ensure that the pull request satisfies the following +requirements before merging it: Content topics: @@ -196,61 +134,6 @@ Documentation * See :ref:`documenting-matplotlib` for our documentation style guide. -.. _release_notes: - -New features and API changes -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -When adding a major new feature or changing the API in a backward incompatible -way, please document it by including a versioning directive in the docstring -and adding an entry to the folder for either the what's new or API change notes. - -+-------------------+-----------------------------+----------------------------------+ -| for this addition | include this directive | create entry in this folder | -+===================+=============================+==================================+ -| new feature | ``.. versionadded:: 3.N`` | :file:`doc/users/next_whats_new/`| -+-------------------+-----------------------------+----------------------------------+ -| API change | ``.. versionchanged:: 3.N`` | :file:`doc/api/next_api_changes/`| -| | | | -| | | probably in ``behavior/`` | -+-------------------+-----------------------------+----------------------------------+ - -The directives should be placed at the end of a description block. For example:: - - class Foo: - """ - This is the summary. - - Followed by a longer description block. - - Consisting of multiple lines and paragraphs. - - .. versionadded:: 3.5 - - Parameters - ---------- - a : int - The first parameter. - b: bool, default: False - This was added later. - - .. versionadded:: 3.6 - """ - - def set_b(b): - """ - Set b. - - .. versionadded:: 3.6 - - Parameters - ---------- - b: bool - -For classes and functions, the directive should be placed before the -*Parameters* section. For parameters, the directive should be placed at the -end of the parameter description. The patch release version is omitted and -the directive should not be added to entire modules. - .. _pr-labels: Labels @@ -330,7 +213,8 @@ Merging Automated tests --------------- -Before being merged, a PR should pass the :ref:`automated-tests`. If you are unsure why a test is failing, ask on the PR or in our `chat space `_ +Before being merged, a PR should pass the :ref:`automated-tests`. If you are +unsure why a test is failing, ask on the PR or in our :ref:`communication-channels` .. _pr-squashing: diff --git a/doc/devel/contribute.rst b/doc/devel/contribute.rst index a9bfb0f816dd..ee78beca0781 100644 --- a/doc/devel/contribute.rst +++ b/doc/devel/contribute.rst @@ -367,27 +367,160 @@ project that leads to a scientific publication, please follow the Coding guidelines ================= -API changes ------------ +While the current state of the Matplotlib code base is not compliant with all +of these guidelines, our goal in enforcing these constraints on new +contributions is that it improves the readability and consistency of the code base +going forward. -API consistency and stability are of great value. Therefore, API changes -(e.g. signature changes, behavior changes, removals) will only be conducted -if the added benefit is worth the user effort for adapting. +PEP8, as enforced by flake8 +--------------------------- + +Formatting should follow the recommendations of PEP8_, as enforced by flake8_. +Matplotlib modifies PEP8 to extend the maximum line length to 88 +characters. You can check flake8 compliance from the command line with :: + + python -m pip install flake8 + flake8 /path/to/module.py + +or your editor may provide integration with it. Note that Matplotlib intentionally +does not use the black_ auto-formatter (1__), in particular due to its inability +to understand the semantics of mathematical expressions (2__, 3__). + +.. _PEP8: https://www.python.org/dev/peps/pep-0008/ +.. _flake8: https://flake8.pycqa.org/ +.. _black: https://black.readthedocs.io/ +.. __: https://github.com/matplotlib/matplotlib/issues/18796 +.. __: https://github.com/psf/black/issues/148 +.. __: https://github.com/psf/black/issues/1984 + + +Package imports +--------------- +Import the following modules using the standard scipy conventions:: + + import numpy as np + import numpy.ma as ma + import matplotlib as mpl + import matplotlib.pyplot as plt + import matplotlib.cbook as cbook + import matplotlib.patches as mpatches + +In general, Matplotlib modules should **not** import `.rcParams` using ``from +matplotlib import rcParams``, but rather access it as ``mpl.rcParams``. This +is because some modules are imported very early, before the `.rcParams` +singleton is constructed. + +Variable names +-------------- + +When feasible, please use our internal variable naming convention for objects +of a given class and objects of any child class: + ++------------------------------------+---------------+------------------------------------------+ +| base class | variable | multiples | ++====================================+===============+==========================================+ +| `~matplotlib.figure.FigureBase` | ``fig`` | | ++------------------------------------+---------------+------------------------------------------+ +| `~matplotlib.axes.Axes` | ``ax`` | | ++------------------------------------+---------------+------------------------------------------+ +| `~matplotlib.transforms.Transform` | ``trans`` | ``trans__`` | ++ + + + +| | | ``trans_`` when target is screen | ++------------------------------------+---------------+------------------------------------------+ + +Generally, denote more than one instance of the same class by adding suffixes to +the variable names. If a format isn't specified in the table, use numbers or +letters as appropriate. + + +.. _type-hints: + +Type hints +---------- + +If you add new public API or change public API, update or add the +corresponding `mypy `_ type hints. +We generally use `stub files +`_ +(``*.pyi``) to store the type information; for example ``colors.pyi`` contains +the type information for ``colors.py``. A notable exception is ``pyplot.py``, +which is type hinted inline. + +Type hints are checked by the mypy :ref:`pre-commit hook ` +and can often be verified using ``tools\stubtest.py`` and occasionally may +require the use of ``tools\check_typehints.py``. + + +.. _new-changed-api: -Because we are a visualization library our primary output is the final -visualization the user sees. Thus it is our :ref:`long standing -` policy that the appearance of the figure is part of the API -and any changes, either semantic or esthetic, will be treated as a -backwards-incompatible API change. +API changes and new features +---------------------------- +API consistency and stability are of great value; Therefore, API changes +(e.g. signature changes, behavior changes, removals) will only be conducted +if the added benefit is worth the effort of adapting existing code. + +Because we are a visualization library, our primary output is the final +visualization the user sees; therefore, the appearance of the figure is part of +the API and any changes, either semantic or :ref:`esthetic `, +are backwards-incompatible API changes. + +.. _api_whats_new: + +Announce changes, deprecations, and new features +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When adding or changing the API in a backward in-compatible way, please add the +appropriate :ref:`versioning directive ` and document it +for the release notes and add the entry to the appropriate folder: + ++-------------------+-----------------------------+----------------------------------------------+ +| addition | versioning directive | announcement folder | ++===================+=============================+==============================================+ +| new feature | ``.. versionadded:: 3.N`` | :file:`doc/users/next_whats_new/` | ++-------------------+-----------------------------+----------------------------------------------+ +| API change | ``.. versionchanged:: 3.N`` | :file:`doc/api/next_api_changes/[kind]` | ++-------------------+-----------------------------+----------------------------------------------+ + +API deprecations are first introduced and then expired. During the introduction +period, users are warned that the API *will* change in the future. +During the expiration period, code is changed as described in the notice posted +during the introductory period. + ++-----------+--------------------------------------------------+----------------------------------------------+ +| stage | required changes | announcement folder | ++===========+==================================================+==============================================+ +| introduce | :ref:`introduce deprecation ` | :file:`doc/api/next_api_changes/deprecation` | ++-----------+--------------------------------------------------+----------------------------------------------+ +| expire | :ref:`expire deprecation ` | :file:`doc/api/next_api_changes/[kind]` | ++-----------+--------------------------------------------------+----------------------------------------------+ + +For both change notes and what's new, please avoid using references in section +titles, as it causes links to be confusing in the table of contents. Instead, +ensure that a reference is included in the descriptive text. + +API Change Notes +"""""""""""""""" +.. include:: ../api/next_api_changes/README.rst + :start-line: 5 + :end-line: 31 + +What's new +"""""""""" +.. include:: ../users/next_whats_new/README.rst + :start-line: 5 + :end-line: 24 + + +Deprecation +^^^^^^^^^^^ API changes in Matplotlib have to be performed following the deprecation process -below, except in very rare circumstances as deemed necessary by the development team. -This ensures that users are notified before the change will take effect and thus -prevents unexpected breaking of code. +below, except in very rare circumstances as deemed necessary by the development +team. This ensures that users are notified before the change will take effect +and thus prevents unexpected breaking of code. Rules -^^^^^ - +""""" - Deprecations are targeted at the next point.release (e.g. 3.x) - Deprecated API is generally removed two point-releases after introduction of the deprecation. Longer deprecations can be imposed by core developers on @@ -398,12 +531,12 @@ Rules - If in doubt, decisions about API changes are finally made by the API consistency lead developer -Introducing -^^^^^^^^^^^ +.. _intro-deprecation: + +Introduce deprecation +""""""""""""""""""""" -#. Announce the deprecation in a new file - :file:`doc/api/next_api_changes/deprecations/99999-ABC.rst` where ``99999`` - is the pull request number and ``ABC`` are the contributor's initials. +#. Create :ref:`deprecation notice ` #. If possible, issue a `~matplotlib.MatplotlibDeprecationWarning` when the deprecated API is used. There are a number of helper tools for this: @@ -439,16 +572,13 @@ Introducing :file:`ci/mypy-stubtest-allowlist.txt` under a heading indicating the deprecation version number. -Expiring -^^^^^^^^ +.. _expire-deprecation: + +Expire deprecation +"""""""""""""""""" -#. Announce the API changes in a new file - :file:`doc/api/next_api_changes/[kind]/99999-ABC.rst` where ``99999`` - is the pull request number and ``ABC`` are the contributor's initials, and - ``[kind]`` is one of the folders :file:`behavior`, :file:`development`, - :file:`removals`. See :file:`doc/api/next_api_changes/README.rst` for more - information. For the content, you can usually copy the deprecation notice - and adapt it slightly. +#. Create :ref:`deprecation announcement `. For the content, + you can usually copy the deprecation notice and adapt it slightly. #. Change the code functionality and remove any related deprecation warnings. @@ -466,8 +596,8 @@ Expiring require that mechanism for deprecation. For removed items that were not in the stub file, only deleting from the allowlist is required. -Adding new API --------------- +Adding new API and features +^^^^^^^^^^^^^^^^^^^^^^^^^^^ Every new function, parameter and attribute that is not explicitly marked as private (i.e., starts with an underscore) becomes part of Matplotlib's public @@ -485,6 +615,51 @@ take particular care when adding new API: __ https://emptysqua.re/blog/api-evolution-the-right-way/#adding-parameters +.. _versioning-directives: + +Versioning directives +""""""""""""""""""""" + +When making a backward incompatible change, please add a versioning directive in +the docstring. The directives should be placed at the end of a description block. +For example:: + + class Foo: + """ + This is the summary. + + Followed by a longer description block. + + Consisting of multiple lines and paragraphs. + + .. versionadded:: 3.5 + + Parameters + ---------- + a : int + The first parameter. + b: bool, default: False + This was added later. + + .. versionadded:: 3.6 + """ + + def set_b(b): + """ + Set b. + + .. versionadded:: 3.6 + + Parameters + ---------- + b: bool + +For classes and functions, the directive should be placed before the +*Parameters* section. For parameters, the directive should be placed at the +end of the parameter description. The patch release version is omitted and +the directive should not be added to entire modules. + + New modules and files: installation ----------------------------------- diff --git a/doc/devel/development_setup.rst b/doc/devel/development_setup.rst index 9da1c5737a36..e18d85cebde0 100644 --- a/doc/devel/development_setup.rst +++ b/doc/devel/development_setup.rst @@ -195,6 +195,8 @@ so that when you make code or document related changes you are aware of the exis * Run test cases to verify installation :ref:`testing` * Verify documentation build :ref:`documenting-matplotlib` +.. _pre-commit-hooks: + Install pre-commit hooks ======================== `pre-commit `_ hooks save time in the review process by diff --git a/doc/devel/development_workflow.rst b/doc/devel/development_workflow.rst index 50170ee4ade3..3f4fe956e37e 100644 --- a/doc/devel/development_workflow.rst +++ b/doc/devel/development_workflow.rst @@ -151,6 +151,23 @@ If you don't think your request is ready to be merged, just say so in your pull request message and use the "Draft PR" feature of GitHub. This is a good way of getting some preliminary code review. +.. _update-pull-request: + +Update a pull request +===================== + +When updating your pull request after making revisions, instead of adding new +commits, please consider amending your initial commit(s) to keep the commit +history clean. + +You can achieve this by using + + .. code-block:: bash + + git commit -a --amend --no-edit + git push [your-remote-repo] [your-branch] --force-with-lease + + Manage commit history ===================== diff --git a/doc/users/next_whats_new/README.rst b/doc/users/next_whats_new/README.rst index e1b27ef97f1e..98b601ee32d8 100644 --- a/doc/users/next_whats_new/README.rst +++ b/doc/users/next_whats_new/README.rst @@ -11,9 +11,7 @@ something like :file:`cool_new_feature.rst` if you have added a brand new feature or something like :file:`updated_feature.rst` for extensions of existing features. -Please avoid using references in section titles, as it causes links to be -confusing in the table of contents. Instead, ensure that a reference is -included in the descriptive text. Include contents of the form: :: +Include contents of the form:: Section title for feature ------------------------- @@ -23,3 +21,11 @@ included in the descriptive text. Include contents of the form: :: A sub-section ~~~~~~~~~~~~~ + +Please avoid using references in section titles, as it causes links to be +confusing in the table of contents. Instead, ensure that a reference is +included in the descriptive text. + +.. NOTE + Lines 5-24 of this file are include in :ref:`api_whats_new`; + therefore, please check the doc build after changing this file. From 917df66a80e44dd5f1588028eae00d5313dcfb86 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sun, 5 Nov 2023 22:26:26 +0100 Subject: [PATCH 06/87] Backport PR #27268: Copy-edit various examples. --- galleries/examples/color/README.txt | 3 +-- galleries/examples/color/named_colors.py | 2 +- .../images_contours_and_fields/image_demo.py | 8 +++----- .../images_contours_and_fields/pcolor_demo.py | 12 +++++------- .../text_labels_and_annotations/accented_text.py | 6 +++--- .../annotation_demo.py | 2 +- .../text_labels_and_annotations/font_file.py | 6 +++--- .../text_labels_and_annotations/legend_demo.py | 2 -- .../axes/constrainedlayout_guide.py | 8 +++----- galleries/users_explain/axes/legend_guide.py | 11 ++++------- .../users_explain/axes/tight_layout_guide.py | 16 ++++++---------- .../colors/colormap-manipulation.py | 15 ++++++--------- galleries/users_explain/colors/colormaps.py | 6 +++--- galleries/users_explain/text/text_intro.py | 6 +++--- 14 files changed, 42 insertions(+), 61 deletions(-) diff --git a/galleries/examples/color/README.txt b/galleries/examples/color/README.txt index 4bcdb526f4d4..4b8b3bc4b751 100644 --- a/galleries/examples/color/README.txt +++ b/galleries/examples/color/README.txt @@ -3,6 +3,5 @@ Color ===== -For more in-depth information about the colormaps available in matplotlib -as well as a description of their properties, +For a description of the colormaps available in Matplotlib, see the :ref:`colormaps tutorial `. diff --git a/galleries/examples/color/named_colors.py b/galleries/examples/color/named_colors.py index 9ae4ec4957f3..d9a7259da773 100644 --- a/galleries/examples/color/named_colors.py +++ b/galleries/examples/color/named_colors.py @@ -3,7 +3,7 @@ List of named colors ==================== -This plots a list of the named colors supported in matplotlib. +This plots a list of the named colors supported by Matplotlib. For more information on colors in matplotlib see * the :ref:`colors_def` tutorial; diff --git a/galleries/examples/images_contours_and_fields/image_demo.py b/galleries/examples/images_contours_and_fields/image_demo.py index 884e68172698..213b6cdd28c2 100644 --- a/galleries/examples/images_contours_and_fields/image_demo.py +++ b/galleries/examples/images_contours_and_fields/image_demo.py @@ -1,9 +1,7 @@ """ -========== -Image demo -========== - -Many ways to plot images in Matplotlib. +======================== +Many ways to plot images +======================== The most common way to plot images in Matplotlib is with `~.axes.Axes.imshow`. The following examples demonstrate much of the diff --git a/galleries/examples/images_contours_and_fields/pcolor_demo.py b/galleries/examples/images_contours_and_fields/pcolor_demo.py index a783c83409df..7a8ef35caf96 100644 --- a/galleries/examples/images_contours_and_fields/pcolor_demo.py +++ b/galleries/examples/images_contours_and_fields/pcolor_demo.py @@ -1,13 +1,11 @@ """ -=========== -Pcolor demo -=========== +============= +pcolor images +============= -Generating images with `~.axes.Axes.pcolor`. - -Pcolor allows you to generate 2D image-style plots. Below we will show how -to do so in Matplotlib. +`~.Axes.pcolor` generates 2D image-style plots, as illustrated below. """ + import matplotlib.pyplot as plt import numpy as np diff --git a/galleries/examples/text_labels_and_annotations/accented_text.py b/galleries/examples/text_labels_and_annotations/accented_text.py index f57b19532a43..7ff080afefd9 100644 --- a/galleries/examples/text_labels_and_annotations/accented_text.py +++ b/galleries/examples/text_labels_and_annotations/accented_text.py @@ -1,7 +1,7 @@ r""" -================================= -Using accented text in Matplotlib -================================= +============= +Accented text +============= Matplotlib supports accented characters via TeX mathtext or Unicode. diff --git a/galleries/examples/text_labels_and_annotations/annotation_demo.py b/galleries/examples/text_labels_and_annotations/annotation_demo.py index 8b310a7a1865..26f3b80bf203 100644 --- a/galleries/examples/text_labels_and_annotations/annotation_demo.py +++ b/galleries/examples/text_labels_and_annotations/annotation_demo.py @@ -3,7 +3,7 @@ Annotating Plots ================ -The following examples show how it is possible to annotate plots in Matplotlib. +The following examples show ways to annotate plots in Matplotlib. This includes highlighting specific points of interest and using various visual tools to call attention to this point. For a more complete and in-depth description of the annotation and text tools in Matplotlib, see the diff --git a/galleries/examples/text_labels_and_annotations/font_file.py b/galleries/examples/text_labels_and_annotations/font_file.py index 8242b57839aa..b4a58808d716 100644 --- a/galleries/examples/text_labels_and_annotations/font_file.py +++ b/galleries/examples/text_labels_and_annotations/font_file.py @@ -1,7 +1,7 @@ r""" -=================================== -Using a ttf font file in Matplotlib -=================================== +==================== +Using ttf font files +==================== Although it is usually not a good idea to explicitly point to a single ttf file for a font instance, you can do so by passing a `pathlib.Path` instance as the diff --git a/galleries/examples/text_labels_and_annotations/legend_demo.py b/galleries/examples/text_labels_and_annotations/legend_demo.py index a425d39b7d8f..2f550729837e 100644 --- a/galleries/examples/text_labels_and_annotations/legend_demo.py +++ b/galleries/examples/text_labels_and_annotations/legend_demo.py @@ -3,8 +3,6 @@ Legend Demo =========== -Plotting legends in Matplotlib. - There are many ways to create and customize legends in Matplotlib. Below we'll show a few examples for how to do so. diff --git a/galleries/users_explain/axes/constrainedlayout_guide.py b/galleries/users_explain/axes/constrainedlayout_guide.py index 260a4f76bf71..4581f5f67808 100644 --- a/galleries/users_explain/axes/constrainedlayout_guide.py +++ b/galleries/users_explain/axes/constrainedlayout_guide.py @@ -40,15 +40,13 @@ .. warning:: - Calling ``plt.tight_layout()`` will turn off *constrained layout*! + Calling `~.pyplot.tight_layout` will turn off *constrained layout*! Simple example ============== -In Matplotlib, the location of Axes (including subplots) are specified in -normalized figure coordinates. It can happen that your axis labels or titles -(or sometimes even ticklabels) go outside the figure area, and are thus -clipped. +With the default Axes positioning, the axes title, axis labels, or tick labels +can sometimes go outside the figure area, and thus get clipped. """ # sphinx_gallery_thumbnail_number = 18 diff --git a/galleries/users_explain/axes/legend_guide.py b/galleries/users_explain/axes/legend_guide.py index 9900b0aa4bdd..3b138fe8ada3 100644 --- a/galleries/users_explain/axes/legend_guide.py +++ b/galleries/users_explain/axes/legend_guide.py @@ -7,13 +7,10 @@ Legend guide ============ -Generating legends flexibly in Matplotlib. - .. currentmodule:: matplotlib.pyplot -This legend guide is an extension of the documentation available at -:func:`~matplotlib.pyplot.legend` - please ensure you are familiar with -contents of that documentation before proceeding with this guide. +This legend guide extends the `~.Axes.legend` docstring - +please read it before proceeding with this guide. This guide makes use of some common terms, which are documented here for clarity: @@ -62,8 +59,8 @@ line_down, = ax.plot([3, 2, 1], label='Line 1') ax.legend(handles=[line_up, line_down]) -In some cases, it is not possible to set the label of the handle, so it is -possible to pass through the list of labels to :func:`legend`:: +In the rare case where the labels cannot directly be set on the handles, they +can also be directly passed to :func:`legend`:: fig, ax = plt.subplots() line_up, = ax.plot([1, 2, 3], label='Line 2') diff --git a/galleries/users_explain/axes/tight_layout_guide.py b/galleries/users_explain/axes/tight_layout_guide.py index 42c227b2e360..8525b9773f91 100644 --- a/galleries/users_explain/axes/tight_layout_guide.py +++ b/galleries/users_explain/axes/tight_layout_guide.py @@ -17,15 +17,11 @@ An alternative to *tight_layout* is :ref:`constrained_layout `. - -Simple Example +Simple example ============== -In matplotlib, the location of axes (including subplots) are specified in -normalized figure coordinates. It can happen that your axis labels or -titles (or sometimes even ticklabels) go outside the figure area, and are thus -clipped. - +With the default Axes positioning, the axes title, axis labels, or tick labels +can sometimes go outside the figure area, and thus get clipped. """ # sphinx_gallery_thumbnail_number = 7 @@ -190,8 +186,8 @@ def example_plot(ax, fontsize=12): # %% # You may provide an optional *rect* parameter, which specifies the bounding -# box that the subplots will be fit inside. The coordinates must be in -# normalized figure coordinates and the default is (0, 0, 1, 1). +# box that the subplots will be fit inside. The coordinates are in +# normalized figure coordinates and default to (0, 0, 1, 1) (the whole figure). fig = plt.figure() @@ -245,7 +241,7 @@ def example_plot(ax, fontsize=12): # Use with AxesGrid1 # ================== # -# While limited, :mod:`mpl_toolkits.axes_grid1` is also supported. +# Limited support for :mod:`mpl_toolkits.axes_grid1` is provided. from mpl_toolkits.axes_grid1 import Grid diff --git a/galleries/users_explain/colors/colormap-manipulation.py b/galleries/users_explain/colors/colormap-manipulation.py index 88e4c5befaf0..87269b87befa 100644 --- a/galleries/users_explain/colors/colormap-manipulation.py +++ b/galleries/users_explain/colors/colormap-manipulation.py @@ -3,9 +3,9 @@ .. _colormap-manipulation: -******************************** -Creating Colormaps in Matplotlib -******************************** +****************** +Creating Colormaps +****************** Matplotlib has a number of built-in colormaps accessible via `.matplotlib.colormaps`. There are also external libraries like @@ -13,17 +13,15 @@ .. _palettable: https://jiffyclub.github.io/palettable/ -However, we often want to create or manipulate colormaps in Matplotlib. +However, we may also want to create or manipulate our own colormaps. This can be done using the class `.ListedColormap` or `.LinearSegmentedColormap`. -Seen from the outside, both colormap classes map values between 0 and 1 to -a bunch of colors. There are, however, slight differences, some of which are -shown in the following. +Both colormap classes map values between 0 and 1 to colors. There are however +differences, as explained below. Before manually creating or manipulating colormaps, let us first see how we can obtain colormaps and their colors from existing colormap classes. - Getting colormaps and accessing their values ============================================ @@ -32,7 +30,6 @@ which returns a colormap object. The length of the list of colors used internally to define the colormap can be adjusted via `.Colormap.resampled`. Below we use a modest value of 8 so there are not a lot of values to look at. - """ import matplotlib.pyplot as plt diff --git a/galleries/users_explain/colors/colormaps.py b/galleries/users_explain/colors/colormaps.py index 92b56d298976..b5db551cb5b5 100644 --- a/galleries/users_explain/colors/colormaps.py +++ b/galleries/users_explain/colors/colormaps.py @@ -3,9 +3,9 @@ .. _colormaps: -******************************** -Choosing Colormaps in Matplotlib -******************************** +****************** +Choosing Colormaps +****************** Matplotlib has a number of built-in colormaps accessible via `.matplotlib.colormaps`. There are also external libraries that diff --git a/galleries/users_explain/text/text_intro.py b/galleries/users_explain/text/text_intro.py index 54fcb00c7a86..948545667fa9 100644 --- a/galleries/users_explain/text/text_intro.py +++ b/galleries/users_explain/text/text_intro.py @@ -4,9 +4,9 @@ .. _text_intro: -======================== -Text in Matplotlib Plots -======================== +================== +Text in Matplotlib +================== Introduction to plotting and working with text in Matplotlib. From f32d0f18bf20075129f55f339e427fadd1470ed5 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sun, 5 Nov 2023 22:26:26 +0100 Subject: [PATCH 07/87] Backport PR #27268: Copy-edit various examples. --- galleries/examples/color/README.txt | 3 +-- galleries/examples/color/named_colors.py | 2 +- .../images_contours_and_fields/image_demo.py | 8 +++----- .../images_contours_and_fields/pcolor_demo.py | 12 +++++------- .../text_labels_and_annotations/accented_text.py | 6 +++--- .../annotation_demo.py | 2 +- .../text_labels_and_annotations/font_file.py | 6 +++--- .../text_labels_and_annotations/legend_demo.py | 2 -- .../axes/constrainedlayout_guide.py | 8 +++----- galleries/users_explain/axes/legend_guide.py | 11 ++++------- .../users_explain/axes/tight_layout_guide.py | 16 ++++++---------- .../colors/colormap-manipulation.py | 15 ++++++--------- galleries/users_explain/colors/colormaps.py | 6 +++--- galleries/users_explain/text/text_intro.py | 6 +++--- 14 files changed, 42 insertions(+), 61 deletions(-) diff --git a/galleries/examples/color/README.txt b/galleries/examples/color/README.txt index 4bcdb526f4d4..4b8b3bc4b751 100644 --- a/galleries/examples/color/README.txt +++ b/galleries/examples/color/README.txt @@ -3,6 +3,5 @@ Color ===== -For more in-depth information about the colormaps available in matplotlib -as well as a description of their properties, +For a description of the colormaps available in Matplotlib, see the :ref:`colormaps tutorial `. diff --git a/galleries/examples/color/named_colors.py b/galleries/examples/color/named_colors.py index 9ae4ec4957f3..d9a7259da773 100644 --- a/galleries/examples/color/named_colors.py +++ b/galleries/examples/color/named_colors.py @@ -3,7 +3,7 @@ List of named colors ==================== -This plots a list of the named colors supported in matplotlib. +This plots a list of the named colors supported by Matplotlib. For more information on colors in matplotlib see * the :ref:`colors_def` tutorial; diff --git a/galleries/examples/images_contours_and_fields/image_demo.py b/galleries/examples/images_contours_and_fields/image_demo.py index 884e68172698..213b6cdd28c2 100644 --- a/galleries/examples/images_contours_and_fields/image_demo.py +++ b/galleries/examples/images_contours_and_fields/image_demo.py @@ -1,9 +1,7 @@ """ -========== -Image demo -========== - -Many ways to plot images in Matplotlib. +======================== +Many ways to plot images +======================== The most common way to plot images in Matplotlib is with `~.axes.Axes.imshow`. The following examples demonstrate much of the diff --git a/galleries/examples/images_contours_and_fields/pcolor_demo.py b/galleries/examples/images_contours_and_fields/pcolor_demo.py index a783c83409df..7a8ef35caf96 100644 --- a/galleries/examples/images_contours_and_fields/pcolor_demo.py +++ b/galleries/examples/images_contours_and_fields/pcolor_demo.py @@ -1,13 +1,11 @@ """ -=========== -Pcolor demo -=========== +============= +pcolor images +============= -Generating images with `~.axes.Axes.pcolor`. - -Pcolor allows you to generate 2D image-style plots. Below we will show how -to do so in Matplotlib. +`~.Axes.pcolor` generates 2D image-style plots, as illustrated below. """ + import matplotlib.pyplot as plt import numpy as np diff --git a/galleries/examples/text_labels_and_annotations/accented_text.py b/galleries/examples/text_labels_and_annotations/accented_text.py index f57b19532a43..7ff080afefd9 100644 --- a/galleries/examples/text_labels_and_annotations/accented_text.py +++ b/galleries/examples/text_labels_and_annotations/accented_text.py @@ -1,7 +1,7 @@ r""" -================================= -Using accented text in Matplotlib -================================= +============= +Accented text +============= Matplotlib supports accented characters via TeX mathtext or Unicode. diff --git a/galleries/examples/text_labels_and_annotations/annotation_demo.py b/galleries/examples/text_labels_and_annotations/annotation_demo.py index 8b310a7a1865..26f3b80bf203 100644 --- a/galleries/examples/text_labels_and_annotations/annotation_demo.py +++ b/galleries/examples/text_labels_and_annotations/annotation_demo.py @@ -3,7 +3,7 @@ Annotating Plots ================ -The following examples show how it is possible to annotate plots in Matplotlib. +The following examples show ways to annotate plots in Matplotlib. This includes highlighting specific points of interest and using various visual tools to call attention to this point. For a more complete and in-depth description of the annotation and text tools in Matplotlib, see the diff --git a/galleries/examples/text_labels_and_annotations/font_file.py b/galleries/examples/text_labels_and_annotations/font_file.py index 8242b57839aa..b4a58808d716 100644 --- a/galleries/examples/text_labels_and_annotations/font_file.py +++ b/galleries/examples/text_labels_and_annotations/font_file.py @@ -1,7 +1,7 @@ r""" -=================================== -Using a ttf font file in Matplotlib -=================================== +==================== +Using ttf font files +==================== Although it is usually not a good idea to explicitly point to a single ttf file for a font instance, you can do so by passing a `pathlib.Path` instance as the diff --git a/galleries/examples/text_labels_and_annotations/legend_demo.py b/galleries/examples/text_labels_and_annotations/legend_demo.py index a425d39b7d8f..2f550729837e 100644 --- a/galleries/examples/text_labels_and_annotations/legend_demo.py +++ b/galleries/examples/text_labels_and_annotations/legend_demo.py @@ -3,8 +3,6 @@ Legend Demo =========== -Plotting legends in Matplotlib. - There are many ways to create and customize legends in Matplotlib. Below we'll show a few examples for how to do so. diff --git a/galleries/users_explain/axes/constrainedlayout_guide.py b/galleries/users_explain/axes/constrainedlayout_guide.py index 260a4f76bf71..4581f5f67808 100644 --- a/galleries/users_explain/axes/constrainedlayout_guide.py +++ b/galleries/users_explain/axes/constrainedlayout_guide.py @@ -40,15 +40,13 @@ .. warning:: - Calling ``plt.tight_layout()`` will turn off *constrained layout*! + Calling `~.pyplot.tight_layout` will turn off *constrained layout*! Simple example ============== -In Matplotlib, the location of Axes (including subplots) are specified in -normalized figure coordinates. It can happen that your axis labels or titles -(or sometimes even ticklabels) go outside the figure area, and are thus -clipped. +With the default Axes positioning, the axes title, axis labels, or tick labels +can sometimes go outside the figure area, and thus get clipped. """ # sphinx_gallery_thumbnail_number = 18 diff --git a/galleries/users_explain/axes/legend_guide.py b/galleries/users_explain/axes/legend_guide.py index 9900b0aa4bdd..3b138fe8ada3 100644 --- a/galleries/users_explain/axes/legend_guide.py +++ b/galleries/users_explain/axes/legend_guide.py @@ -7,13 +7,10 @@ Legend guide ============ -Generating legends flexibly in Matplotlib. - .. currentmodule:: matplotlib.pyplot -This legend guide is an extension of the documentation available at -:func:`~matplotlib.pyplot.legend` - please ensure you are familiar with -contents of that documentation before proceeding with this guide. +This legend guide extends the `~.Axes.legend` docstring - +please read it before proceeding with this guide. This guide makes use of some common terms, which are documented here for clarity: @@ -62,8 +59,8 @@ line_down, = ax.plot([3, 2, 1], label='Line 1') ax.legend(handles=[line_up, line_down]) -In some cases, it is not possible to set the label of the handle, so it is -possible to pass through the list of labels to :func:`legend`:: +In the rare case where the labels cannot directly be set on the handles, they +can also be directly passed to :func:`legend`:: fig, ax = plt.subplots() line_up, = ax.plot([1, 2, 3], label='Line 2') diff --git a/galleries/users_explain/axes/tight_layout_guide.py b/galleries/users_explain/axes/tight_layout_guide.py index 42c227b2e360..8525b9773f91 100644 --- a/galleries/users_explain/axes/tight_layout_guide.py +++ b/galleries/users_explain/axes/tight_layout_guide.py @@ -17,15 +17,11 @@ An alternative to *tight_layout* is :ref:`constrained_layout `. - -Simple Example +Simple example ============== -In matplotlib, the location of axes (including subplots) are specified in -normalized figure coordinates. It can happen that your axis labels or -titles (or sometimes even ticklabels) go outside the figure area, and are thus -clipped. - +With the default Axes positioning, the axes title, axis labels, or tick labels +can sometimes go outside the figure area, and thus get clipped. """ # sphinx_gallery_thumbnail_number = 7 @@ -190,8 +186,8 @@ def example_plot(ax, fontsize=12): # %% # You may provide an optional *rect* parameter, which specifies the bounding -# box that the subplots will be fit inside. The coordinates must be in -# normalized figure coordinates and the default is (0, 0, 1, 1). +# box that the subplots will be fit inside. The coordinates are in +# normalized figure coordinates and default to (0, 0, 1, 1) (the whole figure). fig = plt.figure() @@ -245,7 +241,7 @@ def example_plot(ax, fontsize=12): # Use with AxesGrid1 # ================== # -# While limited, :mod:`mpl_toolkits.axes_grid1` is also supported. +# Limited support for :mod:`mpl_toolkits.axes_grid1` is provided. from mpl_toolkits.axes_grid1 import Grid diff --git a/galleries/users_explain/colors/colormap-manipulation.py b/galleries/users_explain/colors/colormap-manipulation.py index 88e4c5befaf0..87269b87befa 100644 --- a/galleries/users_explain/colors/colormap-manipulation.py +++ b/galleries/users_explain/colors/colormap-manipulation.py @@ -3,9 +3,9 @@ .. _colormap-manipulation: -******************************** -Creating Colormaps in Matplotlib -******************************** +****************** +Creating Colormaps +****************** Matplotlib has a number of built-in colormaps accessible via `.matplotlib.colormaps`. There are also external libraries like @@ -13,17 +13,15 @@ .. _palettable: https://jiffyclub.github.io/palettable/ -However, we often want to create or manipulate colormaps in Matplotlib. +However, we may also want to create or manipulate our own colormaps. This can be done using the class `.ListedColormap` or `.LinearSegmentedColormap`. -Seen from the outside, both colormap classes map values between 0 and 1 to -a bunch of colors. There are, however, slight differences, some of which are -shown in the following. +Both colormap classes map values between 0 and 1 to colors. There are however +differences, as explained below. Before manually creating or manipulating colormaps, let us first see how we can obtain colormaps and their colors from existing colormap classes. - Getting colormaps and accessing their values ============================================ @@ -32,7 +30,6 @@ which returns a colormap object. The length of the list of colors used internally to define the colormap can be adjusted via `.Colormap.resampled`. Below we use a modest value of 8 so there are not a lot of values to look at. - """ import matplotlib.pyplot as plt diff --git a/galleries/users_explain/colors/colormaps.py b/galleries/users_explain/colors/colormaps.py index 92b56d298976..b5db551cb5b5 100644 --- a/galleries/users_explain/colors/colormaps.py +++ b/galleries/users_explain/colors/colormaps.py @@ -3,9 +3,9 @@ .. _colormaps: -******************************** -Choosing Colormaps in Matplotlib -******************************** +****************** +Choosing Colormaps +****************** Matplotlib has a number of built-in colormaps accessible via `.matplotlib.colormaps`. There are also external libraries that diff --git a/galleries/users_explain/text/text_intro.py b/galleries/users_explain/text/text_intro.py index 54fcb00c7a86..948545667fa9 100644 --- a/galleries/users_explain/text/text_intro.py +++ b/galleries/users_explain/text/text_intro.py @@ -4,9 +4,9 @@ .. _text_intro: -======================== -Text in Matplotlib Plots -======================== +================== +Text in Matplotlib +================== Introduction to plotting and working with text in Matplotlib. From 966d7e48e601d6b6b2e98c81ae9dc03fba6a437f Mon Sep 17 00:00:00 2001 From: Ruth Comer <10599679+rcomer@users.noreply.github.com> Date: Mon, 6 Nov 2023 09:00:08 +0000 Subject: [PATCH 08/87] Backport PR #27271: DOC: minor fixes to dev workflow --- doc/devel/development_setup.rst | 7 ++++--- doc/devel/development_workflow.rst | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/doc/devel/development_setup.rst b/doc/devel/development_setup.rst index e18d85cebde0..d5f491a82563 100644 --- a/doc/devel/development_setup.rst +++ b/doc/devel/development_setup.rst @@ -25,11 +25,12 @@ Fork the Matplotlib repository ============================== Matplotlib is hosted at https://github.com/matplotlib/matplotlib.git. If you -plan on solving issues or submit pull requests to the main Matplotlib +plan on solving issues or submitting pull requests to the main Matplotlib repository, you should first *fork* this repository by visiting https://github.com/matplotlib/matplotlib.git and clicking on the -``Fork`` button on the top right of the page (see -`the GitHub documentation `__ for more details.) +``Fork`` :octicon:`repo-forked` button on the top right of the page. See +`the GitHub documentation `__ +for more details. Retrieve the latest version of the code ======================================= diff --git a/doc/devel/development_workflow.rst b/doc/devel/development_workflow.rst index 3f4fe956e37e..edb21bb862cd 100644 --- a/doc/devel/development_workflow.rst +++ b/doc/devel/development_workflow.rst @@ -162,10 +162,10 @@ history clean. You can achieve this by using - .. code-block:: bash +.. code-block:: bash - git commit -a --amend --no-edit - git push [your-remote-repo] [your-branch] --force-with-lease + git commit -a --amend --no-edit + git push [your-remote-repo] [your-branch] --force-with-lease Manage commit history From c5af4a4a813ba1dfb83ae7c047166b9825da84eb Mon Sep 17 00:00:00 2001 From: Ruth Comer <10599679+rcomer@users.noreply.github.com> Date: Mon, 6 Nov 2023 09:00:08 +0000 Subject: [PATCH 09/87] Backport PR #27271: DOC: minor fixes to dev workflow --- doc/devel/development_setup.rst | 7 ++++--- doc/devel/development_workflow.rst | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/doc/devel/development_setup.rst b/doc/devel/development_setup.rst index e18d85cebde0..d5f491a82563 100644 --- a/doc/devel/development_setup.rst +++ b/doc/devel/development_setup.rst @@ -25,11 +25,12 @@ Fork the Matplotlib repository ============================== Matplotlib is hosted at https://github.com/matplotlib/matplotlib.git. If you -plan on solving issues or submit pull requests to the main Matplotlib +plan on solving issues or submitting pull requests to the main Matplotlib repository, you should first *fork* this repository by visiting https://github.com/matplotlib/matplotlib.git and clicking on the -``Fork`` button on the top right of the page (see -`the GitHub documentation `__ for more details.) +``Fork`` :octicon:`repo-forked` button on the top right of the page. See +`the GitHub documentation `__ +for more details. Retrieve the latest version of the code ======================================= diff --git a/doc/devel/development_workflow.rst b/doc/devel/development_workflow.rst index 3f4fe956e37e..edb21bb862cd 100644 --- a/doc/devel/development_workflow.rst +++ b/doc/devel/development_workflow.rst @@ -162,10 +162,10 @@ history clean. You can achieve this by using - .. code-block:: bash +.. code-block:: bash - git commit -a --amend --no-edit - git push [your-remote-repo] [your-branch] --force-with-lease + git commit -a --amend --no-edit + git push [your-remote-repo] [your-branch] --force-with-lease Manage commit history From 0800a480d15a0b5971dbc4dd3c150b3d5e9e073c Mon Sep 17 00:00:00 2001 From: hannah Date: Mon, 6 Nov 2023 15:37:11 -0500 Subject: [PATCH 10/87] Backport PR #27276: Clarify behavior of `prune` parameter to MaxNLocator. --- lib/matplotlib/ticker.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index 958e25d7b2c7..5b1b7e11408c 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -1996,13 +1996,11 @@ def __init__(self, nbins=None, **kwargs): If True, autoscaling will result in a range symmetric about zero. prune : {'lower', 'upper', 'both', None}, default: None - Remove edge ticks -- useful for stacked or ganged plots where - the upper tick of one axes overlaps with the lower tick of the - axes above it, primarily when :rc:`axes.autolimit_mode` is - ``'round_numbers'``. If ``prune=='lower'``, the smallest tick will - be removed. If ``prune == 'upper'``, the largest tick will be - removed. If ``prune == 'both'``, the largest and smallest ticks - will be removed. If *prune* is *None*, no ticks will be removed. + Remove the 'lower' tick, the 'upper' tick, or ticks on 'both' sides + *if they fall exactly on an axis' edge* (this typically occurs when + :rc:`axes.autolimit_mode` is 'round_numbers'). Removing such ticks + is mostly useful for stacked or ganged plots, where the upper tick + of an axes overlaps with the lower tick of the axes above it. min_n_ticks : int, default: 2 Relax *nbins* and *integer* constraints if necessary to obtain From e3eb638135a59155f9f00b30c70e95533f1c181c Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Tue, 7 Nov 2023 10:51:56 -0500 Subject: [PATCH 11/87] Backport PR #27280: DOC: added rest of licenses to license page --- doc/_static/mpl.css | 19 ++++++++ doc/users/project/license.rst | 85 ++++++++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 2 deletions(-) diff --git a/doc/_static/mpl.css b/doc/_static/mpl.css index a37fb76a0436..2b57f7608d26 100644 --- a/doc/_static/mpl.css +++ b/doc/_static/mpl.css @@ -180,3 +180,22 @@ div.wide-table table th.stub { .checklist li p { display: inline; } + +.sdd.sd-dropdown { + box-shadow: none!important; +} + +.sdd.sd-dropdown.sd-card{ + border-style: solid !important; + border-color: var(--pst-color-border) !important; + border-width: thin !important; + border-radius: .05 +} + +.sdd.sd-dropdown .sd-card-header{ + --pst-sd-dropdown-color: none; +} + +.sdd.sd-dropdown .sd-card-header +.sd-card-body{ + --pst-sd-dropdown-color: none; +} diff --git a/doc/users/project/license.rst b/doc/users/project/license.rst index a55927d9f2c5..3f9c1e30531b 100644 --- a/doc/users/project/license.rst +++ b/doc/users/project/license.rst @@ -46,5 +46,86 @@ control logs. License agreement ================= -.. literalinclude:: ../../../LICENSE/LICENSE - :language: none +.. dropdown:: License agreement for Matplotlib versions 1.3.0 and later + :open: + :class-container: sdd + + .. literalinclude:: ../../../LICENSE/LICENSE + :language: none + + + +Bundled software +================ + +.. dropdown:: JSX Tools Resize Observer + :class-container: sdd + + .. literalinclude:: ../../../LICENSE/LICENSE_JSXTOOLS_RESIZE_OBSERVER + :language: none + +.. dropdown:: QT4 Editor + :class-container: sdd + + .. literalinclude:: ../../../LICENSE/LICENSE_QT4_EDITOR + :language: none + + +.. _licenses-cmaps-styles: + +Colormaps and themes +-------------------- + +.. dropdown:: ColorBrewer + :class-container: sdd + + .. literalinclude:: ../../../LICENSE/LICENSE_COLORBREWER + :language: none + +.. dropdown:: Solarized + :class-container: sdd + + .. literalinclude:: ../../../LICENSE/LICENSE_SOLARIZED + :language: none + +.. dropdown:: Yorick + :class-container: sdd + + .. literalinclude:: ../../../LICENSE/LICENSE_YORICK + :language: none + + +.. _licenses-fonts: + +Fonts +----- + +.. dropdown:: American Mathematical Society (AMS) fonts + :class-container: sdd + + .. literalinclude:: ../../../LICENSE/LICENSE_AMSFONTS + :language: none + +.. dropdown:: BaKoMa + :class-container: sdd + + .. literalinclude:: ../../../LICENSE/LICENSE_BAKOMA + :language: none + +.. dropdown:: Carlogo + :class-container: sdd + + .. literalinclude:: ../../../LICENSE/LICENSE_CARLOGO + :language: none + +.. dropdown:: Courier 10 + :class-container: sdd + + .. literalinclude:: ../../../LICENSE/LICENSE_COURIERTEN + :language: none + +.. dropdown:: STIX + :class-container: sdd + + .. literalinclude:: ../../../LICENSE/LICENSE_STIX + :language: none From c143710e08ee95aa6c4cd5bd32a6605f843b1ac6 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 8 Nov 2023 19:41:23 -0500 Subject: [PATCH 12/87] Backport PR #27290: Ensure GIL while releasing buffer --- src/_macosx.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/_macosx.m b/src/_macosx.m index 6df00d0eca8e..a580362f676f 100755 --- a/src/_macosx.m +++ b/src/_macosx.m @@ -1132,8 +1132,10 @@ - (void)setCanvas: (PyObject*)newCanvas } static void _buffer_release(void* info, const void* data, size_t size) { + PyGILState_STATE gstate = PyGILState_Ensure(); PyBuffer_Release((Py_buffer *)info); free(info); + PyGILState_Release(gstate); } static int _copy_agg_buffer(CGContextRef cr, PyObject *renderer) From d88a3d347aeb9aa3621eeaf5b689ffe6fbc4162d Mon Sep 17 00:00:00 2001 From: Oscar Gustafsson Date: Thu, 9 Nov 2023 10:03:51 +0100 Subject: [PATCH 13/87] Backport PR #27291: Expand 3D import to handle any exception not just ImportError --- lib/matplotlib/projections/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/projections/__init__.py b/lib/matplotlib/projections/__init__.py index 8ce118986065..4c5ef8e2508d 100644 --- a/lib/matplotlib/projections/__init__.py +++ b/lib/matplotlib/projections/__init__.py @@ -58,7 +58,7 @@ try: from mpl_toolkits.mplot3d import Axes3D -except ImportError: +except Exception: import warnings warnings.warn("Unable to import Axes3D. This may be due to multiple versions of " "Matplotlib being installed (e.g. as a system package and as a pip " From 62364314d3d4a9d27d82d08fe09cba2f05e80988 Mon Sep 17 00:00:00 2001 From: Oscar Gustafsson Date: Mon, 13 Nov 2023 12:35:22 +0100 Subject: [PATCH 14/87] Backport PR #27312: Doc: Step redirect --- galleries/plot_types/basic/stairs.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/galleries/plot_types/basic/stairs.py b/galleries/plot_types/basic/stairs.py index e8ca455c0e2f..9bc5d025f1e1 100644 --- a/galleries/plot_types/basic/stairs.py +++ b/galleries/plot_types/basic/stairs.py @@ -4,6 +4,8 @@ ============== See `~matplotlib.axes.Axes.stairs`. + +.. redirect-from:: /plot_types/basic/step """ import matplotlib.pyplot as plt import numpy as np From c1349f6f27d78eb05750ba540328b8f985a006ac Mon Sep 17 00:00:00 2001 From: Oscar Gustafsson Date: Mon, 13 Nov 2023 12:35:22 +0100 Subject: [PATCH 15/87] Backport PR #27312: Doc: Step redirect --- galleries/plot_types/basic/stairs.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/galleries/plot_types/basic/stairs.py b/galleries/plot_types/basic/stairs.py index e8ca455c0e2f..9bc5d025f1e1 100644 --- a/galleries/plot_types/basic/stairs.py +++ b/galleries/plot_types/basic/stairs.py @@ -4,6 +4,8 @@ ============== See `~matplotlib.axes.Axes.stairs`. + +.. redirect-from:: /plot_types/basic/step """ import matplotlib.pyplot as plt import numpy as np From 2b587481f9e93461f770d63c5e7f5759b662a27a Mon Sep 17 00:00:00 2001 From: hannah Date: Tue, 14 Nov 2023 12:40:19 -0500 Subject: [PATCH 16/87] Backport PR #27323: [DOC] Minor fixes for savefig-docstring --- lib/matplotlib/figure.py | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index b2d75bcdd736..e8a5f154d593 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -3237,10 +3237,10 @@ def savefig(self, fname, *, transparent=None, **kwargs): Call signature:: - savefig(fname, *, dpi='figure', format=None, metadata=None, - bbox_inches=None, pad_inches=0.1, - facecolor='auto', edgecolor='auto', - backend=None, **kwargs + savefig(fname, *, transparent=None, dpi='figure', format=None, + metadata=None, bbox_inches=None, pad_inches=0.1, + facecolor='auto', edgecolor='auto', backend=None, + **kwargs ) The available output formats depend on the backend being used. @@ -3265,6 +3265,22 @@ def savefig(self, fname, *, transparent=None, **kwargs): Other Parameters ---------------- + transparent : bool, default: :rc:`savefig.transparent` + If *True*, the Axes patches will all be transparent; the + Figure patch will also be transparent unless *facecolor* + and/or *edgecolor* are specified via kwargs. + + If *False* has no effect and the color of the Axes and + Figure patches are unchanged (unless the Figure patch + is specified via the *facecolor* and/or *edgecolor* keyword + arguments in which case those colors are used). + + The transparency of these patches will be restored to their + original values upon exit of this function. + + This is useful, for example, for displaying + a plot on top of a colored background on a web page. + dpi : float or 'figure', default: :rc:`savefig.dpi` The resolution in dots per inch. If 'figure', use the figure's dpi value. @@ -3324,22 +3340,6 @@ def savefig(self, fname, *, transparent=None, **kwargs): 'a10', 'b0' through 'b10'. Only supported for postscript output. - transparent : bool - If *True*, the Axes patches will all be transparent; the - Figure patch will also be transparent unless *facecolor* - and/or *edgecolor* are specified via kwargs. - - If *False* has no effect and the color of the Axes and - Figure patches are unchanged (unless the Figure patch - is specified via the *facecolor* and/or *edgecolor* keyword - arguments in which case those colors are used). - - The transparency of these patches will be restored to their - original values upon exit of this function. - - This is useful, for example, for displaying - a plot on top of a colored background on a web page. - bbox_extra_artists : list of `~matplotlib.artist.Artist`, optional A list of extra artists that will be considered when the tight bbox is calculated. From 26fd30a34f26009ef7b8a3c4f623d87b8829e31e Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 16 Nov 2023 18:58:32 -0500 Subject: [PATCH 17/87] Backport PR #27334: Omit MOVETO lines from nearest contour logic --- lib/matplotlib/contour.py | 21 ++++++++++++--------- lib/matplotlib/tests/test_contour.py | 19 +++++++++++++++++++ 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/lib/matplotlib/contour.py b/lib/matplotlib/contour.py index f5ac14dff34d..6e889968642f 100644 --- a/lib/matplotlib/contour.py +++ b/lib/matplotlib/contour.py @@ -1392,15 +1392,18 @@ def _find_nearest_contour(self, xy, indices=None): for idx_level in indices: path = self._paths[idx_level] - if not len(path.vertices): - continue - lc = self.get_transform().transform(path.vertices) - d2, proj, leg = _find_closest_point_on_path(lc, xy) - if d2 < d2min: - d2min = d2 - idx_level_min = idx_level - idx_vtx_min = leg[1] - proj_min = proj + idx_vtx_start = 0 + for subpath in path._iter_connected_components(): + if not len(subpath.vertices): + continue + lc = self.get_transform().transform(subpath.vertices) + d2, proj, leg = _find_closest_point_on_path(lc, xy) + if d2 < d2min: + d2min = d2 + idx_level_min = idx_level + idx_vtx_min = leg[1] + idx_vtx_start + proj_min = proj + idx_vtx_start += len(subpath) return idx_level_min, idx_vtx_min, proj_min diff --git a/lib/matplotlib/tests/test_contour.py b/lib/matplotlib/tests/test_contour.py index db8ef03925cd..f79584be4086 100644 --- a/lib/matplotlib/tests/test_contour.py +++ b/lib/matplotlib/tests/test_contour.py @@ -125,6 +125,25 @@ def test_contour_manual_labels(split_collections): plt.clabel(cs, manual=pts, fontsize='small', colors=('r', 'g')) +def test_contour_manual_moveto(): + x = np.linspace(-10, 10) + y = np.linspace(-10, 10) + + X, Y = np.meshgrid(x, y) + + Z = X**2 * 1 / Y**2 - 1 + + contours = plt.contour(X, Y, Z, levels=[0, 100]) + + # This point lies on the `MOVETO` line for the 100 contour + # but is actually closest to the 0 contour + point = (1.3, 1) + clabels = plt.clabel(contours, manual=[point]) + + # Ensure that the 0 contour was chosen, not the 100 contour + assert clabels[0].get_text() == "0" + + @pytest.mark.parametrize("split_collections", [False, True]) @image_comparison(['contour_disconnected_segments'], remove_text=True, style='mpl20', extensions=['png']) From cfb494f815ec9ee1318c8e810dbaf10d3be5112f Mon Sep 17 00:00:00 2001 From: Kyle Sunden Date: Thu, 16 Nov 2023 18:40:44 -0600 Subject: [PATCH 18/87] Backport PR #27299: [MNT] swap xkcd script for humor sans --- .circleci/config.yml | 6 +++--- .devcontainer/devcontainer.json | 2 +- doc/devel/dependencies.rst | 3 +-- lib/matplotlib/mpl-data/matplotlibrc | 2 +- lib/matplotlib/mpl-data/stylelib/classic.mplstyle | 2 +- lib/matplotlib/pyplot.py | 12 ++++++------ lib/matplotlib/tests/test_axes.py | 2 +- lib/matplotlib/textpath.py | 2 +- 8 files changed, 15 insertions(+), 16 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8f1f3d6e313d..0668f436ddac 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -59,22 +59,22 @@ commands: graphviz \ fonts-crosextra-carlito \ fonts-freefont-otf \ - fonts-humor-sans \ fonts-noto-cjk \ optipng fonts-install: steps: - restore_cache: - key: fonts-2 + key: fonts-4 - run: name: Install custom fonts command: | mkdir -p ~/.local/share/fonts wget -nc https://github.com/google/fonts/blob/master/ofl/felipa/Felipa-Regular.ttf?raw=true -O ~/.local/share/fonts/Felipa-Regular.ttf || true + wget -nc https://github.com/ipython/xkcd-font/blob/master/xkcd-script/font/xkcd-script.ttf?raw=true -O ~/.local/share/fonts/xkcd-Script.ttf || true fc-cache -f -v - save_cache: - key: fonts-2 + key: fonts-4 paths: - ~/.local/share/fonts/ diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 814c066c43b1..ddec2754d03a 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -7,7 +7,7 @@ "features": { "ghcr.io/devcontainers/features/desktop-lite:1": {}, "ghcr.io/rocker-org/devcontainer-features/apt-packages:1": { - "packages": "inkscape,ffmpeg,dvipng,lmodern,cm-super,texlive-latex-base,texlive-latex-extra,texlive-fonts-recommended,texlive-latex-recommended,texlive-pictures,texlive-xetex,fonts-wqy-zenhei,graphviz,fonts-crosextra-carlito,fonts-freefont-otf,fonts-humor-sans,fonts-noto-cjk,optipng" + "packages": "inkscape,ffmpeg,dvipng,lmodern,cm-super,texlive-latex-base,texlive-latex-extra,texlive-fonts-recommended,texlive-latex-recommended,texlive-pictures,texlive-xetex,fonts-wqy-zenhei,graphviz,fonts-crosextra-carlito,fonts-freefont-otf,fonts-comic-neue,fonts-noto-cjk,optipng" } }, "onCreateCommand": ".devcontainer/setup.sh", diff --git a/doc/devel/dependencies.rst b/doc/devel/dependencies.rst index 400bc63d42ac..7c14d90180d6 100644 --- a/doc/devel/dependencies.rst +++ b/doc/devel/dependencies.rst @@ -399,8 +399,7 @@ Optional, but recommended: * `Inkscape `_ * `optipng `_ -* the font "Humor Sans" (aka the "XKCD" font), or the free alternative - `Comic Neue `_ +* the font `xkcd script `_ or `Comic Neue `_ * the font "Times New Roman" .. note:: diff --git a/lib/matplotlib/mpl-data/matplotlibrc b/lib/matplotlib/mpl-data/matplotlibrc index 2c53651da3d6..5014aa448968 100644 --- a/lib/matplotlib/mpl-data/matplotlibrc +++ b/lib/matplotlib/mpl-data/matplotlibrc @@ -269,7 +269,7 @@ #font.serif: DejaVu Serif, Bitstream Vera Serif, Computer Modern Roman, New Century Schoolbook, Century Schoolbook L, Utopia, ITC Bookman, Bookman, Nimbus Roman No9 L, Times New Roman, Times, Palatino, Charter, serif #font.sans-serif: DejaVu Sans, Bitstream Vera Sans, Computer Modern Sans Serif, Lucida Grande, Verdana, Geneva, Lucid, Arial, Helvetica, Avant Garde, sans-serif #font.cursive: Apple Chancery, Textile, Zapf Chancery, Sand, Script MT, Felipa, Comic Neue, Comic Sans MS, cursive -#font.fantasy: Chicago, Charcoal, Impact, Western, Humor Sans, xkcd, fantasy +#font.fantasy: Chicago, Charcoal, Impact, Western, xkcd script, fantasy #font.monospace: DejaVu Sans Mono, Bitstream Vera Sans Mono, Computer Modern Typewriter, Andale Mono, Nimbus Mono L, Courier New, Courier, Fixed, Terminal, monospace diff --git a/lib/matplotlib/mpl-data/stylelib/classic.mplstyle b/lib/matplotlib/mpl-data/stylelib/classic.mplstyle index 09a38df282f1..e1768e5a6145 100644 --- a/lib/matplotlib/mpl-data/stylelib/classic.mplstyle +++ b/lib/matplotlib/mpl-data/stylelib/classic.mplstyle @@ -91,7 +91,7 @@ font.size : 12.0 font.serif : DejaVu Serif, New Century Schoolbook, Century Schoolbook L, Utopia, ITC Bookman, Bookman, Nimbus Roman No9 L, Times New Roman, Times, Palatino, Charter, serif font.sans-serif: DejaVu Sans, Lucida Grande, Verdana, Geneva, Lucid, Arial, Helvetica, Avant Garde, sans-serif font.cursive : Apple Chancery, Textile, Zapf Chancery, Sand, Script MT, Felipa, cursive -font.fantasy : Comic Sans MS, Chicago, Charcoal, ImpactWestern, Humor Sans, fantasy +font.fantasy : Comic Sans MS, Chicago, Charcoal, ImpactWestern, xkcd script, fantasy font.monospace : DejaVu Sans Mono, Andale Mono, Nimbus Mono L, Courier New, Courier, Fixed, Terminal, monospace ### TEXT diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 42ea405c17cc..62f7eba74eea 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -707,11 +707,12 @@ def xkcd( scale: float = 1, length: float = 100, randomness: float = 2 ) -> ExitStack: """ - Turn on `xkcd `_ sketch-style drawing mode. This will - only have effect on things drawn after this function is called. + Turn on `xkcd `_ sketch-style drawing mode. - For best results, the "Humor Sans" font should be installed: it is - not included with Matplotlib. + This will only have an effect on things drawn after this function is called. + + For best results, install the `xkcd script `_ + font; xkcd fonts are not packaged with Matplotlib. Parameters ---------- @@ -750,8 +751,7 @@ def xkcd( from matplotlib import patheffects rcParams.update({ - 'font.family': ['xkcd', 'xkcd Script', 'Humor Sans', 'Comic Neue', - 'Comic Sans MS'], + 'font.family': ['xkcd', 'xkcd Script', 'Comic Neue', 'Comic Sans MS'], 'font.size': 14.0, 'path.sketch': (scale, length, randomness), 'path.effects': [ diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 6c5dd712b7fd..edc040eff3dd 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -8813,7 +8813,7 @@ def test_tick_param_labelfont(): fig, ax = plt.subplots() ax.plot([1, 2, 3, 4], [1, 2, 3, 4]) ax.set_xlabel('X label in Impact font', fontname='Impact') - ax.set_ylabel('Y label in Humor Sans', fontname='Humor Sans') + ax.set_ylabel('Y label in xkcd script', fontname='xkcd script') ax.tick_params(color='r', labelfontfamily='monospace') plt.title('Title in sans-serif') for text in ax.get_xticklabels(): diff --git a/lib/matplotlib/textpath.py b/lib/matplotlib/textpath.py index de97899f8a72..c00966d6e6c3 100644 --- a/lib/matplotlib/textpath.py +++ b/lib/matplotlib/textpath.py @@ -99,7 +99,7 @@ def get_text_path(self, prop, s, ismath=False): from matplotlib.text import TextToPath from matplotlib.font_manager import FontProperties - fp = FontProperties(family="Humor Sans", style="italic") + fp = FontProperties(family="Comic Neue", style="italic") verts, codes = TextToPath().get_text_path(fp, "ABC") path = Path(verts, codes, closed=False) From dfb7a469d6e3e398ae87de48b284c5c29b7715d5 Mon Sep 17 00:00:00 2001 From: Kyle Sunden Date: Fri, 17 Nov 2023 11:55:15 -0600 Subject: [PATCH 19/87] Github stats fo v3.8.2 --- doc/users/github_stats.rst | 182 ++++-------------- doc/users/installing/index.rst | 2 +- .../prev_whats_new/github_stats_3.8.1.rst | 168 ++++++++++++++++ doc/users/release_notes.rst | 1 + 4 files changed, 208 insertions(+), 145 deletions(-) create mode 100644 doc/users/prev_whats_new/github_stats_3.8.1.rst diff --git a/doc/users/github_stats.rst b/doc/users/github_stats.rst index d521df2080fb..ab2660464ceb 100644 --- a/doc/users/github_stats.rst +++ b/doc/users/github_stats.rst @@ -1,171 +1,65 @@ .. _github-stats: -GitHub statistics for 3.8.1 (Oct 31, 2023) +GitHub statistics for 3.8.2 (Nov 17, 2023) ========================================== -GitHub statistics for 2023/09/15 (tag: v3.8.0) - 2023/10/31 +GitHub statistics for 2023/10/31 (tag: v3.8.1) - 2023/11/17 These lists are automatically generated, and may be incomplete or contain duplicates. -We closed 24 issues and merged 95 pull requests. -The full list can be seen `on GitHub `__ +We closed 3 issues and merged 27 pull requests. +The full list can be seen `on GitHub `__ -The following 27 authors contributed 165 commits. +The following 10 authors contributed 39 commits. -* 0taj * Antony Lee -* Anvi Verma -* Artyom Romanov -* Augusto Borges -* Chiraag Balu -* David Stansby -* dependabot[bot] +* dohyun * Elliott Sales de Andrade -* Eric Firing -* Gaurav-Kumar-Soni -* Greg Lucas -* Gurudatta Shanbhag * hannah -* Hugues Hoppe * Jody Klymak -* Joshua Stevenson -* Junpei Ota -* katotaisei * Kyle Sunden -* Lucia Korpas -* Matthew Morrison * Oscar Gustafsson * Ruth Comer * Thomas A Caswell * Tim Hoffmann -* wemi3 GitHub issues and pull requests: -Pull Requests (95): +Pull Requests (27): -* :ghpull:`27239`: Backport PR #27237 on branch v3.8.x (DOC: Add command to install appropriate ``requirements.txt`` during dev venv setup) -* :ghpull:`27238`: Backport PR #27165 on branch v3.8.x (Fixing Matplotlib Notebook Text) -* :ghpull:`27165`: Fixing Matplotlib Notebook Text -* :ghpull:`27229`: Backport PR #27226 on branch v3.8.x (DOC: link out to troubleshooting guide in install) -* :ghpull:`27226`: DOC: link out to troubleshooting guide in install -* :ghpull:`27227`: Backport PR #27221 on branch v3.8.x (FIX: Enable interrupts on macosx event loops) -* :ghpull:`27221`: FIX: Enable interrupts on macosx event loops -* :ghpull:`27220`: Backport PR #27217 on branch v3.8.x: Fix type hints for undeprecated contour APIs -* :ghpull:`27217`: Fix type hints for undeprecated contour APIs -* :ghpull:`27212`: Backport PR #27088 on branch v3.8.x (Update ``find_nearest_contour`` and revert contour deprecations) -* :ghpull:`27207`: Backport PR #26970 on branch v3.8.x (FIX: Add PyOS_InputHook back to macos backend) -* :ghpull:`27088`: Update ``find_nearest_contour`` and revert contour deprecations -* :ghpull:`27206`: Backport PR #27205 on branch v3.8.x (Improve legend picking example) -* :ghpull:`26970`: FIX: Add PyOS_InputHook back to macos backend -* :ghpull:`27205`: Improve legend picking example -* :ghpull:`27202`: Backport PR #27178 on branch v3.8.x (Try/except import of Axes3D) -* :ghpull:`27178`: Try/except import of Axes3D -* :ghpull:`27201`: Backport PR #27179 on branch v3.8.x (Restore default behavior of hexbin mincnt with C provided) -* :ghpull:`27197`: Backport PR #27045 on branch v3.8.x (Ensure valid path mangling for ContourLabeler) -* :ghpull:`27179`: Restore default behavior of hexbin mincnt with C provided -* :ghpull:`27045`: Ensure valid path mangling for ContourLabeler -* :ghpull:`27191`: Backport PR #27189 on branch v3.8.x (Fix typo in docstring of ``matplotlib.colors.from_levels_and_colors``) -* :ghpull:`27189`: Fix typo in docstring of ``matplotlib.colors.from_levels_and_colors`` -* :ghpull:`27154`: Backport PR #27153 on branch v3.8.x (Link xkcd color survey in named colors example) -* :ghpull:`27133`: Backport PR #27132 on branch v3.8.x (changed automated tests from subsection to section in workflow) -* :ghpull:`27131`: Backport PR #27118 on branch v3.8.x (Update developer release guide to follow conventions) -* :ghpull:`27118`: Update developer release guide to follow conventions -* :ghpull:`27122`: Backport PR #26930 on branch v3.8.x (Added documentation on getting full list of registered colormaps re: issue #26244) -* :ghpull:`26930`: Added documentation on getting full list of registered colormaps re: issue #26244 -* :ghpull:`27113`: Backport PR #27039 on branch v3.8.x (Formatted docs) -* :ghpull:`27039`: Formatted release note docs -* :ghpull:`27101`: Backport PR #27096 on branch v3.8.x (make fonts.py, mathtext.py, text_intro.py confirm to docs guidelines) -* :ghpull:`27097`: Backport PR #27093 on branch v3.8.x ([Doc]: Move Automated Tests section to workflow docs #26998) -* :ghpull:`27065`: Backport PR #26943 on branch v3.8.x (ci: Run mypy against typed cycler) -* :ghpull:`26943`: ci: Run mypy against typed cycler -* :ghpull:`27060`: Backport PR #27059: ci: Clean up Python 3.12 builds -* :ghpull:`27057`: Backport PR #27040 on branch v3.8.x (Bump pypa/cibuildwheel from 2.16.1 to 2.16.2) -* :ghpull:`27059`: ci: Clean up Python 3.12 builds -* :ghpull:`27055`: Backport PR #27054 on branch v3.8.x (updated interactive.rst) -* :ghpull:`27052`: Backport PR #27036 on branch v3.8.x (updated artist_intro.rst) -* :ghpull:`27051`: Backport PR #26995 on branch v3.8.x (user/project/citing updated) -* :ghpull:`27046`: Backport PR #27043 on branch v3.8.x (updated api_interfaces.rst) -* :ghpull:`27040`: Bump pypa/cibuildwheel from 2.16.1 to 2.16.2 -* :ghpull:`27041`: Backport PR #26908 on branch v3.8.x (``allsegs`` and ``allkinds`` return individual segments) -* :ghpull:`26908`: ``allsegs`` and ``allkinds`` return individual segments -* :ghpull:`27034`: Backport PR #27017 on branch v3.8.x (DOC: clarify usetex versus mathtext) -* :ghpull:`27017`: DOC: clarify usetex versus mathtext -* :ghpull:`27031`: Backport PR #27015 on branch v3.8.x (ValueError exception added to handle mix of {} and % string in colorbar format) -* :ghpull:`27015`: ValueError exception added to handle mix of {} and % string in colorbar format -* :ghpull:`27022`: BLD: Remove development dependencies from sdists -* :ghpull:`27023`: Backport PR #26883 on branch v3.8.x ([TYP] Type changes from running against Pandas) -* :ghpull:`26883`: [TYP] Type changes from running against Pandas -* :ghpull:`27018`: Backport PR #26961 on branch v3.8.x (DOC: made "open PR on MPL" a section in contribute guide) -* :ghpull:`27009`: Backport PR #27006 on branch v3.8.x (DOC: Fix resizing of animation examples) -* :ghpull:`26999`: Backport PR #26940 on branch v3.8.x (Add typing to pyplot.show() to avoid errors with mypy --strict.) -* :ghpull:`27000`: Backport PR #26605 on branch v3.8.x (ci: Install GTK4 from brew on macOS) -* :ghpull:`26982`: Backport PR #26976 on branch v3.8.x (Bump pypa/cibuildwheel from 2.16.0 to 2.16.1) -* :ghpull:`26940`: Add typing to pyplot.show() to avoid errors with mypy --strict. -* :ghpull:`26997`: Backport PR #26850 on branch v3.8.x (DOC: Fix missing-reference generation on Windows) -* :ghpull:`26860`: Backport PR #26849 on branch v3.8.x (Bump setuptools required version because of setuptools_scm v8) -* :ghpull:`26850`: DOC: Fix missing-reference generation on Windows -* :ghpull:`26987`: Backport PR #26985 on branch v3.8.x (Reformatted documentation under toolkits and tutorials directory ) -* :ghpull:`26979`: Backport PR #26959 on branch v3.8.x (Move papersize="auto" deprecation to backend_bases.) -* :ghpull:`26976`: Bump pypa/cibuildwheel from 2.16.0 to 2.16.1 -* :ghpull:`26959`: Move papersize="auto" deprecation to backend_bases. -* :ghpull:`26939`: Backport PR #26937 on branch v3.8.x (Add ArrayLike to scatter c arg type hint) -* :ghpull:`26964`: Backport PR #26952 on branch v3.8.x (FIX 2-tuple of colors in to_rgba_array) -* :ghpull:`26956`: Backport PR #26955 on branch v3.8.x (Fix incorrect skip check in test_backend_ps.) -* :ghpull:`26952`: FIX 2-tuple of colors in to_rgba_array -* :ghpull:`26955`: Fix incorrect skip check in test_backend_ps. -* :ghpull:`26945`: Backport PR #26927 on branch v3.8.x ([TYP] Remove some stubtest allowlist entries) -* :ghpull:`26927`: [TYP] Remove some stubtest allowlist entries -* :ghpull:`26937`: Add ArrayLike to scatter c arg type hint -* :ghpull:`26933`: Backport PR #26914 on branch v3.8.x (DOC: add a couple more placement examples, crosslink axes_grid [ci doc]) -* :ghpull:`26849`: Bump setuptools required version because of setuptools_scm v8 -* :ghpull:`26844`: Backport PR #26843 on branch v3.8.x (DOC: Use ax.xaxis rather ax.get_xaxis()) -* :ghpull:`26836`: Backport PR #26834 on branch v3.8.x (Fix Issue 26821: [Bug]: ValueError: The truth value... when an ndarray is passed to the color kwarg of axes3d.scatter) -* :ghpull:`26834`: Fix Issue 26821: [Bug]: ValueError: The truth value... when an ndarray is passed to the color kwarg of axes3d.scatter -* :ghpull:`26835`: Backport PR #26814 on branch v3.8.x (Bump pypa/cibuildwheel from 2.15.0 to 2.16.0) -* :ghpull:`26828`: Backport PR #26825 on branch v3.8.x (Fix issue with non-string labels and legend) -* :ghpull:`26825`: Fix issue with non-string labels and legend -* :ghpull:`26814`: Bump pypa/cibuildwheel from 2.15.0 to 2.16.0 -* :ghpull:`26816`: Backport PR #26799 on branch v3.8.x (Update kiwisolver and pillow versions to be consistent with requirements) -* :ghpull:`26820`: Backport PR #26811 on branch v3.8.x (Add overload for slice to Spines.__getitem__) -* :ghpull:`26811`: Add overload for slice to Spines.__getitem__ -* :ghpull:`26799`: Update kiwisolver and pillow versions to be consistent with requirements -* :ghpull:`26809`: Backport PR #26804 on branch v3.8.x (Fix issue with locale comma when not using math text) -* :ghpull:`26789`: Backport changes to contribute from PR #26737 -* :ghpull:`26810`: Backport PR #26807 on branch v3.8.x (Catch ValueError to support pytorch (and others) plotting) -* :ghpull:`26807`: Catch ValueError to support pytorch (and others) plotting -* :ghpull:`26804`: Fix issue with locale comma when not using math text -* :ghpull:`26781`: Backport PR #26780 on branch v3.8.x (fix Axes.errorbar docstring) -* :ghpull:`26780`: fix Axes.errorbar docstring -* :ghpull:`26699`: Improve naming of cibuildwheel jobs -* :ghpull:`26605`: ci: Install GTK4 from brew on macOS +* :ghpull:`27339`: Backport PR #27299 on branch v3.8.x ([MNT] swap xkcd script for humor sans) +* :ghpull:`27338`: Backport PR #27334 on branch v3.8.x (Omit MOVETO lines from nearest contour logic) +* :ghpull:`27299`: [MNT] swap xkcd script for humor sans +* :ghpull:`27334`: Omit MOVETO lines from nearest contour logic +* :ghpull:`27324`: Backport PR #27323 on branch v3.8.x ([DOC] Minor fixes for savefig-docstring) +* :ghpull:`27323`: [DOC] Minor fixes for savefig-docstring +* :ghpull:`27314`: Backport PR #27312 on branch v3.8.x (Doc: Step redirect) +* :ghpull:`27294`: Backport PR #27291 on branch v3.8.x (Expand 3D import to handle any exception not just ImportError) +* :ghpull:`27291`: Expand 3D import to handle any exception not just ImportError +* :ghpull:`27293`: Backport PR #27290 on branch v3.8.x (Ensure GIL while releasing buffer) +* :ghpull:`27283`: Backport PR #27280 on branch v3.8.x (DOC: added rest of licenses to license page) +* :ghpull:`27280`: DOC: added rest of licenses to license page +* :ghpull:`27278`: Backport PR #27276 on branch v3.8.x (Clarify behavior of ``prune`` parameter to MaxNLocator.) +* :ghpull:`27276`: Clarify behavior of ``prune`` parameter to MaxNLocator. +* :ghpull:`27272`: Backport PR #27271 on branch v3.8.x (DOC: minor fixes to dev workflow) +* :ghpull:`27269`: Backport PR #27268 on branch v3.8.x (Copy-edit various examples.) +* :ghpull:`27263`: Backport PR #27213 on branch v3.8.x (DOC: consolidated coding guide and added naming conventions table) +* :ghpull:`27258`: Backport PR #27249 on branch v3.8.x (DOC: reasoning for communications guidelines) +* :ghpull:`27255`: Backport PR #27253 on branch v3.8.x (Copy-edit the standalone colorbar tutorial) +* :ghpull:`27253`: Copy-edit the standalone colorbar tutorial +* :ghpull:`27252`: Backport PR #26669 on branch v3.8.x ([DOC] debug backends) +* :ghpull:`26669`: [DOC] debug backends +* :ghpull:`27250`: Backport PR #27219 on branch v3.8.x (Updated axes_box_aspect.py and angle_annotation.py to regularize formatting) +* :ghpull:`27219`: Updated axes_box_aspect.py and angle_annotation.py to regularize formatting +* :ghpull:`27247`: Backport PR #26703 on branch v3.8.x (moved communications guidelines from governance, updated and clarified process ) +* :ghpull:`27246`: Backport PR #27244 on branch v3.8.x (Clarify semantics of plt.matshow(..., fignum=...).) +* :ghpull:`27244`: Clarify semantics of plt.matshow(..., fignum=...). -Issues (24): +Issues (3): -* :ghissue:`27120`: [Bug]: macosx backend pause() cannot be ctrl-c'd -* :ghissue:`27070`: [Bug]: find_nearest_contour deprecated with no replacement? -* :ghissue:`26913`: Should ``ContourSet.allsegs`` and ``.allkinds`` be deprecated? -* :ghissue:`26869`: [Bug]: Plot window not shown in Mac OS with backend set to default MacOSX -* :ghissue:`16865`: Hexbin mincnt parameter docstring should say "more than or equal to" not "more than" -* :ghissue:`27103`: [Bug]: hexbin cannot always accept np.max like functions as reduce_C_function -* :ghissue:`27062`: [Bug]: ContourLabeler.clabel with manual != False breaks unconnected contours -* :ghissue:`26971`: [Bug]: plt.clabel raises exception at very low DPI: ``ValueError: 'codes' must be a 1D list or array with the same length of 'vertices'. Your vertices have shape (2, 2) but your codes have shape (1,)`` -* :ghissue:`27188`: Small error in docstring of matplotlib.colors.from_levels_and_colors -* :ghissue:`27126`: [Bug]: LinearSegmentedColormap.from_list cannot process list with two colors -* :ghissue:`26244`: [Doc]: document how to get list of registered colormaps -* :ghissue:`26863`: [Doc]: ``ContourSet`` ``allsegs`` and ``allkinds`` after #25247 -* :ghissue:`26932`: [Bug]: Poetry installs setuptools-scm and setuptools -* :ghissue:`27007`: [Bug]: Colorbar format string kind guess could be made more robust -* :ghissue:`26919`: [Bug]: Missing file pyplot.pyi for mypy typing -* :ghissue:`26949`: [Bug]: colors.LinearSegmentedColormap.from_list does not take two tuples in 3.8.0 -* :ghissue:`26936`: [Bug/TYPE]: Scatter ``c`` Typehint does not support list of numbers when using ``cmap`` -* :ghissue:`26846`: [MNT]: setuptools-scm v8.0.1 compatibility -* :ghissue:`26821`: [Bug]: ``ValueError: The truth value...`` when an ndarray is passed to the ``color`` kwarg of ``axes3d.scatter`` -* :ghissue:`26822`: [Bug]: QuadMesh.get_array change breaks seaborn heatmap annotation -* :ghissue:`26824`: [Bug]: Legend fails for bar plot with numeric label -* :ghissue:`26808`: [Bug]: No overload variant of "__getitem__" of "Spines" matches argument type "slice" [call-overload] -* :ghissue:`26806`: [Bug]: ValueError when plotting 2D pytorch tensor using matplotlib==3.8.0 -* :ghissue:`26803`: [Bug]: use_locale leads to curly brackets around decimal separator +* :ghissue:`27333`: [Bug]: Spurious lines added with some manually add contour labels +* :ghissue:`27274`: [Bug]: prune parameter of MaxNLocator has no effect +* :ghissue:`27262`: [Bug]: Segmentation fault when resizing on Python 3.12 and MacOS 14 Previous GitHub statistics diff --git a/doc/users/installing/index.rst b/doc/users/installing/index.rst index c8c9ba549775..65fd8ee4182a 100644 --- a/doc/users/installing/index.rst +++ b/doc/users/installing/index.rst @@ -125,7 +125,7 @@ process. For example, which default backend to use, whether some of the optional libraries that Matplotlib ships with are installed, and so on. This file will be particularly useful to those packaging Matplotlib. -.. _mplsetup.cfg: https://raw.githubusercontent.com/matplotlib/matplotlib/main/mplsetup.cfg.template +.. _mplsetup.cfg: https://raw.githubusercontent.com/matplotlib/matplotlib/v3.8.x/mplsetup.cfg.template If you are building your own Matplotlib wheels (or sdists) on Windows, note that any DLLs that you copy into the source tree will be packaged too. diff --git a/doc/users/prev_whats_new/github_stats_3.8.1.rst b/doc/users/prev_whats_new/github_stats_3.8.1.rst new file mode 100644 index 000000000000..86de0e3b70a9 --- /dev/null +++ b/doc/users/prev_whats_new/github_stats_3.8.1.rst @@ -0,0 +1,168 @@ +.. _github-stats-3-8-1: + +GitHub statistics for 3.8.1 (Oct 31, 2023) +========================================== + +GitHub statistics for 2023/09/15 (tag: v3.8.0) - 2023/10/31 + +These lists are automatically generated, and may be incomplete or contain duplicates. + +We closed 24 issues and merged 95 pull requests. +The full list can be seen `on GitHub `__ + +The following 27 authors contributed 165 commits. + +* 0taj +* Antony Lee +* Anvi Verma +* Artyom Romanov +* Augusto Borges +* Chiraag Balu +* David Stansby +* dependabot[bot] +* Elliott Sales de Andrade +* Eric Firing +* Gaurav-Kumar-Soni +* Greg Lucas +* Gurudatta Shanbhag +* hannah +* Hugues Hoppe +* Jody Klymak +* Joshua Stevenson +* Junpei Ota +* katotaisei +* Kyle Sunden +* Lucia Korpas +* Matthew Morrison +* Oscar Gustafsson +* Ruth Comer +* Thomas A Caswell +* Tim Hoffmann +* wemi3 + +GitHub issues and pull requests: + +Pull Requests (95): + +* :ghpull:`27239`: Backport PR #27237 on branch v3.8.x (DOC: Add command to install appropriate ``requirements.txt`` during dev venv setup) +* :ghpull:`27238`: Backport PR #27165 on branch v3.8.x (Fixing Matplotlib Notebook Text) +* :ghpull:`27165`: Fixing Matplotlib Notebook Text +* :ghpull:`27229`: Backport PR #27226 on branch v3.8.x (DOC: link out to troubleshooting guide in install) +* :ghpull:`27226`: DOC: link out to troubleshooting guide in install +* :ghpull:`27227`: Backport PR #27221 on branch v3.8.x (FIX: Enable interrupts on macosx event loops) +* :ghpull:`27221`: FIX: Enable interrupts on macosx event loops +* :ghpull:`27220`: Backport PR #27217 on branch v3.8.x: Fix type hints for undeprecated contour APIs +* :ghpull:`27217`: Fix type hints for undeprecated contour APIs +* :ghpull:`27212`: Backport PR #27088 on branch v3.8.x (Update ``find_nearest_contour`` and revert contour deprecations) +* :ghpull:`27207`: Backport PR #26970 on branch v3.8.x (FIX: Add PyOS_InputHook back to macos backend) +* :ghpull:`27088`: Update ``find_nearest_contour`` and revert contour deprecations +* :ghpull:`27206`: Backport PR #27205 on branch v3.8.x (Improve legend picking example) +* :ghpull:`26970`: FIX: Add PyOS_InputHook back to macos backend +* :ghpull:`27205`: Improve legend picking example +* :ghpull:`27202`: Backport PR #27178 on branch v3.8.x (Try/except import of Axes3D) +* :ghpull:`27178`: Try/except import of Axes3D +* :ghpull:`27201`: Backport PR #27179 on branch v3.8.x (Restore default behavior of hexbin mincnt with C provided) +* :ghpull:`27197`: Backport PR #27045 on branch v3.8.x (Ensure valid path mangling for ContourLabeler) +* :ghpull:`27179`: Restore default behavior of hexbin mincnt with C provided +* :ghpull:`27045`: Ensure valid path mangling for ContourLabeler +* :ghpull:`27191`: Backport PR #27189 on branch v3.8.x (Fix typo in docstring of ``matplotlib.colors.from_levels_and_colors``) +* :ghpull:`27189`: Fix typo in docstring of ``matplotlib.colors.from_levels_and_colors`` +* :ghpull:`27154`: Backport PR #27153 on branch v3.8.x (Link xkcd color survey in named colors example) +* :ghpull:`27133`: Backport PR #27132 on branch v3.8.x (changed automated tests from subsection to section in workflow) +* :ghpull:`27131`: Backport PR #27118 on branch v3.8.x (Update developer release guide to follow conventions) +* :ghpull:`27118`: Update developer release guide to follow conventions +* :ghpull:`27122`: Backport PR #26930 on branch v3.8.x (Added documentation on getting full list of registered colormaps re: issue #26244) +* :ghpull:`26930`: Added documentation on getting full list of registered colormaps re: issue #26244 +* :ghpull:`27113`: Backport PR #27039 on branch v3.8.x (Formatted docs) +* :ghpull:`27039`: Formatted release note docs +* :ghpull:`27101`: Backport PR #27096 on branch v3.8.x (make fonts.py, mathtext.py, text_intro.py confirm to docs guidelines) +* :ghpull:`27097`: Backport PR #27093 on branch v3.8.x ([Doc]: Move Automated Tests section to workflow docs #26998) +* :ghpull:`27065`: Backport PR #26943 on branch v3.8.x (ci: Run mypy against typed cycler) +* :ghpull:`26943`: ci: Run mypy against typed cycler +* :ghpull:`27060`: Backport PR #27059: ci: Clean up Python 3.12 builds +* :ghpull:`27057`: Backport PR #27040 on branch v3.8.x (Bump pypa/cibuildwheel from 2.16.1 to 2.16.2) +* :ghpull:`27059`: ci: Clean up Python 3.12 builds +* :ghpull:`27055`: Backport PR #27054 on branch v3.8.x (updated interactive.rst) +* :ghpull:`27052`: Backport PR #27036 on branch v3.8.x (updated artist_intro.rst) +* :ghpull:`27051`: Backport PR #26995 on branch v3.8.x (user/project/citing updated) +* :ghpull:`27046`: Backport PR #27043 on branch v3.8.x (updated api_interfaces.rst) +* :ghpull:`27040`: Bump pypa/cibuildwheel from 2.16.1 to 2.16.2 +* :ghpull:`27041`: Backport PR #26908 on branch v3.8.x (``allsegs`` and ``allkinds`` return individual segments) +* :ghpull:`26908`: ``allsegs`` and ``allkinds`` return individual segments +* :ghpull:`27034`: Backport PR #27017 on branch v3.8.x (DOC: clarify usetex versus mathtext) +* :ghpull:`27017`: DOC: clarify usetex versus mathtext +* :ghpull:`27031`: Backport PR #27015 on branch v3.8.x (ValueError exception added to handle mix of {} and % string in colorbar format) +* :ghpull:`27015`: ValueError exception added to handle mix of {} and % string in colorbar format +* :ghpull:`27022`: BLD: Remove development dependencies from sdists +* :ghpull:`27023`: Backport PR #26883 on branch v3.8.x ([TYP] Type changes from running against Pandas) +* :ghpull:`26883`: [TYP] Type changes from running against Pandas +* :ghpull:`27018`: Backport PR #26961 on branch v3.8.x (DOC: made "open PR on MPL" a section in contribute guide) +* :ghpull:`27009`: Backport PR #27006 on branch v3.8.x (DOC: Fix resizing of animation examples) +* :ghpull:`26999`: Backport PR #26940 on branch v3.8.x (Add typing to pyplot.show() to avoid errors with mypy --strict.) +* :ghpull:`27000`: Backport PR #26605 on branch v3.8.x (ci: Install GTK4 from brew on macOS) +* :ghpull:`26982`: Backport PR #26976 on branch v3.8.x (Bump pypa/cibuildwheel from 2.16.0 to 2.16.1) +* :ghpull:`26940`: Add typing to pyplot.show() to avoid errors with mypy --strict. +* :ghpull:`26997`: Backport PR #26850 on branch v3.8.x (DOC: Fix missing-reference generation on Windows) +* :ghpull:`26860`: Backport PR #26849 on branch v3.8.x (Bump setuptools required version because of setuptools_scm v8) +* :ghpull:`26850`: DOC: Fix missing-reference generation on Windows +* :ghpull:`26987`: Backport PR #26985 on branch v3.8.x (Reformatted documentation under toolkits and tutorials directory ) +* :ghpull:`26979`: Backport PR #26959 on branch v3.8.x (Move papersize="auto" deprecation to backend_bases.) +* :ghpull:`26976`: Bump pypa/cibuildwheel from 2.16.0 to 2.16.1 +* :ghpull:`26959`: Move papersize="auto" deprecation to backend_bases. +* :ghpull:`26939`: Backport PR #26937 on branch v3.8.x (Add ArrayLike to scatter c arg type hint) +* :ghpull:`26964`: Backport PR #26952 on branch v3.8.x (FIX 2-tuple of colors in to_rgba_array) +* :ghpull:`26956`: Backport PR #26955 on branch v3.8.x (Fix incorrect skip check in test_backend_ps.) +* :ghpull:`26952`: FIX 2-tuple of colors in to_rgba_array +* :ghpull:`26955`: Fix incorrect skip check in test_backend_ps. +* :ghpull:`26945`: Backport PR #26927 on branch v3.8.x ([TYP] Remove some stubtest allowlist entries) +* :ghpull:`26927`: [TYP] Remove some stubtest allowlist entries +* :ghpull:`26937`: Add ArrayLike to scatter c arg type hint +* :ghpull:`26933`: Backport PR #26914 on branch v3.8.x (DOC: add a couple more placement examples, crosslink axes_grid [ci doc]) +* :ghpull:`26849`: Bump setuptools required version because of setuptools_scm v8 +* :ghpull:`26844`: Backport PR #26843 on branch v3.8.x (DOC: Use ax.xaxis rather ax.get_xaxis()) +* :ghpull:`26836`: Backport PR #26834 on branch v3.8.x (Fix Issue 26821: [Bug]: ValueError: The truth value... when an ndarray is passed to the color kwarg of axes3d.scatter) +* :ghpull:`26834`: Fix Issue 26821: [Bug]: ValueError: The truth value... when an ndarray is passed to the color kwarg of axes3d.scatter +* :ghpull:`26835`: Backport PR #26814 on branch v3.8.x (Bump pypa/cibuildwheel from 2.15.0 to 2.16.0) +* :ghpull:`26828`: Backport PR #26825 on branch v3.8.x (Fix issue with non-string labels and legend) +* :ghpull:`26825`: Fix issue with non-string labels and legend +* :ghpull:`26814`: Bump pypa/cibuildwheel from 2.15.0 to 2.16.0 +* :ghpull:`26816`: Backport PR #26799 on branch v3.8.x (Update kiwisolver and pillow versions to be consistent with requirements) +* :ghpull:`26820`: Backport PR #26811 on branch v3.8.x (Add overload for slice to Spines.__getitem__) +* :ghpull:`26811`: Add overload for slice to Spines.__getitem__ +* :ghpull:`26799`: Update kiwisolver and pillow versions to be consistent with requirements +* :ghpull:`26809`: Backport PR #26804 on branch v3.8.x (Fix issue with locale comma when not using math text) +* :ghpull:`26789`: Backport changes to contribute from PR #26737 +* :ghpull:`26810`: Backport PR #26807 on branch v3.8.x (Catch ValueError to support pytorch (and others) plotting) +* :ghpull:`26807`: Catch ValueError to support pytorch (and others) plotting +* :ghpull:`26804`: Fix issue with locale comma when not using math text +* :ghpull:`26781`: Backport PR #26780 on branch v3.8.x (fix Axes.errorbar docstring) +* :ghpull:`26780`: fix Axes.errorbar docstring +* :ghpull:`26699`: Improve naming of cibuildwheel jobs +* :ghpull:`26605`: ci: Install GTK4 from brew on macOS + +Issues (24): + +* :ghissue:`27120`: [Bug]: macosx backend pause() cannot be ctrl-c'd +* :ghissue:`27070`: [Bug]: find_nearest_contour deprecated with no replacement? +* :ghissue:`26913`: Should ``ContourSet.allsegs`` and ``.allkinds`` be deprecated? +* :ghissue:`26869`: [Bug]: Plot window not shown in Mac OS with backend set to default MacOSX +* :ghissue:`16865`: Hexbin mincnt parameter docstring should say "more than or equal to" not "more than" +* :ghissue:`27103`: [Bug]: hexbin cannot always accept np.max like functions as reduce_C_function +* :ghissue:`27062`: [Bug]: ContourLabeler.clabel with manual != False breaks unconnected contours +* :ghissue:`26971`: [Bug]: plt.clabel raises exception at very low DPI: ``ValueError: 'codes' must be a 1D list or array with the same length of 'vertices'. Your vertices have shape (2, 2) but your codes have shape (1,)`` +* :ghissue:`27188`: Small error in docstring of matplotlib.colors.from_levels_and_colors +* :ghissue:`27126`: [Bug]: LinearSegmentedColormap.from_list cannot process list with two colors +* :ghissue:`26244`: [Doc]: document how to get list of registered colormaps +* :ghissue:`26863`: [Doc]: ``ContourSet`` ``allsegs`` and ``allkinds`` after #25247 +* :ghissue:`26932`: [Bug]: Poetry installs setuptools-scm and setuptools +* :ghissue:`27007`: [Bug]: Colorbar format string kind guess could be made more robust +* :ghissue:`26919`: [Bug]: Missing file pyplot.pyi for mypy typing +* :ghissue:`26949`: [Bug]: colors.LinearSegmentedColormap.from_list does not take two tuples in 3.8.0 +* :ghissue:`26936`: [Bug/TYPE]: Scatter ``c`` Typehint does not support list of numbers when using ``cmap`` +* :ghissue:`26846`: [MNT]: setuptools-scm v8.0.1 compatibility +* :ghissue:`26821`: [Bug]: ``ValueError: The truth value...`` when an ndarray is passed to the ``color`` kwarg of ``axes3d.scatter`` +* :ghissue:`26822`: [Bug]: QuadMesh.get_array change breaks seaborn heatmap annotation +* :ghissue:`26824`: [Bug]: Legend fails for bar plot with numeric label +* :ghissue:`26808`: [Bug]: No overload variant of "__getitem__" of "Spines" matches argument type "slice" [call-overload] +* :ghissue:`26806`: [Bug]: ValueError when plotting 2D pytorch tensor using matplotlib==3.8.0 +* :ghissue:`26803`: [Bug]: use_locale leads to curly brackets around decimal separator diff --git a/doc/users/release_notes.rst b/doc/users/release_notes.rst index ca09d218cae4..bb0ee1e67a7e 100644 --- a/doc/users/release_notes.rst +++ b/doc/users/release_notes.rst @@ -22,6 +22,7 @@ Version 3.8 ../api/prev_api_changes/api_changes_3.8.1.rst ../api/prev_api_changes/api_changes_3.8.0.rst github_stats.rst + prev_whats_new/github_stats_3.8.1.rst prev_whats_new/github_stats_3.8.0.rst Version 3.7 From eb02b108ea181930ab37717c75e07ba792e01f1d Mon Sep 17 00:00:00 2001 From: Kyle Sunden Date: Fri, 17 Nov 2023 13:49:43 -0600 Subject: [PATCH 20/87] REL: v3.8.2 This is the second bugfix release of the 3.8 series. Highlights of this release include: - Fix a segfault in the MacOS backend when running on Python 3.12 - Fix Contour labeling manual positions selecting incorrect contours. - Various documentation improvements From 9621965f309610d148b0a8a26a53a7f8b6804acf Mon Sep 17 00:00:00 2001 From: Kyle Sunden Date: Fri, 17 Nov 2023 13:54:17 -0600 Subject: [PATCH 21/87] REL: Bump post v3.8.2 tag From d98fee6e0edee5bee74f0665472bd0c1cdfe60b6 Mon Sep 17 00:00:00 2001 From: Kyle Sunden Date: Fri, 17 Nov 2023 14:54:02 -0600 Subject: [PATCH 22/87] Update zenodo for 3.8.2 --- doc/_static/zenodo_cache/10150955.svg | 35 +++++++++++++++++++++++++++ doc/users/project/citing.rst | 3 +++ tools/cache_zenodo_svg.py | 1 + 3 files changed, 39 insertions(+) create mode 100644 doc/_static/zenodo_cache/10150955.svg diff --git a/doc/_static/zenodo_cache/10150955.svg b/doc/_static/zenodo_cache/10150955.svg new file mode 100644 index 000000000000..132bc97ab61d --- /dev/null +++ b/doc/_static/zenodo_cache/10150955.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.10150955 + + + 10.5281/zenodo.10150955 + + + \ No newline at end of file diff --git a/doc/users/project/citing.rst b/doc/users/project/citing.rst index d6b924afec2f..68a57650ea9c 100644 --- a/doc/users/project/citing.rst +++ b/doc/users/project/citing.rst @@ -29,6 +29,9 @@ By version .. START OF AUTOGENERATED +v3.8.2 + .. image:: ../../_static/zenodo_cache/10150955.svg + :target: https://doi.org/10.5281/zenodo.10150955 v3.8.1 .. image:: ../../_static/zenodo_cache/10059757.svg :target: https://doi.org/10.5281/zenodo.10059757 diff --git a/tools/cache_zenodo_svg.py b/tools/cache_zenodo_svg.py index cbd1304bd14c..5ac425b3efd9 100644 --- a/tools/cache_zenodo_svg.py +++ b/tools/cache_zenodo_svg.py @@ -63,6 +63,7 @@ def _get_xdg_cache_dir(): if __name__ == "__main__": data = { + "v3.8.2": "10150955", "v3.8.1": "10059757", "v3.8.0": "8347255", "v3.7.3": "8336761", From 58b353dc5b8f0970ace6d5172cc1ad99a5acd201 Mon Sep 17 00:00:00 2001 From: hannah Date: Mon, 20 Nov 2023 11:05:39 -0500 Subject: [PATCH 23/87] Backport PR #27348: updated api/animation documentation as per standards --- doc/api/animation_api.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/animation_api.rst b/doc/api/animation_api.rst index c7134e302d2f..df39b5942199 100644 --- a/doc/api/animation_api.rst +++ b/doc/api/animation_api.rst @@ -166,7 +166,7 @@ A third method is to use closures to build up the required artists and functions. A fourth method is to create a class. Examples -~~~~~~~~ +^^^^^^^^ * :doc:`../gallery/animation/animate_decay` * :doc:`../gallery/animation/bayes_update` @@ -182,7 +182,7 @@ Examples ------------------- Examples -~~~~~~~~ +^^^^^^^^ * :doc:`../gallery/animation/dynamic_image` From 372948e69685c642d802f2e3a98753a790b39923 Mon Sep 17 00:00:00 2001 From: hannah Date: Mon, 20 Nov 2023 11:05:39 -0500 Subject: [PATCH 24/87] Backport PR #27348: updated api/animation documentation as per standards --- doc/api/animation_api.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/animation_api.rst b/doc/api/animation_api.rst index c7134e302d2f..df39b5942199 100644 --- a/doc/api/animation_api.rst +++ b/doc/api/animation_api.rst @@ -166,7 +166,7 @@ A third method is to use closures to build up the required artists and functions. A fourth method is to create a class. Examples -~~~~~~~~ +^^^^^^^^ * :doc:`../gallery/animation/animate_decay` * :doc:`../gallery/animation/bayes_update` @@ -182,7 +182,7 @@ Examples ------------------- Examples -~~~~~~~~ +^^^^^^^^ * :doc:`../gallery/animation/dynamic_image` From 9501407646aa0e76ba42361b58b3585b451b849d Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Fri, 24 Nov 2023 00:45:33 -0500 Subject: [PATCH 25/87] Backport PR #27365: [DOC]: Fix menu example --- galleries/examples/widgets/menu.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/galleries/examples/widgets/menu.py b/galleries/examples/widgets/menu.py index b8f5268a6477..8d3db3d1b9c3 100644 --- a/galleries/examples/widgets/menu.py +++ b/galleries/examples/widgets/menu.py @@ -3,13 +3,12 @@ Menu ==== +Using texts to construct a simple menu. """ - import matplotlib.pyplot as plt import matplotlib.artist as artist import matplotlib.patches as patches -from matplotlib.transforms import IdentityTransform class ItemProperties: @@ -22,8 +21,8 @@ def __init__(self, fontsize=14, labelcolor='black', bgcolor='yellow', class MenuItem(artist.Artist): - padx = 5 - pady = 5 + padx = 0.05 # inches + pady = 0.05 def __init__(self, fig, labelstr, props=None, hoverprops=None, on_select=None): @@ -41,14 +40,16 @@ def __init__(self, fig, labelstr, props=None, hoverprops=None, self.on_select = on_select - # Setting the transform to IdentityTransform() lets us specify - # coordinates directly in pixels. - self.label = fig.text(0, 0, labelstr, transform=IdentityTransform(), + # specify coordinates in inches. + self.label = fig.text(0, 0, labelstr, transform=fig.dpi_scale_trans, size=props.fontsize) self.text_bbox = self.label.get_window_extent( fig.canvas.get_renderer()) + self.text_bbox = fig.dpi_scale_trans.inverted().transform_bbox(self.text_bbox) - self.rect = patches.Rectangle((0, 0), 1, 1) # Will be updated later. + self.rect = patches.Rectangle( + (0, 0), 1, 1, transform=fig.dpi_scale_trans + ) # Will be updated later. self.set_hover_props(False) @@ -63,7 +64,7 @@ def check_select(self, event): def set_extent(self, x, y, w, h, depth): self.rect.set(x=x, y=y, width=w, height=h) - self.label.set(position=(x + self.padx, y + depth + self.pady/2)) + self.label.set(position=(x + self.padx, y + depth + self.pady / 2)) self.hover = False def draw(self, renderer): @@ -97,10 +98,10 @@ def __init__(self, fig, menuitems): maxh = max(item.text_bbox.height for item in menuitems) depth = max(-item.text_bbox.y0 for item in menuitems) - x0 = 100 - y0 = 400 + x0 = 1 + y0 = 4 - width = maxw + 2*MenuItem.padx + width = maxw + 2 * MenuItem.padx height = maxh + MenuItem.pady for item in menuitems: From 696ac947f2c888e4af82583ebaed1f754853462b Mon Sep 17 00:00:00 2001 From: Samuel Diebolt Date: Mon, 27 Nov 2023 17:22:21 +0100 Subject: [PATCH 26/87] Backport PR #27376: [MNT] fix type annotations of `fignum_exists` --- lib/matplotlib/pyplot.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 62f7eba74eea..42c177490cbb 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -1000,9 +1000,24 @@ def gcf() -> Figure: return figure() -def fignum_exists(num: int) -> bool: - """Return whether the figure with the given id exists.""" - return _pylab_helpers.Gcf.has_fignum(num) or num in get_figlabels() +def fignum_exists(num: int | str) -> bool: + """Return whether the figure with the given id exists. + + Parameters + ---------- + num : int or str + A figure identifier. + + Returns + ------- + bool + Whether or not a figure with id *num* exists. + """ + return ( + _pylab_helpers.Gcf.has_fignum(num) + if isinstance(num, int) + else num in get_figlabels() + ) def get_fignums() -> list[int]: From c302598057775c75c9139cefbab886b630fecfb9 Mon Sep 17 00:00:00 2001 From: hannah Date: Mon, 27 Nov 2023 19:46:42 -0500 Subject: [PATCH 27/87] Backport PR #27377: TST: Make test_movie_writer_invalid_path locale-agnostic --- lib/matplotlib/tests/test_animation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/tests/test_animation.py b/lib/matplotlib/tests/test_animation.py index a4de96d77b62..d026dae59533 100644 --- a/lib/matplotlib/tests/test_animation.py +++ b/lib/matplotlib/tests/test_animation.py @@ -545,9 +545,9 @@ def test_disable_cache_warning(anim): def test_movie_writer_invalid_path(anim): if sys.platform == "win32": - match_str = re.escape("[WinError 3] The system cannot find the path specified:") + match_str = r"\[WinError 3] .*'\\\\foo\\\\bar\\\\aardvark'" else: - match_str = re.escape("[Errno 2] No such file or directory: '/foo") + match_str = r"\[Errno 2] .*'/foo" with pytest.raises(FileNotFoundError, match=match_str): anim.save("/foo/bar/aardvark/thiscannotreallyexist.mp4", writer=animation.FFMpegFileWriter()) From 2783fd47c0a334f67855f74de7e5f8edfb145a00 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Thu, 30 Nov 2023 05:48:02 -0500 Subject: [PATCH 28/87] Backport PR #27386: Doc: add a "please use dev version" to top of contribute docs --- doc/devel/index.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/devel/index.rst b/doc/devel/index.rst index 9537859c107a..47ea39d29c4b 100644 --- a/doc/devel/index.rst +++ b/doc/devel/index.rst @@ -4,6 +4,15 @@ Contribute ########## +.. ifconfig:: releaselevel != 'dev' + + .. important:: + + If you plan to contribute to Matplotlib, please read the + `development version `_ + of this document as it will have the most up to date installation + instructions, workflow process, and contributing guidelines. + Thank you for your interest in helping to improve Matplotlib! There are various ways to contribute: optimizing and refactoring code, detailing unclear documentation and writing new examples, reporting and fixing bugs and requesting From 96b50413a3b04d2169469d4e488b0866341578b4 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Thu, 30 Nov 2023 05:48:02 -0500 Subject: [PATCH 29/87] Backport PR #27386: Doc: add a "please use dev version" to top of contribute docs --- doc/devel/index.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/devel/index.rst b/doc/devel/index.rst index 9537859c107a..47ea39d29c4b 100644 --- a/doc/devel/index.rst +++ b/doc/devel/index.rst @@ -4,6 +4,15 @@ Contribute ########## +.. ifconfig:: releaselevel != 'dev' + + .. important:: + + If you plan to contribute to Matplotlib, please read the + `development version `_ + of this document as it will have the most up to date installation + instructions, workflow process, and contributing guidelines. + Thank you for your interest in helping to improve Matplotlib! There are various ways to contribute: optimizing and refactoring code, detailing unclear documentation and writing new examples, reporting and fixing bugs and requesting From 85f221c6d062e4bbfc370a6cf96149f379907fa8 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Fri, 1 Dec 2023 01:53:47 -0500 Subject: [PATCH 30/87] Backport PR #27412: ci: Block PyQt6 6.6.0 on Ubuntu --- .github/workflows/tests.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0a81e7212a00..4f0a1c0196de 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -62,23 +62,30 @@ jobs: extra-requirements: '-r requirements/testing/extra.txt' CFLAGS: "-fno-lto" # Ensure that disabling LTO works. # https://github.com/matplotlib/matplotlib/pull/26052#issuecomment-1574595954 - pyqt6-ver: '!=6.5.1' + # https://www.riverbankcomputing.com/pipermail/pyqt/2023-November/045606.html + pyqt6-ver: '!=6.5.1,!=6.6.0' # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 pyside6-ver: '!=6.5.1' - os: ubuntu-20.04 python-version: '3.10' extra-requirements: '-r requirements/testing/extra.txt' # https://github.com/matplotlib/matplotlib/pull/26052#issuecomment-1574595954 - pyqt6-ver: '!=6.5.1' + # https://www.riverbankcomputing.com/pipermail/pyqt/2023-November/045606.html + pyqt6-ver: '!=6.5.1,!=6.6.0' # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 pyside6-ver: '!=6.5.1' - os: ubuntu-22.04 python-version: '3.11' + # https://www.riverbankcomputing.com/pipermail/pyqt/2023-November/045606.html + pyqt6-ver: '!=6.6.0' # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 pyside6-ver: '!=6.5.1' extra-requirements: '-r requirements/testing/extra.txt' - os: ubuntu-22.04 python-version: '3.12' + # https://www.riverbankcomputing.com/pipermail/pyqt/2023-November/045606.html + pyqt6-ver: '!=6.6.0' + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 pyside6-ver: '!=6.5.1' - os: macos-latest python-version: 3.9 From 08cc8cb26db1886507e639e4a1afb9fa275ec9f8 Mon Sep 17 00:00:00 2001 From: Matthew Morrison <120498834+mattymo30@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:55:54 -0500 Subject: [PATCH 31/87] Backport PR #27325: Fixing Sentence Case on Section Titles in users_explain --- galleries/users_explain/animations/animations.py | 4 ++-- galleries/users_explain/artists/patheffects_guide.py | 4 ++-- galleries/users_explain/axes/autoscale.py | 4 ++-- galleries/users_explain/axes/axes_ticks.py | 2 +- galleries/users_explain/axes/colorbar_placement.py | 2 +- galleries/users_explain/axes/constrainedlayout_guide.py | 2 +- galleries/users_explain/axes/legend_guide.py | 2 +- galleries/users_explain/axes/tight_layout_guide.py | 5 +++-- galleries/users_explain/colors/colorbar_only.py | 6 +++--- galleries/users_explain/colors/colormap-manipulation.py | 6 +++--- galleries/users_explain/colors/colormapnorms.py | 2 +- galleries/users_explain/colors/colormaps.py | 6 +++--- galleries/users_explain/toolkits/axes_grid.rst | 2 +- galleries/users_explain/toolkits/axisartist.rst | 6 +++--- 14 files changed, 27 insertions(+), 26 deletions(-) diff --git a/galleries/users_explain/animations/animations.py b/galleries/users_explain/animations/animations.py index b022350c8985..fb8564f8318e 100644 --- a/galleries/users_explain/animations/animations.py +++ b/galleries/users_explain/animations/animations.py @@ -20,7 +20,7 @@ import matplotlib.animation as animation # %% -# Animation Classes +# Animation classes # ================= # # The animation process in Matplotlib can be thought of in 2 different ways: @@ -158,7 +158,7 @@ def update(frame): plt.show() # %% -# Animation Writers +# Animation writers # ================= # # Animation objects can be saved to disk using various multimedia writers diff --git a/galleries/users_explain/artists/patheffects_guide.py b/galleries/users_explain/artists/patheffects_guide.py index 259261fb09d4..28a6b9dd1d03 100644 --- a/galleries/users_explain/artists/patheffects_guide.py +++ b/galleries/users_explain/artists/patheffects_guide.py @@ -64,7 +64,7 @@ # automatically followed with the "normal" effect, whereas the latter # explicitly defines the two path effects to draw. # -# Making an artist stand out +# Making an Artist stand out # -------------------------- # # One nice way of making artists visually stand out is to draw an outline in @@ -93,7 +93,7 @@ # its user interface. # # -# Greater control of the path effect artist +# Greater control of the path effect Artist # ----------------------------------------- # # As already mentioned, some of the path effects operate at a lower level diff --git a/galleries/users_explain/axes/autoscale.py b/galleries/users_explain/axes/autoscale.py index a9d6b728866c..53435f4086c9 100644 --- a/galleries/users_explain/axes/autoscale.py +++ b/galleries/users_explain/axes/autoscale.py @@ -3,8 +3,8 @@ .. _autoscale: -Autoscaling -=========== +Autoscaling Axis +================ The limits on an axis can be set manually (e.g. ``ax.set_xlim(xmin, xmax)``) or Matplotlib can set them automatically based on the data already on the axes. diff --git a/galleries/users_explain/axes/axes_ticks.py b/galleries/users_explain/axes/axes_ticks.py index aaec87c6a239..3870c26af70e 100644 --- a/galleries/users_explain/axes/axes_ticks.py +++ b/galleries/users_explain/axes/axes_ticks.py @@ -2,7 +2,7 @@ .. _user_axes_ticks: ========== -Axis Ticks +Axis ticks ========== The x and y Axis on each Axes have default tick "locators" and "formatters" diff --git a/galleries/users_explain/axes/colorbar_placement.py b/galleries/users_explain/axes/colorbar_placement.py index 1e43d4940a98..8dbc2a356cb1 100644 --- a/galleries/users_explain/axes/colorbar_placement.py +++ b/galleries/users_explain/axes/colorbar_placement.py @@ -4,7 +4,7 @@ .. redirect-from:: /gallery/subplots_axes_and_figures/colorbar_placement ================= -Placing Colorbars +Placing colorbars ================= Colorbars indicate the quantitative extent of image data. Placing in diff --git a/galleries/users_explain/axes/constrainedlayout_guide.py b/galleries/users_explain/axes/constrainedlayout_guide.py index 4581f5f67808..d96b829df372 100644 --- a/galleries/users_explain/axes/constrainedlayout_guide.py +++ b/galleries/users_explain/axes/constrainedlayout_guide.py @@ -5,7 +5,7 @@ .. _constrainedlayout_guide: ======================== -Constrained Layout Guide +Constrained layout guide ======================== Use *constrained layout* to fit plots within your figure cleanly. diff --git a/galleries/users_explain/axes/legend_guide.py b/galleries/users_explain/axes/legend_guide.py index 3b138fe8ada3..1482cdbc4a81 100644 --- a/galleries/users_explain/axes/legend_guide.py +++ b/galleries/users_explain/axes/legend_guide.py @@ -213,7 +213,7 @@ plt.show() # %% -# Legend Handlers +# Legend handlers # =============== # # In order to create legend entries, handles are given as an argument to an diff --git a/galleries/users_explain/axes/tight_layout_guide.py b/galleries/users_explain/axes/tight_layout_guide.py index 8525b9773f91..9074641d39ab 100644 --- a/galleries/users_explain/axes/tight_layout_guide.py +++ b/galleries/users_explain/axes/tight_layout_guide.py @@ -4,7 +4,7 @@ .. _tight_layout_guide: ================== -Tight Layout guide +Tight layout guide ================== How to use tight-layout to fit plots within your figure cleanly. @@ -17,6 +17,7 @@ An alternative to *tight_layout* is :ref:`constrained_layout `. + Simple example ============== @@ -209,7 +210,7 @@ def example_plot(ax, fontsize=12): # %% -# Legends and Annotations +# Legends and annotations # ======================= # # Pre Matplotlib 2.2, legends and annotations were excluded from the bounding diff --git a/galleries/users_explain/colors/colorbar_only.py b/galleries/users_explain/colors/colorbar_only.py index a47ced0a4ea6..4140ea454b99 100644 --- a/galleries/users_explain/colors/colorbar_only.py +++ b/galleries/users_explain/colors/colorbar_only.py @@ -1,9 +1,9 @@ """ .. redirect-from:: /tutorials/colors/colorbar_only -==================== -Standalone colorbars -==================== +============================= +Customized Colorbars Tutorial +============================= This tutorial shows how to build and customize standalone colorbars, i.e. without an attached plot. diff --git a/galleries/users_explain/colors/colormap-manipulation.py b/galleries/users_explain/colors/colormap-manipulation.py index 87269b87befa..0cd488857257 100644 --- a/galleries/users_explain/colors/colormap-manipulation.py +++ b/galleries/users_explain/colors/colormap-manipulation.py @@ -3,9 +3,9 @@ .. _colormap-manipulation: -****************** -Creating Colormaps -****************** +******************************** +Creating Colormaps in Matplotlib +******************************** Matplotlib has a number of built-in colormaps accessible via `.matplotlib.colormaps`. There are also external libraries like diff --git a/galleries/users_explain/colors/colormapnorms.py b/galleries/users_explain/colors/colormapnorms.py index f375b3af805b..3aa0ab729371 100644 --- a/galleries/users_explain/colors/colormapnorms.py +++ b/galleries/users_explain/colors/colormapnorms.py @@ -4,7 +4,7 @@ .. _colormapnorms: -Colormap Normalization +Colormap normalization ====================== Objects that use colormaps by default linearly map the colors in the diff --git a/galleries/users_explain/colors/colormaps.py b/galleries/users_explain/colors/colormaps.py index b5db551cb5b5..92b56d298976 100644 --- a/galleries/users_explain/colors/colormaps.py +++ b/galleries/users_explain/colors/colormaps.py @@ -3,9 +3,9 @@ .. _colormaps: -****************** -Choosing Colormaps -****************** +******************************** +Choosing Colormaps in Matplotlib +******************************** Matplotlib has a number of built-in colormaps accessible via `.matplotlib.colormaps`. There are also external libraries that diff --git a/galleries/users_explain/toolkits/axes_grid.rst b/galleries/users_explain/toolkits/axes_grid.rst index ba37c4cf7d78..7ef8b1e582b6 100644 --- a/galleries/users_explain/toolkits/axes_grid.rst +++ b/galleries/users_explain/toolkits/axes_grid.rst @@ -60,7 +60,7 @@ The examples below show what you can do with ImageGrid. :target: /gallery/axes_grid1/demo_axes_grid.html :align: center -AxesDivider Class +AxesDivider class ----------------- Behind the scenes, ImageGrid (and RGBAxes, described below) rely on diff --git a/galleries/users_explain/toolkits/axisartist.rst b/galleries/users_explain/toolkits/axisartist.rst index 9246fb27271b..ed1161fb92a7 100644 --- a/galleries/users_explain/toolkits/axisartist.rst +++ b/galleries/users_explain/toolkits/axisartist.rst @@ -99,7 +99,7 @@ Here is an example that uses ParasiteAxes. :target: /gallery/axisartist/demo_parasite_axes2.html :align: center -Curvilinear Grid +Curvilinear grid ---------------- The motivation behind the AxisArtist module is to support a curvilinear grid @@ -298,7 +298,7 @@ HowTo To change the pad between ticklabels and axis label, axis.label.set_pad method. -Rotation and Alignment of TickLabels +Rotation and alignment of TickLabels ==================================== This is also quite different from standard Matplotlib and can be @@ -547,7 +547,7 @@ way is to add it as an item of Axes's axis attribute.:: See the first example of this page. -Current Limitations and TODO's +Current limitations and TODO's ============================== The code need more refinement. Here is a incomplete list of issues and TODO's From f0d7435eb5f9f14916ebdc82934af3eeb99ab4d2 Mon Sep 17 00:00:00 2001 From: Matthew Morrison <120498834+mattymo30@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:55:54 -0500 Subject: [PATCH 32/87] Backport PR #27325: Fixing Sentence Case on Section Titles in users_explain --- galleries/users_explain/animations/animations.py | 4 ++-- galleries/users_explain/artists/patheffects_guide.py | 4 ++-- galleries/users_explain/axes/autoscale.py | 4 ++-- galleries/users_explain/axes/axes_ticks.py | 2 +- galleries/users_explain/axes/colorbar_placement.py | 2 +- galleries/users_explain/axes/constrainedlayout_guide.py | 2 +- galleries/users_explain/axes/legend_guide.py | 2 +- galleries/users_explain/axes/tight_layout_guide.py | 5 +++-- galleries/users_explain/colors/colorbar_only.py | 6 +++--- galleries/users_explain/colors/colormap-manipulation.py | 6 +++--- galleries/users_explain/colors/colormapnorms.py | 2 +- galleries/users_explain/colors/colormaps.py | 6 +++--- galleries/users_explain/toolkits/axes_grid.rst | 2 +- galleries/users_explain/toolkits/axisartist.rst | 6 +++--- 14 files changed, 27 insertions(+), 26 deletions(-) diff --git a/galleries/users_explain/animations/animations.py b/galleries/users_explain/animations/animations.py index b022350c8985..fb8564f8318e 100644 --- a/galleries/users_explain/animations/animations.py +++ b/galleries/users_explain/animations/animations.py @@ -20,7 +20,7 @@ import matplotlib.animation as animation # %% -# Animation Classes +# Animation classes # ================= # # The animation process in Matplotlib can be thought of in 2 different ways: @@ -158,7 +158,7 @@ def update(frame): plt.show() # %% -# Animation Writers +# Animation writers # ================= # # Animation objects can be saved to disk using various multimedia writers diff --git a/galleries/users_explain/artists/patheffects_guide.py b/galleries/users_explain/artists/patheffects_guide.py index 259261fb09d4..28a6b9dd1d03 100644 --- a/galleries/users_explain/artists/patheffects_guide.py +++ b/galleries/users_explain/artists/patheffects_guide.py @@ -64,7 +64,7 @@ # automatically followed with the "normal" effect, whereas the latter # explicitly defines the two path effects to draw. # -# Making an artist stand out +# Making an Artist stand out # -------------------------- # # One nice way of making artists visually stand out is to draw an outline in @@ -93,7 +93,7 @@ # its user interface. # # -# Greater control of the path effect artist +# Greater control of the path effect Artist # ----------------------------------------- # # As already mentioned, some of the path effects operate at a lower level diff --git a/galleries/users_explain/axes/autoscale.py b/galleries/users_explain/axes/autoscale.py index a9d6b728866c..53435f4086c9 100644 --- a/galleries/users_explain/axes/autoscale.py +++ b/galleries/users_explain/axes/autoscale.py @@ -3,8 +3,8 @@ .. _autoscale: -Autoscaling -=========== +Autoscaling Axis +================ The limits on an axis can be set manually (e.g. ``ax.set_xlim(xmin, xmax)``) or Matplotlib can set them automatically based on the data already on the axes. diff --git a/galleries/users_explain/axes/axes_ticks.py b/galleries/users_explain/axes/axes_ticks.py index aaec87c6a239..3870c26af70e 100644 --- a/galleries/users_explain/axes/axes_ticks.py +++ b/galleries/users_explain/axes/axes_ticks.py @@ -2,7 +2,7 @@ .. _user_axes_ticks: ========== -Axis Ticks +Axis ticks ========== The x and y Axis on each Axes have default tick "locators" and "formatters" diff --git a/galleries/users_explain/axes/colorbar_placement.py b/galleries/users_explain/axes/colorbar_placement.py index 1e43d4940a98..8dbc2a356cb1 100644 --- a/galleries/users_explain/axes/colorbar_placement.py +++ b/galleries/users_explain/axes/colorbar_placement.py @@ -4,7 +4,7 @@ .. redirect-from:: /gallery/subplots_axes_and_figures/colorbar_placement ================= -Placing Colorbars +Placing colorbars ================= Colorbars indicate the quantitative extent of image data. Placing in diff --git a/galleries/users_explain/axes/constrainedlayout_guide.py b/galleries/users_explain/axes/constrainedlayout_guide.py index 4581f5f67808..d96b829df372 100644 --- a/galleries/users_explain/axes/constrainedlayout_guide.py +++ b/galleries/users_explain/axes/constrainedlayout_guide.py @@ -5,7 +5,7 @@ .. _constrainedlayout_guide: ======================== -Constrained Layout Guide +Constrained layout guide ======================== Use *constrained layout* to fit plots within your figure cleanly. diff --git a/galleries/users_explain/axes/legend_guide.py b/galleries/users_explain/axes/legend_guide.py index 3b138fe8ada3..1482cdbc4a81 100644 --- a/galleries/users_explain/axes/legend_guide.py +++ b/galleries/users_explain/axes/legend_guide.py @@ -213,7 +213,7 @@ plt.show() # %% -# Legend Handlers +# Legend handlers # =============== # # In order to create legend entries, handles are given as an argument to an diff --git a/galleries/users_explain/axes/tight_layout_guide.py b/galleries/users_explain/axes/tight_layout_guide.py index 8525b9773f91..9074641d39ab 100644 --- a/galleries/users_explain/axes/tight_layout_guide.py +++ b/galleries/users_explain/axes/tight_layout_guide.py @@ -4,7 +4,7 @@ .. _tight_layout_guide: ================== -Tight Layout guide +Tight layout guide ================== How to use tight-layout to fit plots within your figure cleanly. @@ -17,6 +17,7 @@ An alternative to *tight_layout* is :ref:`constrained_layout `. + Simple example ============== @@ -209,7 +210,7 @@ def example_plot(ax, fontsize=12): # %% -# Legends and Annotations +# Legends and annotations # ======================= # # Pre Matplotlib 2.2, legends and annotations were excluded from the bounding diff --git a/galleries/users_explain/colors/colorbar_only.py b/galleries/users_explain/colors/colorbar_only.py index a47ced0a4ea6..4140ea454b99 100644 --- a/galleries/users_explain/colors/colorbar_only.py +++ b/galleries/users_explain/colors/colorbar_only.py @@ -1,9 +1,9 @@ """ .. redirect-from:: /tutorials/colors/colorbar_only -==================== -Standalone colorbars -==================== +============================= +Customized Colorbars Tutorial +============================= This tutorial shows how to build and customize standalone colorbars, i.e. without an attached plot. diff --git a/galleries/users_explain/colors/colormap-manipulation.py b/galleries/users_explain/colors/colormap-manipulation.py index 87269b87befa..0cd488857257 100644 --- a/galleries/users_explain/colors/colormap-manipulation.py +++ b/galleries/users_explain/colors/colormap-manipulation.py @@ -3,9 +3,9 @@ .. _colormap-manipulation: -****************** -Creating Colormaps -****************** +******************************** +Creating Colormaps in Matplotlib +******************************** Matplotlib has a number of built-in colormaps accessible via `.matplotlib.colormaps`. There are also external libraries like diff --git a/galleries/users_explain/colors/colormapnorms.py b/galleries/users_explain/colors/colormapnorms.py index f375b3af805b..3aa0ab729371 100644 --- a/galleries/users_explain/colors/colormapnorms.py +++ b/galleries/users_explain/colors/colormapnorms.py @@ -4,7 +4,7 @@ .. _colormapnorms: -Colormap Normalization +Colormap normalization ====================== Objects that use colormaps by default linearly map the colors in the diff --git a/galleries/users_explain/colors/colormaps.py b/galleries/users_explain/colors/colormaps.py index b5db551cb5b5..92b56d298976 100644 --- a/galleries/users_explain/colors/colormaps.py +++ b/galleries/users_explain/colors/colormaps.py @@ -3,9 +3,9 @@ .. _colormaps: -****************** -Choosing Colormaps -****************** +******************************** +Choosing Colormaps in Matplotlib +******************************** Matplotlib has a number of built-in colormaps accessible via `.matplotlib.colormaps`. There are also external libraries that diff --git a/galleries/users_explain/toolkits/axes_grid.rst b/galleries/users_explain/toolkits/axes_grid.rst index ba37c4cf7d78..7ef8b1e582b6 100644 --- a/galleries/users_explain/toolkits/axes_grid.rst +++ b/galleries/users_explain/toolkits/axes_grid.rst @@ -60,7 +60,7 @@ The examples below show what you can do with ImageGrid. :target: /gallery/axes_grid1/demo_axes_grid.html :align: center -AxesDivider Class +AxesDivider class ----------------- Behind the scenes, ImageGrid (and RGBAxes, described below) rely on diff --git a/galleries/users_explain/toolkits/axisartist.rst b/galleries/users_explain/toolkits/axisartist.rst index 9246fb27271b..ed1161fb92a7 100644 --- a/galleries/users_explain/toolkits/axisartist.rst +++ b/galleries/users_explain/toolkits/axisartist.rst @@ -99,7 +99,7 @@ Here is an example that uses ParasiteAxes. :target: /gallery/axisartist/demo_parasite_axes2.html :align: center -Curvilinear Grid +Curvilinear grid ---------------- The motivation behind the AxisArtist module is to support a curvilinear grid @@ -298,7 +298,7 @@ HowTo To change the pad between ticklabels and axis label, axis.label.set_pad method. -Rotation and Alignment of TickLabels +Rotation and alignment of TickLabels ==================================== This is also quite different from standard Matplotlib and can be @@ -547,7 +547,7 @@ way is to add it as an item of Axes's axis attribute.:: See the first example of this page. -Current Limitations and TODO's +Current limitations and TODO's ============================== The code need more refinement. Here is a incomplete list of issues and TODO's From 9697bbfe680e13423dcd1fba898b5cce6cbaf658 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Sat, 2 Dec 2023 14:36:16 -0800 Subject: [PATCH 33/87] Backport PR #27411: DOC: multilevel tick example --- galleries/examples/ticks/multilevel_ticks.py | 99 ++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 galleries/examples/ticks/multilevel_ticks.py diff --git a/galleries/examples/ticks/multilevel_ticks.py b/galleries/examples/ticks/multilevel_ticks.py new file mode 100644 index 000000000000..5b8d0ada5ae2 --- /dev/null +++ b/galleries/examples/ticks/multilevel_ticks.py @@ -0,0 +1,99 @@ +""" +========================= +Multilevel (nested) ticks +========================= + +Sometimes we want another level of tick labels on an axis, perhaps to indicate +a grouping of the ticks. + +Matplotlib does not provide an automated way to do this, but it is relatively +straightforward to annotate below the main axis. + +These examples use `.Axes.secondary_xaxis`, which is one approach. It has the +advantage that we can use Matplotlib Locators and Formatters on the axis that +does the grouping if we want. + +This first example creates a secondary xaxis and manually adds the ticks and +labels using `.Axes.set_xticks`. Note that the tick labels have a newline +(e.g. ``"\nOughts"``) at the beginning of them to put the second-level tick +labels below the main tick labels. +""" + +import matplotlib.pyplot as plt +import numpy as np + +import matplotlib.dates as mdates + +rng = np.random.default_rng(19680801) + +fig, ax = plt.subplots(layout='constrained', figsize=(4, 4)) + +ax.plot(np.arange(30)) + +sec = ax.secondary_xaxis(location=0) +sec.set_xticks([5, 15, 25], labels=['\nOughts', '\nTeens', '\nTwenties']) + +# %% +# This second example adds a second level of annotation to a categorical axis. +# Here we need to note that each animal (category) is assigned an integer, so +# ``cats`` is at x=0, ``dogs`` at x=1 etc. Then we place the ticks on the +# second level on an x that is at the middle of the animal class we are trying +# to delineate. +# +# This example also adds tick marks between the classes by adding a second +# secondary xaxis, and placing long, wide ticks at the boundaries between the +# animal classes. + +fig, ax = plt.subplots(layout='constrained', figsize=(7, 4)) + +ax.plot(['cats', 'dogs', 'pigs', 'snakes', 'lizards', 'chickens', + 'eagles', 'herons', 'buzzards'], + rng.normal(size=9), 'o') + +# label the classes: +sec = ax.secondary_xaxis(location=0) +sec.set_xticks([1, 3.5, 6.5], labels=['\n\nMammals', '\n\nReptiles', '\n\nBirds']) +sec.tick_params('x', length=0) + +# lines between the classes: +sec2 = ax.secondary_xaxis(location=0) +sec2.set_xticks([-0.5, 2.5, 4.5, 8.5], labels=[]) +sec2.tick_params('x', length=40, width=1.5) +ax.set_xlim(-0.6, 8.6) + +# %% +# Dates are another common place where we may want to have a second level of +# tick labels. In this last example, we take advantage of the ability to add +# an automatic locator and formatter to the secondary xaxis, which means we do +# not need to set the ticks manually. +# +# This example also differs from the above, in that we placed it at a location +# below the main axes ``location=-0.075`` and then we hide the spine by setting +# the line width to zero. That means that our formatter no longer needs the +# carriage returns of the previous two examples. + +fig, ax = plt.subplots(layout='constrained', figsize=(7, 4)) + +time = np.arange(np.datetime64('2020-01-01'), np.datetime64('2020-03-31'), + np.timedelta64(1, 'D')) + +ax.plot(time, rng.random(size=len(time))) + +# just format the days: +ax.xaxis.set_major_formatter(mdates.DateFormatter('%d')) + +# label the months: +sec = ax.secondary_xaxis(location=-0.075) +sec.xaxis.set_major_locator(mdates.MonthLocator(bymonthday=1)) + +# note the extra spaces in the label to align the month label inside the month. +# Note that this could have been done by changing ``bymonthday`` above as well: +sec.xaxis.set_major_formatter(mdates.DateFormatter(' %b')) +sec.tick_params('x', length=0) +sec.spines['bottom'].set_linewidth(0) + +# label the xaxis, but note for this to look good, it needs to be on the +# secondary xaxis. +sec.set_xlabel('Dates (2020)') + +plt.show() From c19ea29c4a07d4b187b86f3cf201f21af000fa61 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Sat, 2 Dec 2023 14:36:16 -0800 Subject: [PATCH 34/87] Backport PR #27411: DOC: multilevel tick example --- galleries/examples/ticks/multilevel_ticks.py | 99 ++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 galleries/examples/ticks/multilevel_ticks.py diff --git a/galleries/examples/ticks/multilevel_ticks.py b/galleries/examples/ticks/multilevel_ticks.py new file mode 100644 index 000000000000..5b8d0ada5ae2 --- /dev/null +++ b/galleries/examples/ticks/multilevel_ticks.py @@ -0,0 +1,99 @@ +""" +========================= +Multilevel (nested) ticks +========================= + +Sometimes we want another level of tick labels on an axis, perhaps to indicate +a grouping of the ticks. + +Matplotlib does not provide an automated way to do this, but it is relatively +straightforward to annotate below the main axis. + +These examples use `.Axes.secondary_xaxis`, which is one approach. It has the +advantage that we can use Matplotlib Locators and Formatters on the axis that +does the grouping if we want. + +This first example creates a secondary xaxis and manually adds the ticks and +labels using `.Axes.set_xticks`. Note that the tick labels have a newline +(e.g. ``"\nOughts"``) at the beginning of them to put the second-level tick +labels below the main tick labels. +""" + +import matplotlib.pyplot as plt +import numpy as np + +import matplotlib.dates as mdates + +rng = np.random.default_rng(19680801) + +fig, ax = plt.subplots(layout='constrained', figsize=(4, 4)) + +ax.plot(np.arange(30)) + +sec = ax.secondary_xaxis(location=0) +sec.set_xticks([5, 15, 25], labels=['\nOughts', '\nTeens', '\nTwenties']) + +# %% +# This second example adds a second level of annotation to a categorical axis. +# Here we need to note that each animal (category) is assigned an integer, so +# ``cats`` is at x=0, ``dogs`` at x=1 etc. Then we place the ticks on the +# second level on an x that is at the middle of the animal class we are trying +# to delineate. +# +# This example also adds tick marks between the classes by adding a second +# secondary xaxis, and placing long, wide ticks at the boundaries between the +# animal classes. + +fig, ax = plt.subplots(layout='constrained', figsize=(7, 4)) + +ax.plot(['cats', 'dogs', 'pigs', 'snakes', 'lizards', 'chickens', + 'eagles', 'herons', 'buzzards'], + rng.normal(size=9), 'o') + +# label the classes: +sec = ax.secondary_xaxis(location=0) +sec.set_xticks([1, 3.5, 6.5], labels=['\n\nMammals', '\n\nReptiles', '\n\nBirds']) +sec.tick_params('x', length=0) + +# lines between the classes: +sec2 = ax.secondary_xaxis(location=0) +sec2.set_xticks([-0.5, 2.5, 4.5, 8.5], labels=[]) +sec2.tick_params('x', length=40, width=1.5) +ax.set_xlim(-0.6, 8.6) + +# %% +# Dates are another common place where we may want to have a second level of +# tick labels. In this last example, we take advantage of the ability to add +# an automatic locator and formatter to the secondary xaxis, which means we do +# not need to set the ticks manually. +# +# This example also differs from the above, in that we placed it at a location +# below the main axes ``location=-0.075`` and then we hide the spine by setting +# the line width to zero. That means that our formatter no longer needs the +# carriage returns of the previous two examples. + +fig, ax = plt.subplots(layout='constrained', figsize=(7, 4)) + +time = np.arange(np.datetime64('2020-01-01'), np.datetime64('2020-03-31'), + np.timedelta64(1, 'D')) + +ax.plot(time, rng.random(size=len(time))) + +# just format the days: +ax.xaxis.set_major_formatter(mdates.DateFormatter('%d')) + +# label the months: +sec = ax.secondary_xaxis(location=-0.075) +sec.xaxis.set_major_locator(mdates.MonthLocator(bymonthday=1)) + +# note the extra spaces in the label to align the month label inside the month. +# Note that this could have been done by changing ``bymonthday`` above as well: +sec.xaxis.set_major_formatter(mdates.DateFormatter(' %b')) +sec.tick_params('x', length=0) +sec.spines['bottom'].set_linewidth(0) + +# label the xaxis, but note for this to look good, it needs to be on the +# secondary xaxis. +sec.set_xlabel('Dates (2020)') + +plt.show() From ab26f684a4517484db13cbc858bcde03ca1eb7dc Mon Sep 17 00:00:00 2001 From: Oscar Gustafsson Date: Tue, 5 Dec 2023 12:29:57 +0100 Subject: [PATCH 35/87] Backport PR #27441: Fix some minor issues with hexbin bins argument --- galleries/examples/statistics/hexbin_demo.py | 2 +- lib/matplotlib/axes/_axes.py | 4 ++-- lib/matplotlib/tests/test_axes.py | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/galleries/examples/statistics/hexbin_demo.py b/galleries/examples/statistics/hexbin_demo.py index 4bd0e0401424..b9a6206a934f 100644 --- a/galleries/examples/statistics/hexbin_demo.py +++ b/galleries/examples/statistics/hexbin_demo.py @@ -29,7 +29,7 @@ hb = ax1.hexbin(x, y, gridsize=50, bins='log', cmap='inferno') ax1.set(xlim=xlim, ylim=ylim) ax1.set_title("With a log color scale") -cb = fig.colorbar(hb, ax=ax1, label='log10(N)') +cb = fig.colorbar(hb, ax=ax1, label='counts') plt.show() diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index b4a41099beda..28a9a92550e8 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -5084,10 +5084,10 @@ def reduce_C_function(C: array) -> float ) # Set normalizer if bins is 'log' - if bins == 'log': + if cbook._str_equal(bins, 'log'): if norm is not None: _api.warn_external("Only one of 'bins' and 'norm' arguments " - f"can be supplied, ignoring bins={bins}") + f"can be supplied, ignoring {bins=}") else: norm = mcolors.LogNorm(vmin=vmin, vmax=vmax) vmin = vmax = None diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index edc040eff3dd..87d5e35013c0 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -956,17 +956,18 @@ def test_hexbin_extent(): ax.hexbin("x", "y", extent=[.1, .3, .6, .7], data=data) -@image_comparison(['hexbin_empty.png', 'hexbin_empty.png'], remove_text=True) +@image_comparison(['hexbin_empty.png'], remove_text=True) def test_hexbin_empty(): # From #3886: creating hexbin from empty dataset raises ValueError fig, ax = plt.subplots() ax.hexbin([], []) - fig, ax = plt.subplots() # From #23922: creating hexbin with log scaling from empty # dataset raises ValueError ax.hexbin([], [], bins='log') # From #27103: np.max errors when handed empty data ax.hexbin([], [], C=[], reduce_C_function=np.max) + # No string-comparison warning from NumPy. + ax.hexbin([], [], bins=np.arange(10)) def test_hexbin_pickable(): From 842d05f8b59b9a80ca7491801c3c28d0bb67e1dd Mon Sep 17 00:00:00 2001 From: hannah Date: Tue, 5 Dec 2023 15:39:10 -0500 Subject: [PATCH 36/87] Backport PR #27397: SpanSelector widget: Improve doc for `extents` --- lib/matplotlib/widgets.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 0a31a9dd2529..3c6208e71029 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -2915,7 +2915,12 @@ def _snap(values, snap_values): @property def extents(self): - """Return extents of the span selector.""" + """ + (float, float) + The values, in data coordinates, for the start and end points of the current + selection. If there is no selection then the start and end values will be + the same. + """ if self.direction == 'horizontal': vmin = self._selection_artist.get_x() vmax = vmin + self._selection_artist.get_width() From d3fc627077833263082abc86d525f7b44e0dc2f4 Mon Sep 17 00:00:00 2001 From: hannah Date: Wed, 6 Dec 2023 12:11:51 -0500 Subject: [PATCH 37/87] Backport PR #27434: FIX: Expand stairs plot-type entry intro (reattempt) --- galleries/plot_types/basic/stairs.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/galleries/plot_types/basic/stairs.py b/galleries/plot_types/basic/stairs.py index 9bc5d025f1e1..de5761e3a7a5 100644 --- a/galleries/plot_types/basic/stairs.py +++ b/galleries/plot_types/basic/stairs.py @@ -3,7 +3,9 @@ stairs(values) ============== -See `~matplotlib.axes.Axes.stairs`. +See `~matplotlib.axes.Axes.stairs` when plotting :math:`y` between +:math:`(x_i, x_{i+1})`. For plotting :math:`y` at :math:`x`, see +`~matplotlib.axes.Axes.step`. .. redirect-from:: /plot_types/basic/step """ From e86f487c2727040f4e67fd4b93b20f5b4f4fece5 Mon Sep 17 00:00:00 2001 From: hannah Date: Wed, 6 Dec 2023 12:11:51 -0500 Subject: [PATCH 38/87] Backport PR #27434: FIX: Expand stairs plot-type entry intro (reattempt) --- galleries/plot_types/basic/stairs.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/galleries/plot_types/basic/stairs.py b/galleries/plot_types/basic/stairs.py index 9bc5d025f1e1..de5761e3a7a5 100644 --- a/galleries/plot_types/basic/stairs.py +++ b/galleries/plot_types/basic/stairs.py @@ -3,7 +3,9 @@ stairs(values) ============== -See `~matplotlib.axes.Axes.stairs`. +See `~matplotlib.axes.Axes.stairs` when plotting :math:`y` between +:math:`(x_i, x_{i+1})`. For plotting :math:`y` at :math:`x`, see +`~matplotlib.axes.Axes.step`. .. redirect-from:: /plot_types/basic/step """ From 53279ca3322074e33d479ba93a6de7c87cf3ed95 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Thu, 7 Dec 2023 21:12:50 +0100 Subject: [PATCH 39/87] Backport PR #27316: DOC: Synchronize LICENSE_STIX files --- LICENSE/LICENSE_STIX | 193 +++++++++++++++++++++++++++---------------- 1 file changed, 123 insertions(+), 70 deletions(-) diff --git a/LICENSE/LICENSE_STIX b/LICENSE/LICENSE_STIX index 2f7aeea331ce..6034d9474814 100644 --- a/LICENSE/LICENSE_STIX +++ b/LICENSE/LICENSE_STIX @@ -1,71 +1,124 @@ -TERMS AND CONDITIONS - - 1. Permission is hereby granted, free of charge, to any person -obtaining a copy of the STIX Fonts-TM set accompanying this license -(collectively, the "Fonts") and the associated documentation files -(collectively with the Fonts, the "Font Software"), to reproduce and -distribute the Font Software, including the rights to use, copy, merge -and publish copies of the Font Software, and to permit persons to whom -the Font Software is furnished to do so same, subject to the following -terms and conditions (the "License"). - - 2. The following copyright and trademark notice and these Terms and -Conditions shall be included in all copies of one or more of the Font -typefaces and any derivative work created as permitted under this -License: - - Copyright (c) 2001-2005 by the STI Pub Companies, consisting of -the American Institute of Physics, the American Chemical Society, the -American Mathematical Society, the American Physical Society, Elsevier, -Inc., and The Institute of Electrical and Electronic Engineers, Inc. -Portions copyright (c) 1998-2003 by MicroPress, Inc. Portions copyright -(c) 1990 by Elsevier, Inc. All rights reserved. STIX Fonts-TM is a -trademark of The Institute of Electrical and Electronics Engineers, Inc. - - 3. You may (a) convert the Fonts from one format to another (e.g., -from TrueType to PostScript), in which case the normal and reasonable -distortion that occurs during such conversion shall be permitted and (b) -embed or include a subset of the Fonts in a document for the purposes of -allowing users to read text in the document that utilizes the Fonts. In -each case, you may use the STIX Fonts-TM mark to designate the resulting -Fonts or subset of the Fonts. - - 4. You may also (a) add glyphs or characters to the Fonts, or modify -the shape of existing glyphs, so long as the base set of glyphs is not -removed and (b) delete glyphs or characters from the Fonts, provided -that the resulting font set is distributed with the following -disclaimer: "This [name] font does not include all the Unicode points -covered in the STIX Fonts-TM set but may include others." In each case, -the name used to denote the resulting font set shall not include the -term "STIX" or any similar term. - - 5. You may charge a fee in connection with the distribution of the -Font Software, provided that no copy of one or more of the individual -Font typefaces that form the STIX Fonts-TM set may be sold by itself. - - 6. THE FONT SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY -KIND, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK OR OTHER RIGHT. IN NO EVENT SHALL -MICROPRESS OR ANY OF THE STI PUB COMPANIES BE LIABLE FOR ANY CLAIM, -DAMAGES OR OTHER LIABILITY, INCLUDING, BUT NOT LIMITED TO, ANY GENERAL, -SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM OR OUT OF THE USE OR -INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT -SOFTWARE. - - 7. Except as contained in the notice set forth in Section 2, the -names MicroPress Inc. and STI Pub Companies, as well as the names of the -companies/organizations that compose the STI Pub Companies, shall not be -used in advertising or otherwise to promote the sale, use or other -dealings in the Font Software without the prior written consent of the -respective company or organization. - - 8. This License shall become null and void in the event of any -material breach of the Terms and Conditions herein by licensee. - - 9. A substantial portion of the STIX Fonts set was developed by -MicroPress Inc. for the STI Pub Companies. To obtain additional -mathematical fonts, please contact MicroPress, Inc., 68-30 Harrow -Street, Forest Hills, NY 11375, USA - Phone: (718) 575-1816. +The STIX fonts distributed with matplotlib have been modified from +their canonical form. They have been converted from OTF to TTF format +using Fontforge and this script: + #!/usr/bin/env fontforge + i=1 + while ( i<$argc ) + Open($argv[i]) + Generate($argv[i]:r + ".ttf") + i = i+1 + endloop + +The original STIX Font License begins below. + +----------------------------------------------------------- + +STIX Font License + +24 May 2010 + +Copyright (c) 2001-2010 by the STI Pub Companies, consisting of the American +Institute of Physics, the American Chemical Society, the American Mathematical +Society, the American Physical Society, Elsevier, Inc., and The Institute of +Electrical and Electronic Engineers, Inc. (www.stixfonts.org), with Reserved +Font Name STIX Fonts, STIX Fonts (TM) is a trademark of The Institute of +Electrical and Electronics Engineers, Inc. + +Portions copyright (c) 1998-2003 by MicroPress, Inc. (www.micropress-inc.com), +with Reserved Font Name TM Math. To obtain additional mathematical fonts, please +contact MicroPress, Inc., 68-30 Harrow Street, Forest Hills, NY 11375, USA, +Phone: (718) 575-1816. + +Portions copyright (c) 1990 by Elsevier, Inc. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://scripts.sil.org/OFL + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. From 7dd778180dc806fbdf807d183a435bfacf4b790c Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 7 Dec 2023 15:28:26 -0500 Subject: [PATCH 40/87] Merge pull request #27395 from story645/dependencies-install DOC: Moved dependencies under install because they're version dependent (cherry picked from commit d267a1d353ba09300a16621364670ec7ebba0fb9) --- doc/devel/development_setup.rst | 12 +++++- doc/devel/index.rst | 6 --- .../installing}/dependencies.rst | 4 +- doc/users/installing/index.rst | 43 +++++++++++++------ 4 files changed, 43 insertions(+), 22 deletions(-) rename doc/{devel => users/installing}/dependencies.rst (99%) diff --git a/doc/devel/development_setup.rst b/doc/devel/development_setup.rst index d5f491a82563..5489a377586c 100644 --- a/doc/devel/development_setup.rst +++ b/doc/devel/development_setup.rst @@ -155,9 +155,16 @@ Remember to activate the environment whenever you start working on Matplotlib. Install Dependencies ==================== + Most Python dependencies will be installed when :ref:`setting up the environment ` but non-Python dependencies like C++ compilers, LaTeX, and other system applications -must be installed separately. For a full list, see :ref:`dependencies`. +must be installed separately. + +.. toctree:: + :maxdepth: 2 + + ../users/installing/dependencies + Install Matplotlib in editable mode =================================== @@ -178,7 +185,8 @@ also happen if you change branches) you will have to re-run If the installation is not working, please consult the :ref:`troubleshooting guide `. If the guide does not offer a solution, please reach out via `chat `_ -or :ref:`open an issue `. +or :ref:`open an issue `. For a list of environment variables +you can set before install, see :ref:`environment-variables`. Verify the Installation ======================= diff --git a/doc/devel/index.rst b/doc/devel/index.rst index 47ea39d29c4b..04f83f22227f 100644 --- a/doc/devel/index.rst +++ b/doc/devel/index.rst @@ -97,12 +97,6 @@ Development environment development_setup - .. toctree:: - :maxdepth: 1 - - dependencies - ../users/installing/environment_variables_faq.rst - .. grid-item-card:: :shadow: none diff --git a/doc/devel/dependencies.rst b/doc/users/installing/dependencies.rst similarity index 99% rename from doc/devel/dependencies.rst rename to doc/users/installing/dependencies.rst index 7c14d90180d6..d5145ea85b3a 100644 --- a/doc/devel/dependencies.rst +++ b/doc/users/installing/dependencies.rst @@ -1,3 +1,5 @@ +.. redirect-from: /devel/dependencies + .. _dependencies: ============ @@ -377,7 +379,7 @@ The additional Python packages required to build the The content of :file:`doc-requirements.txt` is also shown below: -.. include:: ../../requirements/doc/doc-requirements.txt +.. include:: ../../../requirements/doc/doc-requirements.txt :literal: Additional external dependencies diff --git a/doc/users/installing/index.rst b/doc/users/installing/index.rst index 65fd8ee4182a..ba380a22e41f 100644 --- a/doc/users/installing/index.rst +++ b/doc/users/installing/index.rst @@ -1,8 +1,8 @@ .. redirect-from:: /users/installing -============ +************ Installation -============ +************ Install an official release @@ -135,25 +135,42 @@ Configure build and behavior defaults ===================================== Aspects of the build and install process and some behaviorial defaults of the -library can be configured via :ref:`environment-variables`. Default plotting -appearance and behavior can be configured via the +library can be configured via: + +.. toctree:: + :maxdepth: 2 + + environment_variables_faq.rst + +Default plotting appearance and behavior can be configured via the :ref:`rcParams file ` +Dependencies +============ + +Mandatory dependencies should be installed automatically if you install Matplotlib using +a package manager such as ``pip`` or ``conda``; therefore this list is primarily for +reference and troubleshooting. + +.. toctree:: + :maxdepth: 2 + + dependencies + .. _installing-faq: -========================== Frequently asked questions -========================== +=========================== Report a compilation problem -============================ +---------------------------- See :ref:`reporting-problems`. Matplotlib compiled fine, but nothing shows up when I use it -============================================================ +------------------------------------------------------------ The first thing to try is a :ref:`clean install ` and see if that helps. If not, the best way to test your install is by running a script, @@ -175,7 +192,7 @@ If you are still having trouble, see :ref:`reporting-problems`. .. _clean-install: How to completely remove Matplotlib -=================================== +----------------------------------- Occasionally, problems with Matplotlib can be solved with a clean installation of the package. In order to fully remove an installed Matplotlib: @@ -187,12 +204,12 @@ installation of the package. In order to fully remove an installed Matplotlib: directory `. OSX Notes -========= +--------- .. _which-python-for-osx: Which python for OSX? ---------------------- +^^^^^^^^^^^^^^^^^^^^^ Apple ships OSX with its own Python, in ``/usr/bin/python``, and its own copy of Matplotlib. Unfortunately, the way Apple currently installs its own copies @@ -222,7 +239,7 @@ or Python.org Python. .. _install_osx_binaries: Installing OSX binary wheels ----------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you are using Python from https://www.python.org, Homebrew, or Macports, then you can use the standard pip installer to install Matplotlib binaries in @@ -242,7 +259,7 @@ You might also want to install IPython or the Jupyter notebook (``python3 -m pip install ipython notebook``). Checking your installation --------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^ The new version of Matplotlib should now be on your Python "path". Check this at the Terminal.app command line:: From adcb7a124498fa3f6947ac83a8246e28e25024df Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 7 Dec 2023 15:28:26 -0500 Subject: [PATCH 41/87] Merge pull request #27395 from story645/dependencies-install DOC: Moved dependencies under install because they're version dependent (cherry picked from commit d267a1d353ba09300a16621364670ec7ebba0fb9) --- doc/devel/development_setup.rst | 15 ++++++- doc/devel/index.rst | 6 --- .../installing}/dependencies.rst | 4 +- doc/users/installing/index.rst | 43 +++++++++++++------ 4 files changed, 46 insertions(+), 22 deletions(-) rename doc/{devel => users/installing}/dependencies.rst (99%) diff --git a/doc/devel/development_setup.rst b/doc/devel/development_setup.rst index d5f491a82563..babb5829e5ea 100644 --- a/doc/devel/development_setup.rst +++ b/doc/devel/development_setup.rst @@ -155,9 +155,18 @@ Remember to activate the environment whenever you start working on Matplotlib. Install Dependencies ==================== + Most Python dependencies will be installed when :ref:`setting up the environment ` but non-Python dependencies like C++ compilers, LaTeX, and other system applications -must be installed separately. For a full list, see :ref:`dependencies`. +must be installed separately. + +.. toctree:: + :maxdepth: 2 + + ../users/installing/dependencies + + +.. _development-install: Install Matplotlib in editable mode =================================== @@ -178,7 +187,9 @@ also happen if you change branches) you will have to re-run If the installation is not working, please consult the :ref:`troubleshooting guide `. If the guide does not offer a solution, please reach out via `chat `_ -or :ref:`open an issue `. +or :ref:`open an issue `. For a list of the environment +variables you can set before install, see :ref:`environment-variables`. + Verify the Installation ======================= diff --git a/doc/devel/index.rst b/doc/devel/index.rst index 47ea39d29c4b..04f83f22227f 100644 --- a/doc/devel/index.rst +++ b/doc/devel/index.rst @@ -97,12 +97,6 @@ Development environment development_setup - .. toctree:: - :maxdepth: 1 - - dependencies - ../users/installing/environment_variables_faq.rst - .. grid-item-card:: :shadow: none diff --git a/doc/devel/dependencies.rst b/doc/users/installing/dependencies.rst similarity index 99% rename from doc/devel/dependencies.rst rename to doc/users/installing/dependencies.rst index 7c14d90180d6..d5145ea85b3a 100644 --- a/doc/devel/dependencies.rst +++ b/doc/users/installing/dependencies.rst @@ -1,3 +1,5 @@ +.. redirect-from: /devel/dependencies + .. _dependencies: ============ @@ -377,7 +379,7 @@ The additional Python packages required to build the The content of :file:`doc-requirements.txt` is also shown below: -.. include:: ../../requirements/doc/doc-requirements.txt +.. include:: ../../../requirements/doc/doc-requirements.txt :literal: Additional external dependencies diff --git a/doc/users/installing/index.rst b/doc/users/installing/index.rst index 65fd8ee4182a..ba380a22e41f 100644 --- a/doc/users/installing/index.rst +++ b/doc/users/installing/index.rst @@ -1,8 +1,8 @@ .. redirect-from:: /users/installing -============ +************ Installation -============ +************ Install an official release @@ -135,25 +135,42 @@ Configure build and behavior defaults ===================================== Aspects of the build and install process and some behaviorial defaults of the -library can be configured via :ref:`environment-variables`. Default plotting -appearance and behavior can be configured via the +library can be configured via: + +.. toctree:: + :maxdepth: 2 + + environment_variables_faq.rst + +Default plotting appearance and behavior can be configured via the :ref:`rcParams file ` +Dependencies +============ + +Mandatory dependencies should be installed automatically if you install Matplotlib using +a package manager such as ``pip`` or ``conda``; therefore this list is primarily for +reference and troubleshooting. + +.. toctree:: + :maxdepth: 2 + + dependencies + .. _installing-faq: -========================== Frequently asked questions -========================== +=========================== Report a compilation problem -============================ +---------------------------- See :ref:`reporting-problems`. Matplotlib compiled fine, but nothing shows up when I use it -============================================================ +------------------------------------------------------------ The first thing to try is a :ref:`clean install ` and see if that helps. If not, the best way to test your install is by running a script, @@ -175,7 +192,7 @@ If you are still having trouble, see :ref:`reporting-problems`. .. _clean-install: How to completely remove Matplotlib -=================================== +----------------------------------- Occasionally, problems with Matplotlib can be solved with a clean installation of the package. In order to fully remove an installed Matplotlib: @@ -187,12 +204,12 @@ installation of the package. In order to fully remove an installed Matplotlib: directory `. OSX Notes -========= +--------- .. _which-python-for-osx: Which python for OSX? ---------------------- +^^^^^^^^^^^^^^^^^^^^^ Apple ships OSX with its own Python, in ``/usr/bin/python``, and its own copy of Matplotlib. Unfortunately, the way Apple currently installs its own copies @@ -222,7 +239,7 @@ or Python.org Python. .. _install_osx_binaries: Installing OSX binary wheels ----------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you are using Python from https://www.python.org, Homebrew, or Macports, then you can use the standard pip installer to install Matplotlib binaries in @@ -242,7 +259,7 @@ You might also want to install IPython or the Jupyter notebook (``python3 -m pip install ipython notebook``). Checking your installation --------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^ The new version of Matplotlib should now be on your Python "path". Check this at the Terminal.app command line:: From f9bcd6b827b00b2b24a6cfb8934f147770ab4ce5 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Sat, 9 Dec 2023 13:27:08 +0100 Subject: [PATCH 42/87] Backport PR #27481: Fixing Pylab documentation in API interface overview --- galleries/users_explain/figure/api_interfaces.rst | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/galleries/users_explain/figure/api_interfaces.rst b/galleries/users_explain/figure/api_interfaces.rst index 6947817e6781..5279fe24634a 100644 --- a/galleries/users_explain/figure/api_interfaces.rst +++ b/galleries/users_explain/figure/api_interfaces.rst @@ -285,6 +285,11 @@ Appendix: "pylab" interface --------------------------- There is one further interface that is highly discouraged, and that is to -basically do ``from matplotlib.pyplot import *``. This allows users to simply -call ``plot(x, y)``. While convenient, this can lead to obvious problems if the -user unwittingly names a variable the same name as a pyplot method. +basically do ``from matplotlib.pylab import *``. This imports all the +functions from ``matplotlib.pyplot``, ``numpy``, ``numpy.fft``, ``numpy.linalg``, and +``numpy.random``, and some additional functions into the global namespace. + +Such a pattern is considered bad practice in modern python, as it clutters +the global namespace. Even more severely, in the case of ``pylab``, this will +overwrite some builtin functions (e.g. the builtin ``sum`` will be replaced by +``numpy.sum``), which can lead to unexpected behavior. From 0229df5e716421eb53e834dfd1dbcc03a43275f2 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Mon, 11 Dec 2023 21:57:39 -0500 Subject: [PATCH 43/87] Backport PR #27496: Bump actions/setup-python from 4 to 5 --- .github/workflows/cibuildwheel.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/mypy-stubtest.yml | 2 +- .github/workflows/reviewdog.yml | 4 ++-- .github/workflows/tests.yml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/cibuildwheel.yml b/.github/workflows/cibuildwheel.yml index 217c5d88f16e..c31842a4ce1f 100644 --- a/.github/workflows/cibuildwheel.yml +++ b/.github/workflows/cibuildwheel.yml @@ -43,7 +43,7 @@ jobs: with: fetch-depth: 0 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 name: Install Python with: python-version: 3.9 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index d31973e954d5..f0041ece02ee 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -29,7 +29,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 if: matrix.language != 'javascript' with: python-version: '3.x' diff --git a/.github/workflows/mypy-stubtest.yml b/.github/workflows/mypy-stubtest.yml index 6da6f607642c..efd37a38b15b 100644 --- a/.github/workflows/mypy-stubtest.yml +++ b/.github/workflows/mypy-stubtest.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up Python 3 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.9 diff --git a/.github/workflows/reviewdog.yml b/.github/workflows/reviewdog.yml index f9c1121581c0..f4495eda8b84 100644 --- a/.github/workflows/reviewdog.yml +++ b/.github/workflows/reviewdog.yml @@ -15,7 +15,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up Python 3 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.9 @@ -45,7 +45,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up Python 3 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.9 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4f0a1c0196de..76290bfcc0d1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -98,7 +98,7 @@ jobs: fetch-depth: 0 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} From 8b40424ea4020bea189124024ad360e10b01aa61 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:19:07 +0100 Subject: [PATCH 44/87] Backport PR #27504: DOC: correct return type for axline --- lib/matplotlib/axes/_axes.py | 2 +- lib/matplotlib/axes/_axes.pyi | 4 ++-- lib/matplotlib/pyplot.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 28a9a92550e8..a54150f6f3c2 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -893,7 +893,7 @@ def axline(self, xy1, xy2=None, *, slope=None, **kwargs): Returns ------- - `.Line2D` + `.AxLine` Other Parameters ---------------- diff --git a/lib/matplotlib/axes/_axes.pyi b/lib/matplotlib/axes/_axes.pyi index 9602db3b950c..1bfca8fc333d 100644 --- a/lib/matplotlib/axes/_axes.pyi +++ b/lib/matplotlib/axes/_axes.pyi @@ -18,7 +18,7 @@ from matplotlib.contour import ContourSet, QuadContourSet from matplotlib.image import AxesImage, PcolorImage from matplotlib.legend import Legend from matplotlib.legend_handler import HandlerBase -from matplotlib.lines import Line2D +from matplotlib.lines import Line2D, AxLine from matplotlib.mlab import GaussianKDE from matplotlib.patches import Rectangle, FancyArrow, Polygon, StepPatch, Wedge from matplotlib.quiver import Quiver, QuiverKey, Barbs @@ -151,7 +151,7 @@ class Axes(_AxesBase): *, slope: float | None = ..., **kwargs - ) -> Line2D: ... + ) -> AxLine: ... def axhspan( self, ymin: float, ymax: float, xmin: float = ..., xmax: float = ..., **kwargs ) -> Polygon: ... diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 42c177490cbb..fb925f1df62b 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -130,7 +130,7 @@ # We may not need the following imports here: from matplotlib.colors import Normalize -from matplotlib.lines import Line2D +from matplotlib.lines import Line2D, AxLine from matplotlib.text import Text, Annotation from matplotlib.patches import Polygon, Rectangle, Circle, Arrow from matplotlib.widgets import Button, Slider, Widget @@ -2721,7 +2721,7 @@ def axline( *, slope: float | None = None, **kwargs, -) -> Line2D: +) -> AxLine: return gca().axline(xy1, xy2=xy2, slope=slope, **kwargs) From bce5d1a599f6f1ebfc78fee63e9294aebb81b055 Mon Sep 17 00:00:00 2001 From: James Salsman Date: Wed, 13 Dec 2023 03:09:26 -0800 Subject: [PATCH 45/87] Backport PR #27346: DOC: Show and correct default alignment parameters in text.py --- .../examples/text_labels_and_annotations/text_alignment.py | 3 ++- lib/matplotlib/axes/_axes.py | 5 ++++- lib/matplotlib/text.py | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/galleries/examples/text_labels_and_annotations/text_alignment.py b/galleries/examples/text_labels_and_annotations/text_alignment.py index 7ad0d07c6572..4c0351e0bbc5 100644 --- a/galleries/examples/text_labels_and_annotations/text_alignment.py +++ b/galleries/examples/text_labels_and_annotations/text_alignment.py @@ -4,7 +4,8 @@ ============== Texts are aligned relative to their anchor point depending on the properties -``horizontalalignment`` and ``verticalalignment``. +``horizontalalignment`` (default: ``left``) and ``verticalalignment`` +(default: ``baseline``.) .. redirect-from:: /gallery/pyplots/text_layout diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index a54150f6f3c2..793ff56cfcf9 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -632,7 +632,10 @@ def text(self, x, y, s, fontdict=None, **kwargs): """ Add text to the Axes. - Add the text *s* to the Axes at location *x*, *y* in data coordinates. + Add the text *s* to the Axes at location *x*, *y* in data coordinates, + with a default ``horizontalalignment`` on the ``left`` and + ``verticalalignment`` at the ``baseline``. See + :doc:`/gallery/text_labels_and_annotations/text_alignment`. Parameters ---------- diff --git a/lib/matplotlib/text.py b/lib/matplotlib/text.py index 7a58ce717200..7e7b84a3f9c3 100644 --- a/lib/matplotlib/text.py +++ b/lib/matplotlib/text.py @@ -123,7 +123,7 @@ def __init__(self, The text is aligned relative to the anchor point (*x*, *y*) according to ``horizontalalignment`` (default: 'left') and ``verticalalignment`` - (default: 'bottom'). See also + (default: 'baseline'). See also :doc:`/gallery/text_labels_and_annotations/text_alignment`. While Text accepts the 'label' keyword argument, by default it is not @@ -1251,7 +1251,7 @@ def set_verticalalignment(self, align): Parameters ---------- - align : {'bottom', 'baseline', 'center', 'center_baseline', 'top'} + align : {'baseline', 'bottom', 'center', 'center_baseline', 'top'} """ _api.check_in_list( ['top', 'bottom', 'center', 'baseline', 'center_baseline'], From ca293a2e63083ad73b2ddbed87d86cade7015f1f Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Fri, 15 Dec 2023 22:18:57 -0800 Subject: [PATCH 46/87] Backport PR #27528: FIX: Remove runloop execution while waiting for stdin --- src/_macosx.m | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/_macosx.m b/src/_macosx.m index a580362f676f..d39f37063c9e 100755 --- a/src/_macosx.m +++ b/src/_macosx.m @@ -89,9 +89,6 @@ static int wait_for_stdin() { if (!event) { break; } [NSApp sendEvent: event]; } - // We need to run the run loop for a short time to allow the - // events to be processed and keep flushing them while we wait for stdin - [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; } // Remove the input handler as an observer [[NSNotificationCenter defaultCenter] removeObserver: stdinHandle]; From 4a7efefa7dd8d55764eb5b5373e96dfba8f9b9c8 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Sun, 17 Dec 2023 20:59:31 +0100 Subject: [PATCH 47/87] Backport PR #27534: Clarify AxLine Params --- lib/matplotlib/lines.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py index 92d55a3fe6ae..9bc058f92132 100644 --- a/lib/matplotlib/lines.py +++ b/lib/matplotlib/lines.py @@ -1478,9 +1478,10 @@ def __init__(self, xy1, xy2, slope, **kwargs): The first set of (x, y) coordinates for the line to pass through. xy2 : (float, float) or None The second set of (x, y) coordinates for the line to pass through. - Either *xy2* or *slope* has to be given. + Both *xy2* and *slope* must be passed, but one of them must be None. slope : float or None - The slope of the line. Either *xy2* or *slope* has to be given. + The slope of the line. Both *xy2* and *slope* must be passed, but one of + them must be None. """ super().__init__([0, 1], [0, 1], **kwargs) From 428cf414e2210edfcc574ffbcd9407c4124e2dcc Mon Sep 17 00:00:00 2001 From: Oscar Gustafsson Date: Mon, 18 Dec 2023 12:14:35 +0100 Subject: [PATCH 48/87] Backport PR #27535: Update ax.legend input types --- lib/matplotlib/axes/_axes.py | 2 +- lib/matplotlib/axes/_axes.pyi | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 793ff56cfcf9..499c221bb5c9 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -280,7 +280,7 @@ def legend(self, *args, **kwargs): Parameters ---------- - handles : sequence of (`.Artist` or tuple of `.Artist`), optional + handles : list of (`.Artist` or tuple of `.Artist`), optional A list of Artists (lines, patches) to be added to the legend. Use this together with *labels*, if you need full control on what is shown in the legend and the automatic mechanism described above diff --git a/lib/matplotlib/axes/_axes.pyi b/lib/matplotlib/axes/_axes.pyi index 1bfca8fc333d..0a70e575dfc4 100644 --- a/lib/matplotlib/axes/_axes.pyi +++ b/lib/matplotlib/axes/_axes.pyi @@ -31,7 +31,7 @@ import matplotlib.streamplot as mstream import datetime import PIL.Image -from collections.abc import Callable, Sequence +from collections.abc import Callable, Iterable, Sequence from typing import Any, Literal, overload import numpy as np from numpy.typing import ArrayLike @@ -57,11 +57,11 @@ class Axes(_AxesBase): @overload def legend(self) -> Legend: ... @overload - def legend(self, handles: Sequence[Artist | tuple[Artist, ...]], labels: Sequence[str], **kwargs) -> Legend: ... + def legend(self, handles: Iterable[Artist | tuple[Artist, ...]], labels: Iterable[str], **kwargs) -> Legend: ... @overload - def legend(self, *, handles: Sequence[Artist | tuple[Artist, ...]], **kwargs) -> Legend: ... + def legend(self, *, handles: Iterable[Artist | tuple[Artist, ...]], **kwargs) -> Legend: ... @overload - def legend(self, labels: Sequence[str], **kwargs) -> Legend: ... + def legend(self, labels: Iterable[str], **kwargs) -> Legend: ... @overload def legend(self, **kwargs) -> Legend: ... From 33bbb0520650a03309a218e28fcc87b0be401f0f Mon Sep 17 00:00:00 2001 From: Kyle Sunden Date: Mon, 18 Dec 2023 18:37:06 -0600 Subject: [PATCH 49/87] Backport PR #27527: FIX: Add macos timers to the main thread --- src/_macosx.m | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/_macosx.m b/src/_macosx.m index d39f37063c9e..6be86cfe7367 100755 --- a/src/_macosx.m +++ b/src/_macosx.m @@ -1723,11 +1723,15 @@ - (void)flagsChanged:(NSEvent *)event } // hold a reference to the timer so we can invalidate/stop it later - self->timer = [NSTimer scheduledTimerWithTimeInterval: interval - repeats: !single - block: ^(NSTimer *timer) { + self->timer = [NSTimer timerWithTimeInterval: interval + repeats: !single + block: ^(NSTimer *timer) { gil_call_method((PyObject*)self, "_on_timer"); }]; + // Schedule the timer on the main run loop which is needed + // when updating the UI from a background thread + [[NSRunLoop mainRunLoop] addTimer: self->timer forMode: NSRunLoopCommonModes]; + exit: Py_XDECREF(py_interval); Py_XDECREF(py_single); From 2e01b0c6b281794be8784c16d4c346cec3b39da9 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Fri, 29 Dec 2023 23:43:34 +0100 Subject: [PATCH 50/87] Backport PR #27578: Fix polar labels with negative theta limit --- lib/matplotlib/projections/polar.py | 2 +- lib/matplotlib/tests/test_polar.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/projections/polar.py b/lib/matplotlib/projections/polar.py index 0bff320e5728..33063674c415 100644 --- a/lib/matplotlib/projections/polar.py +++ b/lib/matplotlib/projections/polar.py @@ -298,7 +298,7 @@ def set_axis(self, axis): def __call__(self): lim = self.axis.get_view_interval() if _is_full_circle_deg(lim[0], lim[1]): - return np.arange(8) * 2 * np.pi / 8 + return np.deg2rad(min(lim)) + np.arange(8) * 2 * np.pi / 8 else: return np.deg2rad(self.base()) diff --git a/lib/matplotlib/tests/test_polar.py b/lib/matplotlib/tests/test_polar.py index 9d6e78da2cbc..9a1c6be6fcff 100644 --- a/lib/matplotlib/tests/test_polar.py +++ b/lib/matplotlib/tests/test_polar.py @@ -446,3 +446,11 @@ def test_polar_log(): n = 100 ax.plot(np.linspace(0, 2 * np.pi, n), np.logspace(0, 2, n)) + + +def test_polar_neg_theta_lims(): + fig = plt.figure() + ax = fig.add_subplot(projection='polar') + ax.set_thetalim(-np.pi, np.pi) + labels = [l.get_text() for l in ax.xaxis.get_ticklabels()] + assert labels == ['-180°', '-135°', '-90°', '-45°', '0°', '45°', '90°', '135°'] From 88d3600a0431fcc9ab8785e434ce47569d4be086 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Thu, 4 Jan 2024 02:34:10 -0500 Subject: [PATCH 51/87] Backport PR #27595: Fix is_sorted_and_has_non_nan for byteswapped inputs. --- lib/matplotlib/tests/test_lines.py | 2 ++ src/_path_wrapper.cpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/tests/test_lines.py b/lib/matplotlib/tests/test_lines.py index 4f23e6969b0b..6ad8c640384e 100644 --- a/lib/matplotlib/tests/test_lines.py +++ b/lib/matplotlib/tests/test_lines.py @@ -249,6 +249,8 @@ def test_is_sorted_and_has_non_nan(): assert _path.is_sorted_and_has_non_nan(np.array([1, 2, 3])) assert _path.is_sorted_and_has_non_nan(np.array([1, np.nan, 3])) assert not _path.is_sorted_and_has_non_nan([3, 5] + [np.nan] * 100 + [0, 2]) + # [2, 256] byteswapped: + assert not _path.is_sorted_and_has_non_nan(np.array([33554432, 65536], ">i4")) n = 2 * mlines.Line2D._subslice_optim_min_size plt.plot([np.nan] * n, range(n)) diff --git a/src/_path_wrapper.cpp b/src/_path_wrapper.cpp index 369d9e030880..15a30e298dfd 100644 --- a/src/_path_wrapper.cpp +++ b/src/_path_wrapper.cpp @@ -699,8 +699,8 @@ static PyObject *Py_is_sorted_and_has_non_nan(PyObject *self, PyObject *obj) { bool result; - PyArrayObject *array = (PyArrayObject *)PyArray_FromAny( - obj, NULL, 1, 1, 0, NULL); + PyArrayObject *array = (PyArrayObject *)PyArray_CheckFromAny( + obj, NULL, 1, 1, NPY_ARRAY_NOTSWAPPED, NULL); if (array == NULL) { return NULL; From 2445d11fedb0586e00b10022f497ceea59e70351 Mon Sep 17 00:00:00 2001 From: Oscar Gustafsson Date: Thu, 4 Jan 2024 10:15:31 +0100 Subject: [PATCH 52/87] Backport PR #27594: Cleanup viewlims example. --- galleries/examples/event_handling/viewlims.py | 79 +++++++++---------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/galleries/examples/event_handling/viewlims.py b/galleries/examples/event_handling/viewlims.py index b47e3b5b0801..ebc3e6de5fb8 100644 --- a/galleries/examples/event_handling/viewlims.py +++ b/galleries/examples/event_handling/viewlims.py @@ -14,21 +14,15 @@ You can copy and paste individual parts, or download the entire example using the link at the bottom of the page. """ + +import functools + import matplotlib.pyplot as plt import numpy as np from matplotlib.patches import Rectangle -# We just subclass Rectangle so that it can be called with an Axes -# instance, causing the rectangle to update its shape to match the -# bounds of the Axes -class UpdatingRect(Rectangle): - def __call__(self, ax): - self.set_bounds(*ax.viewLim.bounds) - ax.figure.canvas.draw_idle() - - # A class that will regenerate a fractal set as we zoom in, so that you # can actually see the increasing detail. A box in the left panel will show # the area to which we are zoomed. @@ -40,9 +34,9 @@ def __init__(self, h=500, w=500, niter=50, radius=2., power=2): self.radius = radius self.power = power - def compute_image(self, xstart, xend, ystart, yend): - self.x = np.linspace(xstart, xend, self.width) - self.y = np.linspace(ystart, yend, self.height).reshape(-1, 1) + def compute_image(self, xlim, ylim): + self.x = np.linspace(*xlim, self.width) + self.y = np.linspace(*ylim, self.height).reshape(-1, 1) c = self.x + 1.0j * self.y threshold_time = np.zeros((self.height, self.width)) z = np.zeros(threshold_time.shape, dtype=complex) @@ -56,38 +50,43 @@ def compute_image(self, xstart, xend, ystart, yend): def ax_update(self, ax): ax.set_autoscale_on(False) # Otherwise, infinite loop # Get the number of points from the number of pixels in the window - self.width, self.height = \ - np.round(ax.patch.get_window_extent().size).astype(int) - # Get the range for the new area - vl = ax.viewLim - extent = vl.x0, vl.x1, vl.y0, vl.y1 + self.width, self.height = ax.patch.get_window_extent().size.round().astype(int) # Update the image object with our new data and extent - im = ax.images[-1] - im.set_data(self.compute_image(*extent)) - im.set_extent(extent) + ax.images[-1].set(data=self.compute_image(ax.get_xlim(), ax.get_ylim()), + extent=(*ax.get_xlim(), *ax.get_ylim())) ax.figure.canvas.draw_idle() md = MandelbrotDisplay() -Z = md.compute_image(-2., 0.5, -1.25, 1.25) - -fig1, (ax1, ax2) = plt.subplots(1, 2) -ax1.imshow(Z, origin='lower', - extent=(md.x.min(), md.x.max(), md.y.min(), md.y.max())) -ax2.imshow(Z, origin='lower', - extent=(md.x.min(), md.x.max(), md.y.min(), md.y.max())) - -rect = UpdatingRect( - [0, 0], 0, 0, facecolor='none', edgecolor='black', linewidth=1.0) -rect.set_bounds(*ax2.viewLim.bounds) -ax1.add_patch(rect) - -# Connect for changing the view limits -ax2.callbacks.connect('xlim_changed', rect) -ax2.callbacks.connect('ylim_changed', rect) - -ax2.callbacks.connect('xlim_changed', md.ax_update) -ax2.callbacks.connect('ylim_changed', md.ax_update) -ax2.set_title("Zoom here") + +fig1, (ax_full, ax_zoom) = plt.subplots(1, 2) +ax_zoom.imshow([[0]], origin="lower") # Empty initial image. +ax_zoom.set_title("Zoom here") + +rect = Rectangle( + [0, 0], 0, 0, facecolor="none", edgecolor="black", linewidth=1.0) +ax_full.add_patch(rect) + + +def update_rect(rect, ax): # Let the rectangle track the bounds of the zoom axes. + xlo, xhi = ax.get_xlim() + ylo, yhi = ax.get_ylim() + rect.set_bounds((xlo, ylo, xhi - xlo, yhi - ylo)) + ax.figure.canvas.draw_idle() + + +# Connect for changing the view limits. +ax_zoom.callbacks.connect("xlim_changed", functools.partial(update_rect, rect)) +ax_zoom.callbacks.connect("ylim_changed", functools.partial(update_rect, rect)) + +ax_zoom.callbacks.connect("xlim_changed", md.ax_update) +ax_zoom.callbacks.connect("ylim_changed", md.ax_update) + +# Initialize: trigger image computation by setting view limits; set colormap limits; +# copy image to full view. +ax_zoom.set(xlim=(-2, .5), ylim=(-1.25, 1.25)) +im = ax_zoom.images[0] +ax_zoom.images[0].set(clim=(im.get_array().min(), im.get_array().max())) +ax_full.imshow(im.get_array(), extent=im.get_extent(), origin="lower") plt.show() From 14aa4e0a24558124fbd4a322c9c143287eab56b3 Mon Sep 17 00:00:00 2001 From: Oscar Gustafsson Date: Thu, 4 Jan 2024 10:15:31 +0100 Subject: [PATCH 53/87] Backport PR #27594: Cleanup viewlims example. --- galleries/examples/event_handling/viewlims.py | 79 +++++++++---------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/galleries/examples/event_handling/viewlims.py b/galleries/examples/event_handling/viewlims.py index b47e3b5b0801..ebc3e6de5fb8 100644 --- a/galleries/examples/event_handling/viewlims.py +++ b/galleries/examples/event_handling/viewlims.py @@ -14,21 +14,15 @@ You can copy and paste individual parts, or download the entire example using the link at the bottom of the page. """ + +import functools + import matplotlib.pyplot as plt import numpy as np from matplotlib.patches import Rectangle -# We just subclass Rectangle so that it can be called with an Axes -# instance, causing the rectangle to update its shape to match the -# bounds of the Axes -class UpdatingRect(Rectangle): - def __call__(self, ax): - self.set_bounds(*ax.viewLim.bounds) - ax.figure.canvas.draw_idle() - - # A class that will regenerate a fractal set as we zoom in, so that you # can actually see the increasing detail. A box in the left panel will show # the area to which we are zoomed. @@ -40,9 +34,9 @@ def __init__(self, h=500, w=500, niter=50, radius=2., power=2): self.radius = radius self.power = power - def compute_image(self, xstart, xend, ystart, yend): - self.x = np.linspace(xstart, xend, self.width) - self.y = np.linspace(ystart, yend, self.height).reshape(-1, 1) + def compute_image(self, xlim, ylim): + self.x = np.linspace(*xlim, self.width) + self.y = np.linspace(*ylim, self.height).reshape(-1, 1) c = self.x + 1.0j * self.y threshold_time = np.zeros((self.height, self.width)) z = np.zeros(threshold_time.shape, dtype=complex) @@ -56,38 +50,43 @@ def compute_image(self, xstart, xend, ystart, yend): def ax_update(self, ax): ax.set_autoscale_on(False) # Otherwise, infinite loop # Get the number of points from the number of pixels in the window - self.width, self.height = \ - np.round(ax.patch.get_window_extent().size).astype(int) - # Get the range for the new area - vl = ax.viewLim - extent = vl.x0, vl.x1, vl.y0, vl.y1 + self.width, self.height = ax.patch.get_window_extent().size.round().astype(int) # Update the image object with our new data and extent - im = ax.images[-1] - im.set_data(self.compute_image(*extent)) - im.set_extent(extent) + ax.images[-1].set(data=self.compute_image(ax.get_xlim(), ax.get_ylim()), + extent=(*ax.get_xlim(), *ax.get_ylim())) ax.figure.canvas.draw_idle() md = MandelbrotDisplay() -Z = md.compute_image(-2., 0.5, -1.25, 1.25) - -fig1, (ax1, ax2) = plt.subplots(1, 2) -ax1.imshow(Z, origin='lower', - extent=(md.x.min(), md.x.max(), md.y.min(), md.y.max())) -ax2.imshow(Z, origin='lower', - extent=(md.x.min(), md.x.max(), md.y.min(), md.y.max())) - -rect = UpdatingRect( - [0, 0], 0, 0, facecolor='none', edgecolor='black', linewidth=1.0) -rect.set_bounds(*ax2.viewLim.bounds) -ax1.add_patch(rect) - -# Connect for changing the view limits -ax2.callbacks.connect('xlim_changed', rect) -ax2.callbacks.connect('ylim_changed', rect) - -ax2.callbacks.connect('xlim_changed', md.ax_update) -ax2.callbacks.connect('ylim_changed', md.ax_update) -ax2.set_title("Zoom here") + +fig1, (ax_full, ax_zoom) = plt.subplots(1, 2) +ax_zoom.imshow([[0]], origin="lower") # Empty initial image. +ax_zoom.set_title("Zoom here") + +rect = Rectangle( + [0, 0], 0, 0, facecolor="none", edgecolor="black", linewidth=1.0) +ax_full.add_patch(rect) + + +def update_rect(rect, ax): # Let the rectangle track the bounds of the zoom axes. + xlo, xhi = ax.get_xlim() + ylo, yhi = ax.get_ylim() + rect.set_bounds((xlo, ylo, xhi - xlo, yhi - ylo)) + ax.figure.canvas.draw_idle() + + +# Connect for changing the view limits. +ax_zoom.callbacks.connect("xlim_changed", functools.partial(update_rect, rect)) +ax_zoom.callbacks.connect("ylim_changed", functools.partial(update_rect, rect)) + +ax_zoom.callbacks.connect("xlim_changed", md.ax_update) +ax_zoom.callbacks.connect("ylim_changed", md.ax_update) + +# Initialize: trigger image computation by setting view limits; set colormap limits; +# copy image to full view. +ax_zoom.set(xlim=(-2, .5), ylim=(-1.25, 1.25)) +im = ax_zoom.images[0] +ax_zoom.images[0].set(clim=(im.get_array().min(), im.get_array().max())) +ax_full.imshow(im.get_array(), extent=im.get_extent(), origin="lower") plt.show() From a179ea8be9ec250fb04b7b8551e3da3f14775550 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 8 Jan 2024 09:21:09 -0500 Subject: [PATCH 54/87] Backport PR #27606: Pin black version --- environment.yml | 2 +- requirements/testing/all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/environment.yml b/environment.yml index 7b13735bb172..15cba9a01c32 100644 --- a/environment.yml +++ b/environment.yml @@ -62,4 +62,4 @@ dependencies: - pytest-xdist - tornado - pytz - - black + - black<24 diff --git a/requirements/testing/all.txt b/requirements/testing/all.txt index 173c5a4a9909..4ca786fcf73e 100644 --- a/requirements/testing/all.txt +++ b/requirements/testing/all.txt @@ -1,6 +1,6 @@ # pip requirements for all the CI builds -black +black<24 certifi coverage!=6.3 psutil From fabbaabedb9e1e2c5ee50ea092131a06756c1848 Mon Sep 17 00:00:00 2001 From: hannah Date: Tue, 9 Jan 2024 17:15:05 -0500 Subject: [PATCH 55/87] Backport PR #27620: DOC: simplify histogram animation example --- .../examples/animation/animated_histogram.py | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/galleries/examples/animation/animated_histogram.py b/galleries/examples/animation/animated_histogram.py index d23d174ad2ca..03fb04d8acda 100644 --- a/galleries/examples/animation/animated_histogram.py +++ b/galleries/examples/animation/animated_histogram.py @@ -7,43 +7,42 @@ histogram. """ +import functools + import matplotlib.pyplot as plt import numpy as np import matplotlib.animation as animation -# Fixing random state for reproducibility -np.random.seed(19680801) -# Fixing bin edges +# Setting up a random number generator with a fixed state for reproducibility. +rng = np.random.default_rng(seed=19680801) +# Fixing bin edges. HIST_BINS = np.linspace(-4, 4, 100) -# histogram our data with numpy -data = np.random.randn(1000) +# Histogram our data with numpy. +data = rng.standard_normal(1000) n, _ = np.histogram(data, HIST_BINS) # %% # To animate the histogram, we need an ``animate`` function, which generates -# a random set of numbers and updates the heights of rectangles. We utilize a -# python closure to track an instance of `.BarContainer` whose `.Rectangle` -# patches we shall update. +# a random set of numbers and updates the heights of rectangles. The ``animate`` +# function updates the `.Rectangle` patches on an instance of `.BarContainer`. -def prepare_animation(bar_container): +def animate(frame_number, bar_container): + # Simulate new data coming in. + data = rng.standard_normal(1000) + n, _ = np.histogram(data, HIST_BINS) + for count, rect in zip(n, bar_container.patches): + rect.set_height(count) - def animate(frame_number): - # simulate new data coming in - data = np.random.randn(1000) - n, _ = np.histogram(data, HIST_BINS) - for count, rect in zip(n, bar_container.patches): - rect.set_height(count) - return bar_container.patches - return animate + return bar_container.patches # %% # Using :func:`~matplotlib.pyplot.hist` allows us to get an instance of -# `.BarContainer`, which is a collection of `.Rectangle` instances. Calling -# ``prepare_animation`` will define ``animate`` function working with supplied -# `.BarContainer`, all this is used to setup `.FuncAnimation`. +# `.BarContainer`, which is a collection of `.Rectangle` instances. Since +# `.FuncAnimation` will only pass the frame number parameter to the animation +# function, we use `functools.partial` to fix the ``bar_container`` parameter. # Output generated via `matplotlib.animation.Animation.to_jshtml`. @@ -52,6 +51,6 @@ def animate(frame_number): ec="yellow", fc="green", alpha=0.5) ax.set_ylim(top=55) # set safe limit to ensure that all data is visible. -ani = animation.FuncAnimation(fig, prepare_animation(bar_container), 50, - repeat=False, blit=True) +anim = functools.partial(animate, bar_container=bar_container) +ani = animation.FuncAnimation(fig, anim, 50, repeat=False, blit=True) plt.show() From fbb5aa5bdbf4e9d0e24f0655a0cbf896344359e7 Mon Sep 17 00:00:00 2001 From: hannah Date: Tue, 9 Jan 2024 17:15:05 -0500 Subject: [PATCH 56/87] Backport PR #27620: DOC: simplify histogram animation example --- .../examples/animation/animated_histogram.py | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/galleries/examples/animation/animated_histogram.py b/galleries/examples/animation/animated_histogram.py index d23d174ad2ca..03fb04d8acda 100644 --- a/galleries/examples/animation/animated_histogram.py +++ b/galleries/examples/animation/animated_histogram.py @@ -7,43 +7,42 @@ histogram. """ +import functools + import matplotlib.pyplot as plt import numpy as np import matplotlib.animation as animation -# Fixing random state for reproducibility -np.random.seed(19680801) -# Fixing bin edges +# Setting up a random number generator with a fixed state for reproducibility. +rng = np.random.default_rng(seed=19680801) +# Fixing bin edges. HIST_BINS = np.linspace(-4, 4, 100) -# histogram our data with numpy -data = np.random.randn(1000) +# Histogram our data with numpy. +data = rng.standard_normal(1000) n, _ = np.histogram(data, HIST_BINS) # %% # To animate the histogram, we need an ``animate`` function, which generates -# a random set of numbers and updates the heights of rectangles. We utilize a -# python closure to track an instance of `.BarContainer` whose `.Rectangle` -# patches we shall update. +# a random set of numbers and updates the heights of rectangles. The ``animate`` +# function updates the `.Rectangle` patches on an instance of `.BarContainer`. -def prepare_animation(bar_container): +def animate(frame_number, bar_container): + # Simulate new data coming in. + data = rng.standard_normal(1000) + n, _ = np.histogram(data, HIST_BINS) + for count, rect in zip(n, bar_container.patches): + rect.set_height(count) - def animate(frame_number): - # simulate new data coming in - data = np.random.randn(1000) - n, _ = np.histogram(data, HIST_BINS) - for count, rect in zip(n, bar_container.patches): - rect.set_height(count) - return bar_container.patches - return animate + return bar_container.patches # %% # Using :func:`~matplotlib.pyplot.hist` allows us to get an instance of -# `.BarContainer`, which is a collection of `.Rectangle` instances. Calling -# ``prepare_animation`` will define ``animate`` function working with supplied -# `.BarContainer`, all this is used to setup `.FuncAnimation`. +# `.BarContainer`, which is a collection of `.Rectangle` instances. Since +# `.FuncAnimation` will only pass the frame number parameter to the animation +# function, we use `functools.partial` to fix the ``bar_container`` parameter. # Output generated via `matplotlib.animation.Animation.to_jshtml`. @@ -52,6 +51,6 @@ def animate(frame_number): ec="yellow", fc="green", alpha=0.5) ax.set_ylim(top=55) # set safe limit to ensure that all data is visible. -ani = animation.FuncAnimation(fig, prepare_animation(bar_container), 50, - repeat=False, blit=True) +anim = functools.partial(animate, bar_container=bar_container) +ani = animation.FuncAnimation(fig, anim, 50, repeat=False, blit=True) plt.show() From f189a342176838df9564537b38326882a14c1ded Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 11 Jan 2024 11:22:43 -0500 Subject: [PATCH 57/87] Backport PR #27634: circle: Make deploy stage into a normal step --- .circleci/config.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0668f436ddac..e120f95f5ee8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,6 +1,6 @@ # Circle CI configuration file # https://circleci.com/docs/ - +--- version: 2.1 @@ -197,6 +197,12 @@ commands: - store_artifacts: path: doc/build/sphinx-gallery-files.tar.gz + deploy-docs: + steps: + - run: + name: "Deploy new docs" + command: ./.circleci/deploy-docs.sh + ########################################## # Here is where the real jobs are defined. @@ -234,9 +240,7 @@ jobs: fingerprints: - "be:c3:c1:d8:fb:a1:0e:37:71:72:d7:a3:40:13:8f:14" - - deploy: - name: "Deploy new docs" - command: ./.circleci/deploy-docs.sh + - deploy-docs ######################################### # Defining workflows gets us parallelism. From f7f0937d5572c6e6b8c94ff1dc225fe11ce92634 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 11 Jan 2024 11:22:43 -0500 Subject: [PATCH 58/87] Backport PR #27634: circle: Make deploy stage into a normal step --- .circleci/config.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0668f436ddac..e120f95f5ee8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,6 +1,6 @@ # Circle CI configuration file # https://circleci.com/docs/ - +--- version: 2.1 @@ -197,6 +197,12 @@ commands: - store_artifacts: path: doc/build/sphinx-gallery-files.tar.gz + deploy-docs: + steps: + - run: + name: "Deploy new docs" + command: ./.circleci/deploy-docs.sh + ########################################## # Here is where the real jobs are defined. @@ -234,9 +240,7 @@ jobs: fingerprints: - "be:c3:c1:d8:fb:a1:0e:37:71:72:d7:a3:40:13:8f:14" - - deploy: - name: "Deploy new docs" - command: ./.circleci/deploy-docs.sh + - deploy-docs ######################################### # Defining workflows gets us parallelism. From 2d566207f511cc02522753d0793a207971026cc1 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Tue, 16 Jan 2024 19:53:52 -0500 Subject: [PATCH 59/87] Backport PR #27624: Prepare for Pytest v8 --- lib/matplotlib/backends/backend_pdf.py | 2 +- lib/matplotlib/tests/test_colors.py | 11 ++++++++--- lib/matplotlib/tests/test_rcparams.py | 6 ++---- lib/matplotlib/tests/test_ticker.py | 16 ++++++++++++---- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index aa4883135d92..facb8efec819 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -2732,7 +2732,7 @@ def close(self): _api.warn_deprecated("3.8", message=( "Keeping empty pdf files is deprecated since %(since)s and support " "will be removed %(removal)s.")) - PdfFile(self._filename, metadata=self._metadata) # touch the file. + PdfFile(self._filename, metadata=self._metadata).close() # touch the file. def infodict(self): """ diff --git a/lib/matplotlib/tests/test_colors.py b/lib/matplotlib/tests/test_colors.py index 139efbe17407..35030b6bea7a 100644 --- a/lib/matplotlib/tests/test_colors.py +++ b/lib/matplotlib/tests/test_colors.py @@ -1,6 +1,7 @@ import copy import itertools import unittest.mock +from packaging.version import parse as parse_version from io import BytesIO import numpy as np @@ -147,9 +148,13 @@ def test_double_register_builtin_cmap(): with pytest.raises(ValueError, match='A colormap named "viridis"'): with pytest.warns(mpl.MatplotlibDeprecationWarning): cm.register_cmap(name, mpl.colormaps[name]) - with pytest.warns(UserWarning): - # TODO is warning more than once! - cm.register_cmap(name, mpl.colormaps[name], override_builtin=True) + + if parse_version(pytest.__version__).major < 8: + with pytest.warns(UserWarning): + cm.register_cmap(name, mpl.colormaps[name], override_builtin=True) + else: + with pytest.warns(UserWarning), pytest.warns(mpl.MatplotlibDeprecationWarning): + cm.register_cmap(name, mpl.colormaps[name], override_builtin=True) def test_unregister_builtin_cmap(): diff --git a/lib/matplotlib/tests/test_rcparams.py b/lib/matplotlib/tests/test_rcparams.py index 515068c462d4..610b6056f20f 100644 --- a/lib/matplotlib/tests/test_rcparams.py +++ b/lib/matplotlib/tests/test_rcparams.py @@ -106,14 +106,12 @@ def test_rcparams_update(): rc = mpl.RcParams({'figure.figsize': (3.5, 42)}) bad_dict = {'figure.figsize': (3.5, 42, 1)} # make sure validation happens on input - with pytest.raises(ValueError), \ - pytest.warns(UserWarning, match="validate"): + with pytest.raises(ValueError): rc.update(bad_dict) def test_rcparams_init(): - with pytest.raises(ValueError), \ - pytest.warns(UserWarning, match="validate"): + with pytest.raises(ValueError): mpl.RcParams({'figure.figsize': (3.5, 42, 1)}) diff --git a/lib/matplotlib/tests/test_ticker.py b/lib/matplotlib/tests/test_ticker.py index 961daaa1d167..ff9a519ee185 100644 --- a/lib/matplotlib/tests/test_ticker.py +++ b/lib/matplotlib/tests/test_ticker.py @@ -3,6 +3,7 @@ import locale import logging import re +from packaging.version import parse as parse_version import numpy as np from numpy.testing import assert_almost_equal, assert_array_equal @@ -914,10 +915,17 @@ def test_mathtext_ticks(self): 'axes.formatter.use_mathtext': False }) - with pytest.warns(UserWarning, match='cmr10 font should ideally'): - fig, ax = plt.subplots() - ax.set_xticks([-1, 0, 1]) - fig.canvas.draw() + if parse_version(pytest.__version__).major < 8: + with pytest.warns(UserWarning, match='cmr10 font should ideally'): + fig, ax = plt.subplots() + ax.set_xticks([-1, 0, 1]) + fig.canvas.draw() + else: + with (pytest.warns(UserWarning, match="Glyph 8722"), + pytest.warns(UserWarning, match='cmr10 font should ideally')): + fig, ax = plt.subplots() + ax.set_xticks([-1, 0, 1]) + fig.canvas.draw() def test_cmr10_substitutions(self, caplog): mpl.rcParams.update({ From 697c7cb6dfac027727f3c99a9abe9d4d07089b2f Mon Sep 17 00:00:00 2001 From: David Stansby Date: Wed, 17 Jan 2024 11:57:43 +0000 Subject: [PATCH 60/87] Merge pull request #27647 from saranti/minorticks Fix error that occurs when minorticks are on multi-Axes Figure with more than one boxplot (cherry picked from commit b03407b375d8a4b5101f40178f9d19b84bfce822) --- lib/matplotlib/tests/test_ticker.py | 21 +++++++++++++++++++++ lib/matplotlib/ticker.py | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_ticker.py b/lib/matplotlib/tests/test_ticker.py index ff9a519ee185..202465847b5b 100644 --- a/lib/matplotlib/tests/test_ticker.py +++ b/lib/matplotlib/tests/test_ticker.py @@ -1797,3 +1797,24 @@ def test_set_offset_string(formatter): assert formatter.get_offset() == '' formatter.set_offset_string('mpl') assert formatter.get_offset() == 'mpl' + + +def test_minorticks_on_multi_fig(): + """ + Turning on minor gridlines in a multi-Axes Figure + that contains more than one boxplot and shares the x-axis + should not raise an exception. + """ + fig, ax = plt.subplots() + + ax.boxplot(np.arange(10), positions=[0]) + ax.boxplot(np.arange(10), positions=[0]) + ax.boxplot(np.arange(10), positions=[1]) + + ax.grid(which="major") + ax.grid(which="minor") + ax.minorticks_on() + fig.draw_without_rendering() + + assert ax.get_xgridlines() + assert isinstance(ax.xaxis.get_minor_locator(), mpl.ticker.AutoMinorLocator) diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index 5b1b7e11408c..47a06f3eec3a 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -2895,7 +2895,7 @@ def __call__(self): 'logarithmic scale') return [] - majorlocs = self.axis.get_majorticklocs() + majorlocs = np.unique(self.axis.get_majorticklocs()) try: majorstep = majorlocs[1] - majorlocs[0] except IndexError: From 72403c7c63c2f2f957182d0ed0501adde385f2c1 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Wed, 17 Jan 2024 12:03:53 +0000 Subject: [PATCH 61/87] Backport PR #27657: Fix Numpy 2.0 related test failures --- lib/matplotlib/pylab.py | 2 ++ lib/matplotlib/tests/test_axes.py | 23 ++++++++++++++--------- lib/matplotlib/tests/test_colors.py | 16 ++++++++-------- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/lib/matplotlib/pylab.py b/lib/matplotlib/pylab.py index 77eb6506d87f..a50779cf6d26 100644 --- a/lib/matplotlib/pylab.py +++ b/lib/matplotlib/pylab.py @@ -60,6 +60,8 @@ bytes = __import__("builtins").bytes # We also don't want the numpy version of these functions abs = __import__("builtins").abs +bool = __import__("builtins").bool max = __import__("builtins").max min = __import__("builtins").min +pow = __import__("builtins").pow round = __import__("builtins").round diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 87d5e35013c0..3fa7cbe0e34d 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -5735,7 +5735,12 @@ def test_text_labelsize(): ax.tick_params(direction='out') -@image_comparison(['pie_default.png']) +# Note: The `pie` image tests were affected by Numpy 2.0 changing promotions +# (NEP 50). While the changes were only marginal, tolerances were introduced. +# These tolerances could likely go away when numpy 2.0 is the minimum supported +# numpy and the images are regenerated. + +@image_comparison(['pie_default.png'], tol=0.01) def test_pie_default(): # The slices will be ordered and plotted counter-clockwise. labels = 'Frogs', 'Hogs', 'Dogs', 'Logs' @@ -5748,7 +5753,7 @@ def test_pie_default(): @image_comparison(['pie_linewidth_0', 'pie_linewidth_0', 'pie_linewidth_0'], - extensions=['png'], style='mpl20') + extensions=['png'], style='mpl20', tol=0.01) def test_pie_linewidth_0(): # The slices will be ordered and plotted counter-clockwise. labels = 'Frogs', 'Hogs', 'Dogs', 'Logs' @@ -5780,7 +5785,7 @@ def test_pie_linewidth_0(): plt.axis('equal') -@image_comparison(['pie_center_radius.png'], style='mpl20') +@image_comparison(['pie_center_radius.png'], style='mpl20', tol=0.005) def test_pie_center_radius(): # The slices will be ordered and plotted counter-clockwise. labels = 'Frogs', 'Hogs', 'Dogs', 'Logs' @@ -5800,7 +5805,7 @@ def test_pie_center_radius(): plt.axis('equal') -@image_comparison(['pie_linewidth_2.png'], style='mpl20') +@image_comparison(['pie_linewidth_2.png'], style='mpl20', tol=0.01) def test_pie_linewidth_2(): # The slices will be ordered and plotted counter-clockwise. labels = 'Frogs', 'Hogs', 'Dogs', 'Logs' @@ -5815,7 +5820,7 @@ def test_pie_linewidth_2(): plt.axis('equal') -@image_comparison(['pie_ccw_true.png'], style='mpl20') +@image_comparison(['pie_ccw_true.png'], style='mpl20', tol=0.01) def test_pie_ccw_true(): # The slices will be ordered and plotted counter-clockwise. labels = 'Frogs', 'Hogs', 'Dogs', 'Logs' @@ -5830,7 +5835,7 @@ def test_pie_ccw_true(): plt.axis('equal') -@image_comparison(['pie_frame_grid.png'], style='mpl20') +@image_comparison(['pie_frame_grid.png'], style='mpl20', tol=0.002) def test_pie_frame_grid(): # The slices will be ordered and plotted counter-clockwise. labels = 'Frogs', 'Hogs', 'Dogs', 'Logs' @@ -5857,7 +5862,7 @@ def test_pie_frame_grid(): plt.axis('equal') -@image_comparison(['pie_rotatelabels_true.png'], style='mpl20') +@image_comparison(['pie_rotatelabels_true.png'], style='mpl20', tol=0.009) def test_pie_rotatelabels_true(): # The slices will be ordered and plotted counter-clockwise. labels = 'Hogwarts', 'Frogs', 'Dogs', 'Logs' @@ -5872,7 +5877,7 @@ def test_pie_rotatelabels_true(): plt.axis('equal') -@image_comparison(['pie_no_label.png']) +@image_comparison(['pie_no_label.png'], tol=0.01) def test_pie_nolabel_but_legend(): labels = 'Frogs', 'Hogs', 'Dogs', 'Logs' sizes = [15, 30, 45, 10] @@ -5886,7 +5891,7 @@ def test_pie_nolabel_but_legend(): plt.legend() -@image_comparison(['pie_shadow.png'], style='mpl20') +@image_comparison(['pie_shadow.png'], style='mpl20', tol=0.002) def test_pie_shadow(): # Also acts as a test for the shade argument of Shadow sizes = [15, 30, 45, 10] diff --git a/lib/matplotlib/tests/test_colors.py b/lib/matplotlib/tests/test_colors.py index 35030b6bea7a..49761ba20307 100644 --- a/lib/matplotlib/tests/test_colors.py +++ b/lib/matplotlib/tests/test_colors.py @@ -1371,38 +1371,38 @@ def test_scalarmappable_to_rgba(bytes): # uint8 RGBA x = np.ones((2, 3, 4), dtype=np.uint8) expected = x.copy() if bytes else x.astype(np.float32)/255 - np.testing.assert_array_equal(sm.to_rgba(x, bytes=bytes), expected) + np.testing.assert_almost_equal(sm.to_rgba(x, bytes=bytes), expected) # uint8 RGB expected[..., 3] = alpha_1 - np.testing.assert_array_equal(sm.to_rgba(x[..., :3], bytes=bytes), expected) + np.testing.assert_almost_equal(sm.to_rgba(x[..., :3], bytes=bytes), expected) # uint8 masked RGBA xm = np.ma.masked_array(x, mask=np.zeros_like(x)) xm.mask[0, 0, 0] = True expected = x.copy() if bytes else x.astype(np.float32)/255 expected[0, 0, 3] = 0 - np.testing.assert_array_equal(sm.to_rgba(xm, bytes=bytes), expected) + np.testing.assert_almost_equal(sm.to_rgba(xm, bytes=bytes), expected) # uint8 masked RGB expected[..., 3] = alpha_1 expected[0, 0, 3] = 0 - np.testing.assert_array_equal(sm.to_rgba(xm[..., :3], bytes=bytes), expected) + np.testing.assert_almost_equal(sm.to_rgba(xm[..., :3], bytes=bytes), expected) # float RGBA x = np.ones((2, 3, 4), dtype=float) * 0.5 expected = (x * 255).astype(np.uint8) if bytes else x.copy() - np.testing.assert_array_equal(sm.to_rgba(x, bytes=bytes), expected) + np.testing.assert_almost_equal(sm.to_rgba(x, bytes=bytes), expected) # float RGB expected[..., 3] = alpha_1 - np.testing.assert_array_equal(sm.to_rgba(x[..., :3], bytes=bytes), expected) + np.testing.assert_almost_equal(sm.to_rgba(x[..., :3], bytes=bytes), expected) # float masked RGBA xm = np.ma.masked_array(x, mask=np.zeros_like(x)) xm.mask[0, 0, 0] = True expected = (x * 255).astype(np.uint8) if bytes else x.copy() expected[0, 0, 3] = 0 - np.testing.assert_array_equal(sm.to_rgba(xm, bytes=bytes), expected) + np.testing.assert_almost_equal(sm.to_rgba(xm, bytes=bytes), expected) # float masked RGB expected[..., 3] = alpha_1 expected[0, 0, 3] = 0 - np.testing.assert_array_equal(sm.to_rgba(xm[..., :3], bytes=bytes), expected) + np.testing.assert_almost_equal(sm.to_rgba(xm[..., :3], bytes=bytes), expected) def test_failed_conversions(): From 0e42de70641413d2aa4140601a70d438032dc0cf Mon Sep 17 00:00:00 2001 From: David Stansby Date: Fri, 29 Dec 2023 10:35:34 +0000 Subject: [PATCH 62/87] Backport PR #27581: CI: install German lanugage packs an ubuntu test runners Merge pull request #27581 from tacaswell/ci/de_locale CI: install German lanugage packs an ubuntu test runners (cherry picked from commit 4df729a4755a4de6af706f069e23b1b3cdeab839) --- .github/workflows/tests.yml | 1 + azure-pipelines.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 76290bfcc0d1..a9724425b60d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -118,6 +118,7 @@ jobs: gir1.2-gtk-3.0 \ graphviz \ inkscape \ + language-pack-de \ lcov \ libcairo2 \ libcairo2-dev \ diff --git a/azure-pipelines.yml b/azure-pipelines.yml index bb38804ae121..4e01d67f7385 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -101,6 +101,7 @@ stages: gir1.2-gtk-3.0 \ graphviz \ inkscape \ + language-pack-de \ libcairo2 \ libgirepository-1.0-1 \ lmodern \ From cf8735a91baed6fea30cc914d43d918a647aca1e Mon Sep 17 00:00:00 2001 From: Ruth Comer <10599679+rcomer@users.noreply.github.com> Date: Sun, 21 Jan 2024 10:53:28 +0000 Subject: [PATCH 63/87] Backport PR #27678: DOC: selecting individual colors from a colormap --- .flake8 | 1 + .../color/individual_colors_from_cmap.py | 61 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 galleries/examples/color/individual_colors_from_cmap.py diff --git a/.flake8 b/.flake8 index ee739cdf4231..f6d6b8d8c6df 100644 --- a/.flake8 +++ b/.flake8 @@ -73,6 +73,7 @@ per-file-ignores = galleries/users_explain/text/text_props.py: E501 galleries/examples/animation/frame_grabbing_sgskip.py: E402 + galleries/examples/color/individual_colors_from_cmap.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 diff --git a/galleries/examples/color/individual_colors_from_cmap.py b/galleries/examples/color/individual_colors_from_cmap.py new file mode 100644 index 000000000000..572fb33e6f11 --- /dev/null +++ b/galleries/examples/color/individual_colors_from_cmap.py @@ -0,0 +1,61 @@ +""" +=========================================== +Selecting individual colors from a colormap +=========================================== + +Sometimes we want to use more colors or a different set of colors than the default color +cycle provides. Selecting individual colors from one of the provided colormaps can be a +convenient way to do this. + +Once we have hold of a `.Colormap` instance, the individual colors can be accessed +by passing it an index. If we want a specific number of colors taken at regular +intervals from a continuous colormap, we can create a new colormap using the +`~.Colormap.resampled` method. + +For more details about manipulating colormaps, see :ref:`colormap-manipulation`. +""" + +import matplotlib.pyplot as plt + +import matplotlib as mpl + +n_lines = 21 + +cmap = mpl.colormaps.get_cmap('plasma').resampled(n_lines) + +fig, ax = plt.subplots(layout='constrained') + +for i in range(n_lines): + ax.plot([0, i], color=cmap(i)) + +plt.show() + +# %% +# Instead of passing colors one by one to `~.Axes.plot`, we can replace the default +# color cycle with a different set of colors. Specifying a `~cycler.cycler` instance +# within `.rcParams` achieves that. See :ref:`color_cycle` for details. + + +from cycler import cycler + +cmap = mpl.colormaps.get_cmap('Dark2') +colors = cmap(range(cmap.N)) # cmap.N is number of unique colors in the colormap + +with mpl.rc_context({'axes.prop_cycle': cycler(color=colors)}): + + fig, ax = plt.subplots(layout='constrained') + + for i in range(n_lines): + ax.plot([0, i]) + +plt.show() + +# %% +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.colors.Colormap` +# - `matplotlib.colors.Colormap.resampled` From 47b5ba315f359c0e94665e43514ec65171f87bbd Mon Sep 17 00:00:00 2001 From: Ruth Comer <10599679+rcomer@users.noreply.github.com> Date: Sun, 21 Jan 2024 10:53:28 +0000 Subject: [PATCH 64/87] Backport PR #27678: DOC: selecting individual colors from a colormap --- .flake8 | 1 + .../color/individual_colors_from_cmap.py | 61 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 galleries/examples/color/individual_colors_from_cmap.py diff --git a/.flake8 b/.flake8 index ee739cdf4231..f6d6b8d8c6df 100644 --- a/.flake8 +++ b/.flake8 @@ -73,6 +73,7 @@ per-file-ignores = galleries/users_explain/text/text_props.py: E501 galleries/examples/animation/frame_grabbing_sgskip.py: E402 + galleries/examples/color/individual_colors_from_cmap.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 diff --git a/galleries/examples/color/individual_colors_from_cmap.py b/galleries/examples/color/individual_colors_from_cmap.py new file mode 100644 index 000000000000..572fb33e6f11 --- /dev/null +++ b/galleries/examples/color/individual_colors_from_cmap.py @@ -0,0 +1,61 @@ +""" +=========================================== +Selecting individual colors from a colormap +=========================================== + +Sometimes we want to use more colors or a different set of colors than the default color +cycle provides. Selecting individual colors from one of the provided colormaps can be a +convenient way to do this. + +Once we have hold of a `.Colormap` instance, the individual colors can be accessed +by passing it an index. If we want a specific number of colors taken at regular +intervals from a continuous colormap, we can create a new colormap using the +`~.Colormap.resampled` method. + +For more details about manipulating colormaps, see :ref:`colormap-manipulation`. +""" + +import matplotlib.pyplot as plt + +import matplotlib as mpl + +n_lines = 21 + +cmap = mpl.colormaps.get_cmap('plasma').resampled(n_lines) + +fig, ax = plt.subplots(layout='constrained') + +for i in range(n_lines): + ax.plot([0, i], color=cmap(i)) + +plt.show() + +# %% +# Instead of passing colors one by one to `~.Axes.plot`, we can replace the default +# color cycle with a different set of colors. Specifying a `~cycler.cycler` instance +# within `.rcParams` achieves that. See :ref:`color_cycle` for details. + + +from cycler import cycler + +cmap = mpl.colormaps.get_cmap('Dark2') +colors = cmap(range(cmap.N)) # cmap.N is number of unique colors in the colormap + +with mpl.rc_context({'axes.prop_cycle': cycler(color=colors)}): + + fig, ax = plt.subplots(layout='constrained') + + for i in range(n_lines): + ax.plot([0, i]) + +plt.show() + +# %% +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.colors.Colormap` +# - `matplotlib.colors.Colormap.resampled` From a9ff1e78242674326064b73e13e68337631e1a30 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 22 Jan 2024 12:21:14 -0500 Subject: [PATCH 65/87] Backport PR #27670: Implement macos AppDelegate --- src/_macosx.m | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/_macosx.m b/src/_macosx.m index 6be86cfe7367..47df6c37da93 100755 --- a/src/_macosx.m +++ b/src/_macosx.m @@ -100,6 +100,9 @@ static int wait_for_stdin() { } /* ---------------------------- Cocoa classes ---------------------------- */ +@interface MatplotlibAppDelegate : NSObject +- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app; +@end @interface Window : NSWindow { PyObject* manager; @@ -195,6 +198,7 @@ static void lazy_init(void) { NSApp = [NSApplication sharedApplication]; [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + [NSApp setDelegate: [[[MatplotlibAppDelegate alloc] init] autorelease]]; // Run our own event loop while waiting for stdin on the Python side // this is needed to keep the application responsive while waiting for input @@ -779,6 +783,12 @@ int mpl_check_modifier( }, }; +@implementation MatplotlibAppDelegate +- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app { + return YES; +} +@end + @interface NavigationToolbar2Handler : NSObject { PyObject* toolbar; NSButton* panbutton; From c6a86a51ee44938b44f17e37be2d0aa1f482792e Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Wed, 24 Jan 2024 00:11:49 +0100 Subject: [PATCH 66/87] Backport PR #27681: doc: fix Patch.contains_point docstring example --- lib/matplotlib/patches.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/patches.py b/lib/matplotlib/patches.py index f80df92c62fc..ce012b0faf98 100644 --- a/lib/matplotlib/patches.py +++ b/lib/matplotlib/patches.py @@ -190,10 +190,10 @@ def contains_point(self, point, radius=None): transform them first: >>> center = 0, 0 - >>> c = Circle(center, radius=1) + >>> c = Circle(center, radius=3) >>> plt.gca().add_patch(c) - >>> transformed_center = c.get_transform().transform(center) - >>> c.contains_point(transformed_center) + >>> transformed_interior_point = c.get_data_transform().transform((0, 2)) + >>> c.contains_point(transformed_interior_point) True """ From b0e7df368248c37bc67adab2efcbf5b27f8fabfb Mon Sep 17 00:00:00 2001 From: Kyle Sunden Date: Wed, 24 Jan 2024 17:57:45 -0600 Subject: [PATCH 67/87] Backport PR #27044: Fix quiver key plot when angles='xy' and/or scale_units='xy' --- lib/matplotlib/quiver.py | 30 ++++++---- .../test_quiver/quiver_key_xy.png | Bin 20249 -> 20122 bytes lib/matplotlib/tests/test_quiver.py | 56 ++++++++++++++++++ 3 files changed, 73 insertions(+), 13 deletions(-) diff --git a/lib/matplotlib/quiver.py b/lib/matplotlib/quiver.py index c8f8ba566106..5dbf7970f933 100644 --- a/lib/matplotlib/quiver.py +++ b/lib/matplotlib/quiver.py @@ -263,7 +263,7 @@ def __init__(self, Q, X, Y, U, label, The key label (e.g., length and units of the key). angle : float, default: 0 The angle of the key arrow, in degrees anti-clockwise from the - x-axis. + horizontal axis. coordinates : {'axes', 'figure', 'data', 'inches'}, default: 'axes' Coordinate system and units for *X*, *Y*: 'axes' and 'figure' are normalized coordinate systems with (0, 0) in the lower left and @@ -327,10 +327,8 @@ def _init(self): Umask=ma.nomask): u = self.U * np.cos(np.radians(self.angle)) v = self.U * np.sin(np.radians(self.angle)) - angle = (self.Q.angles if isinstance(self.Q.angles, str) - else 'uv') - self.verts = self.Q._make_verts( - np.array([u]), np.array([v]), angle) + self.verts = self.Q._make_verts([[0., 0.]], + np.array([u]), np.array([v]), 'uv') kwargs = self.Q.polykw kwargs.update(self.kw) self.vector = mcollections.PolyCollection( @@ -521,7 +519,7 @@ def _init(self): # _make_verts sets self.scale if not already specified if (self._dpi_at_last_init != self.axes.figure.dpi and self.scale is None): - self._make_verts(self.U, self.V, self.angles) + self._make_verts(self.XY, self.U, self.V, self.angles) self._dpi_at_last_init = self.axes.figure.dpi @@ -537,7 +535,7 @@ def get_datalim(self, transData): @martist.allow_rasterization def draw(self, renderer): self._init() - verts = self._make_verts(self.U, self.V, self.angles) + verts = self._make_verts(self.XY, self.U, self.V, self.angles) self.set_verts(verts, closed=False) super().draw(renderer) self.stale = False @@ -594,33 +592,38 @@ def _set_transform(self): self.set_transform(trans) return trans - def _angles_lengths(self, U, V, eps=1): - xy = self.axes.transData.transform(self.XY) + # Calculate angles and lengths for segment between (x, y), (x+u, y+v) + def _angles_lengths(self, XY, U, V, eps=1): + xy = self.axes.transData.transform(XY) uv = np.column_stack((U, V)) - xyp = self.axes.transData.transform(self.XY + eps * uv) + xyp = self.axes.transData.transform(XY + eps * uv) dxy = xyp - xy angles = np.arctan2(dxy[:, 1], dxy[:, 0]) lengths = np.hypot(*dxy.T) / eps return angles, lengths - def _make_verts(self, U, V, angles): + # XY is stacked [X, Y]. + # See quiver() doc for meaning of X, Y, U, V, angles. + def _make_verts(self, XY, U, V, angles): uv = (U + V * 1j) str_angles = angles if isinstance(angles, str) else '' if str_angles == 'xy' and self.scale_units == 'xy': # Here eps is 1 so that if we get U, V by diffing # the X, Y arrays, the vectors will connect the # points, regardless of the axis scaling (including log). - angles, lengths = self._angles_lengths(U, V, eps=1) + angles, lengths = self._angles_lengths(XY, U, V, eps=1) elif str_angles == 'xy' or self.scale_units == 'xy': # Calculate eps based on the extents of the plot # so that we don't end up with roundoff error from # adding a small number to a large. eps = np.abs(self.axes.dataLim.extents).max() * 0.001 - angles, lengths = self._angles_lengths(U, V, eps=eps) + angles, lengths = self._angles_lengths(XY, U, V, eps=eps) + if str_angles and self.scale_units == 'xy': a = lengths else: a = np.abs(uv) + if self.scale is None: sn = max(10, math.sqrt(self.N)) if self.Umask is not ma.nomask: @@ -630,6 +633,7 @@ def _make_verts(self, U, V, angles): # crude auto-scaling # scale is typical arrow length as a multiple of the arrow width scale = 1.8 * amean * sn / self.span + if self.scale_units is None: if self.scale is None: self.scale = scale diff --git a/lib/matplotlib/tests/baseline_images/test_quiver/quiver_key_xy.png b/lib/matplotlib/tests/baseline_images/test_quiver/quiver_key_xy.png index 8475b027093083cf82d7d59ee6f62d3fa2717b86..f4b2b83d08e2b46844a64755c82588a38cc7442e 100644 GIT binary patch literal 20122 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 aK3BHL{&xqmamGIACK((y(aF`a3I1Qf3w3J% literal 20249 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 Date: Tue, 30 Jan 2024 05:28:10 -0500 Subject: [PATCH 68/87] Backport PR #27716: fix default image format in gtk4 savefig dialog --- lib/matplotlib/backends/backend_gtk4.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/backends/backend_gtk4.py b/lib/matplotlib/backends/backend_gtk4.py index cb4006048d55..7e73a4863212 100644 --- a/lib/matplotlib/backends/backend_gtk4.py +++ b/lib/matplotlib/backends/backend_gtk4.py @@ -361,7 +361,7 @@ def save_figure(self, *args): formats = [formats[default_format], *formats[:default_format], *formats[default_format+1:]] dialog.add_choice('format', 'File format', formats, formats) - dialog.set_choice('format', formats[default_format]) + dialog.set_choice('format', formats[0]) dialog.set_current_folder(Gio.File.new_for_path( os.path.expanduser(mpl.rcParams['savefig.directory']))) From f80b03f22b10f6560fe01aebf6fac21bbb0a1280 Mon Sep 17 00:00:00 2001 From: hannah Date: Sat, 3 Feb 2024 19:13:40 -0500 Subject: [PATCH 69/87] Backport PR #27708: DOC: update colors from colormaps example --- .flake8 | 1 - .../color/individual_colors_from_cmap.py | 46 ++++++++++--------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/.flake8 b/.flake8 index f6d6b8d8c6df..ee739cdf4231 100644 --- a/.flake8 +++ b/.flake8 @@ -73,7 +73,6 @@ per-file-ignores = galleries/users_explain/text/text_props.py: E501 galleries/examples/animation/frame_grabbing_sgskip.py: E402 - galleries/examples/color/individual_colors_from_cmap.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 diff --git a/galleries/examples/color/individual_colors_from_cmap.py b/galleries/examples/color/individual_colors_from_cmap.py index 572fb33e6f11..1a14bd6b2ae1 100644 --- a/galleries/examples/color/individual_colors_from_cmap.py +++ b/galleries/examples/color/individual_colors_from_cmap.py @@ -7,50 +7,54 @@ cycle provides. Selecting individual colors from one of the provided colormaps can be a convenient way to do this. -Once we have hold of a `.Colormap` instance, the individual colors can be accessed -by passing it an index. If we want a specific number of colors taken at regular -intervals from a continuous colormap, we can create a new colormap using the -`~.Colormap.resampled` method. +We can retrieve colors from any `.Colormap` by calling it with a float or a list of +floats in the range [0, 1]; e.g. ``cmap(0.5)`` will give the middle color. See also +`.Colormap.__call__`. -For more details about manipulating colormaps, see :ref:`colormap-manipulation`. +Extracting colors from a continuous colormap +-------------------------------------------- """ import matplotlib.pyplot as plt +import numpy as np import matplotlib as mpl n_lines = 21 +cmap = mpl.colormaps['plasma'] -cmap = mpl.colormaps.get_cmap('plasma').resampled(n_lines) +# Take colors at regular intervals spanning the colormap. +colors = cmap(np.linspace(0, 1, n_lines)) fig, ax = plt.subplots(layout='constrained') -for i in range(n_lines): - ax.plot([0, i], color=cmap(i)) +for i, color in enumerate(colors): + ax.plot([0, i], color=color) plt.show() # %% -# Instead of passing colors one by one to `~.Axes.plot`, we can replace the default -# color cycle with a different set of colors. Specifying a `~cycler.cycler` instance -# within `.rcParams` achieves that. See :ref:`color_cycle` for details. - - -from cycler import cycler - -cmap = mpl.colormaps.get_cmap('Dark2') -colors = cmap(range(cmap.N)) # cmap.N is number of unique colors in the colormap +# +# Extracting colors from a discrete colormap +# ------------------------------------------ +# The list of all colors in a `.ListedColormap` is available as the ``colors`` +# attribute. -with mpl.rc_context({'axes.prop_cycle': cycler(color=colors)}): +colors = mpl.colormaps['Dark2'].colors - fig, ax = plt.subplots(layout='constrained') +fig, ax = plt.subplots(layout='constrained') - for i in range(n_lines): - ax.plot([0, i]) +for i, color in enumerate(colors): + ax.plot([0, i], color=color) plt.show() # %% +# See Also +# -------- +# +# For more details about manipulating colormaps, see :ref:`colormap-manipulation`. To +# change the default color cycle, see :ref:`color_cycle`. # # .. admonition:: References # From 5b26b119af6df1ae9f68b42ed257b51365db200f Mon Sep 17 00:00:00 2001 From: hannah Date: Sat, 3 Feb 2024 19:13:40 -0500 Subject: [PATCH 70/87] Backport PR #27708: DOC: update colors from colormaps example --- .flake8 | 1 - .../color/individual_colors_from_cmap.py | 46 ++++++++++--------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/.flake8 b/.flake8 index f6d6b8d8c6df..ee739cdf4231 100644 --- a/.flake8 +++ b/.flake8 @@ -73,7 +73,6 @@ per-file-ignores = galleries/users_explain/text/text_props.py: E501 galleries/examples/animation/frame_grabbing_sgskip.py: E402 - galleries/examples/color/individual_colors_from_cmap.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 diff --git a/galleries/examples/color/individual_colors_from_cmap.py b/galleries/examples/color/individual_colors_from_cmap.py index 572fb33e6f11..1a14bd6b2ae1 100644 --- a/galleries/examples/color/individual_colors_from_cmap.py +++ b/galleries/examples/color/individual_colors_from_cmap.py @@ -7,50 +7,54 @@ cycle provides. Selecting individual colors from one of the provided colormaps can be a convenient way to do this. -Once we have hold of a `.Colormap` instance, the individual colors can be accessed -by passing it an index. If we want a specific number of colors taken at regular -intervals from a continuous colormap, we can create a new colormap using the -`~.Colormap.resampled` method. +We can retrieve colors from any `.Colormap` by calling it with a float or a list of +floats in the range [0, 1]; e.g. ``cmap(0.5)`` will give the middle color. See also +`.Colormap.__call__`. -For more details about manipulating colormaps, see :ref:`colormap-manipulation`. +Extracting colors from a continuous colormap +-------------------------------------------- """ import matplotlib.pyplot as plt +import numpy as np import matplotlib as mpl n_lines = 21 +cmap = mpl.colormaps['plasma'] -cmap = mpl.colormaps.get_cmap('plasma').resampled(n_lines) +# Take colors at regular intervals spanning the colormap. +colors = cmap(np.linspace(0, 1, n_lines)) fig, ax = plt.subplots(layout='constrained') -for i in range(n_lines): - ax.plot([0, i], color=cmap(i)) +for i, color in enumerate(colors): + ax.plot([0, i], color=color) plt.show() # %% -# Instead of passing colors one by one to `~.Axes.plot`, we can replace the default -# color cycle with a different set of colors. Specifying a `~cycler.cycler` instance -# within `.rcParams` achieves that. See :ref:`color_cycle` for details. - - -from cycler import cycler - -cmap = mpl.colormaps.get_cmap('Dark2') -colors = cmap(range(cmap.N)) # cmap.N is number of unique colors in the colormap +# +# Extracting colors from a discrete colormap +# ------------------------------------------ +# The list of all colors in a `.ListedColormap` is available as the ``colors`` +# attribute. -with mpl.rc_context({'axes.prop_cycle': cycler(color=colors)}): +colors = mpl.colormaps['Dark2'].colors - fig, ax = plt.subplots(layout='constrained') +fig, ax = plt.subplots(layout='constrained') - for i in range(n_lines): - ax.plot([0, i]) +for i, color in enumerate(colors): + ax.plot([0, i], color=color) plt.show() # %% +# See Also +# -------- +# +# For more details about manipulating colormaps, see :ref:`colormap-manipulation`. To +# change the default color cycle, see :ref:`color_cycle`. # # .. admonition:: References # From 6ad534ad7fd9659ae5b7a252904f6118922d69b9 Mon Sep 17 00:00:00 2001 From: Greg Lucas Date: Wed, 7 Feb 2024 20:33:08 -0700 Subject: [PATCH 71/87] Backport PR #27755: Allow threads during macos event loop --- src/_macosx.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/_macosx.m b/src/_macosx.m index 47df6c37da93..3fac8b41f84e 100755 --- a/src/_macosx.m +++ b/src/_macosx.m @@ -461,6 +461,8 @@ int mpl_check_modifier( return NULL; } + Py_BEGIN_ALLOW_THREADS + NSDate* date = (timeout > 0.0) ? [NSDate dateWithTimeIntervalSinceNow: timeout] : [NSDate distantFuture]; @@ -473,6 +475,8 @@ int mpl_check_modifier( [NSApp sendEvent: event]; } + Py_END_ALLOW_THREADS + Py_RETURN_NONE; } From ae7a8dc7ecc28e9bdf187cc5fb7ec57a329cdaa7 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 12 Feb 2024 09:41:29 -0500 Subject: [PATCH 72/87] Backport PR #27776: Better document the relation between figure and manager --- lib/matplotlib/backend_bases.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 958b6e0e1c21..fd5e70433fa7 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -2584,9 +2584,13 @@ class FigureManagerBase: backend-independent way. It's an adapter for the real (GUI) framework that represents the visual figure on screen. - GUI backends define from this class to translate common operations such + The figure manager is connected to a specific canvas instance, which in turn + is connected to a specific figure instance. To access a figure manager for + a given figure in user code, you typically use ``fig.canvas.manager``. + + GUI backends derive from this class to translate common operations such as *show* or *resize* to the GUI-specific code. Non-GUI backends do not - support these operations an can just use the base class. + support these operations and can just use the base class. This following basic operations are accessible: @@ -2771,6 +2775,11 @@ def set_window_title(self, title): Set the title text of the window containing the figure. This has no effect for non-GUI (e.g., PS) backends. + + Examples + -------- + >>> fig = plt.figure() + >>> fig.canvas.manager.set_window_title('My figure') """ From df6c029cf5365ccf762c6e9c15b0f2775ca11b5a Mon Sep 17 00:00:00 2001 From: Kyle Sunden Date: Mon, 12 Feb 2024 13:54:18 -0600 Subject: [PATCH 73/87] Backport PR #27773: MNT: pcolormesh robust underflow --- lib/matplotlib/axes/_axes.py | 2 +- lib/matplotlib/tests/test_axes.py | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 499c221bb5c9..3bd355bdc663 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -5852,7 +5852,7 @@ def _pcolorargs(self, funcname, *args, shading='auto', **kwargs): def _interp_grid(X): # helper for below if np.shape(X)[1] > 1: - dX = np.diff(X, axis=1)/2. + dX = np.diff(X, axis=1) * 0.5 if not (np.all(dX >= 0) or np.all(dX <= 0)): _api.warn_external( f"The input coordinates to {funcname} are " diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 3fa7cbe0e34d..0372747009b6 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -1499,6 +1499,19 @@ def test_pcolorargs(): ax.pcolormesh(X, Y, Z, shading='auto') +def test_pcolormesh_underflow_error(): + """ + Test that underflow errors don't crop up in pcolormesh. Probably + a numpy bug (https://github.com/numpy/numpy/issues/25810). + """ + with np.errstate(under="raise"): + x = np.arange(0, 3, 0.1) + y = np.arange(0, 6, 0.1) + z = np.random.randn(len(y), len(x)) + fig, ax = plt.subplots() + ax.pcolormesh(x, y, z) + + def test_pcolorargs_with_read_only(): x = np.arange(6).reshape(2, 3) xmask = np.broadcast_to([False, True, False], x.shape) # read-only array From 1a2efec7a9ab0e32b374776cc3516d7ffe428ea7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 19:12:01 +0000 Subject: [PATCH 74/87] Bump the actions group with 3 updates Bumps the actions group with 3 updates: [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel), [peter-evans/create-or-update-comment](https://github.com/peter-evans/create-or-update-comment) and [scientific-python/upload-nightly-action](https://github.com/scientific-python/upload-nightly-action). Updates `pypa/cibuildwheel` from 2.16.2 to 2.16.4 - [Release notes](https://github.com/pypa/cibuildwheel/releases) - [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md) - [Commits](https://github.com/pypa/cibuildwheel/compare/fff9ec32ed25a9c576750c91e06b410ed0c15db7...0b04ab1040366101259658b355777e4ff2d16f83) Updates `peter-evans/create-or-update-comment` from 3 to 4 - [Release notes](https://github.com/peter-evans/create-or-update-comment/releases) - [Commits](https://github.com/peter-evans/create-or-update-comment/compare/v3...v4) --- updated-dependencies: - dependency-name: pypa/cibuildwheel dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions - dependency-name: peter-evans/create-or-update-comment dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions ... Signed-off-by: dependabot[bot] --- .github/workflows/cibuildwheel.yml | 10 +++++----- .github/workflows/good-first-issue.yml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/cibuildwheel.yml b/.github/workflows/cibuildwheel.yml index c31842a4ce1f..cc2b203153b2 100644 --- a/.github/workflows/cibuildwheel.yml +++ b/.github/workflows/cibuildwheel.yml @@ -139,7 +139,7 @@ jobs: path: dist/ - name: Build wheels for CPython 3.12 - uses: pypa/cibuildwheel@fff9ec32ed25a9c576750c91e06b410ed0c15db7 # v2.16.2 + uses: pypa/cibuildwheel@0b04ab1040366101259658b355777e4ff2d16f83 # v2.16.4 with: package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} env: @@ -147,7 +147,7 @@ jobs: CIBW_ARCHS: ${{ matrix.cibw_archs }} - name: Build wheels for CPython 3.11 - uses: pypa/cibuildwheel@fff9ec32ed25a9c576750c91e06b410ed0c15db7 # v2.16.2 + uses: pypa/cibuildwheel@0b04ab1040366101259658b355777e4ff2d16f83 # v2.16.4 with: package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} env: @@ -155,7 +155,7 @@ jobs: CIBW_ARCHS: ${{ matrix.cibw_archs }} - name: Build wheels for CPython 3.10 - uses: pypa/cibuildwheel@fff9ec32ed25a9c576750c91e06b410ed0c15db7 # v2.16.2 + uses: pypa/cibuildwheel@0b04ab1040366101259658b355777e4ff2d16f83 # v2.16.4 with: package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} env: @@ -163,7 +163,7 @@ jobs: CIBW_ARCHS: ${{ matrix.cibw_archs }} - name: Build wheels for CPython 3.9 - uses: pypa/cibuildwheel@fff9ec32ed25a9c576750c91e06b410ed0c15db7 # v2.16.2 + uses: pypa/cibuildwheel@0b04ab1040366101259658b355777e4ff2d16f83 # v2.16.4 with: package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} env: @@ -171,7 +171,7 @@ jobs: CIBW_ARCHS: ${{ matrix.cibw_archs }} - name: Build wheels for PyPy - uses: pypa/cibuildwheel@fff9ec32ed25a9c576750c91e06b410ed0c15db7 # v2.16.2 + uses: pypa/cibuildwheel@0b04ab1040366101259658b355777e4ff2d16f83 # v2.16.4 with: package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} env: diff --git a/.github/workflows/good-first-issue.yml b/.github/workflows/good-first-issue.yml index 30a9917d6d1e..baac893a6a7a 100644 --- a/.github/workflows/good-first-issue.yml +++ b/.github/workflows/good-first-issue.yml @@ -11,7 +11,7 @@ jobs: issues: write steps: - name: Add comment - uses: peter-evans/create-or-update-comment@v3 + uses: peter-evans/create-or-update-comment@v4 with: issue-number: ${{ github.event.issue.number }} body: | From bef02fa30a1f8f8903cbfc36a9dee3005541c31b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 19:15:37 +0000 Subject: [PATCH 75/87] Bump the actions group with 2 updates Bumps the actions group with 2 updates: [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) and [codecov/codecov-action](https://github.com/codecov/codecov-action). Updates `pypa/cibuildwheel` from 2.16.4 to 2.16.5 - [Release notes](https://github.com/pypa/cibuildwheel/releases) - [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md) - [Commits](https://github.com/pypa/cibuildwheel/compare/0b04ab1040366101259658b355777e4ff2d16f83...ce3fb7832089eb3e723a0a99cab7f3eaccf074fd) --- updated-dependencies: - dependency-name: pypa/cibuildwheel dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions ... Signed-off-by: dependabot[bot] --- .github/workflows/cibuildwheel.yml | 10 +++++----- .github/workflows/cygwin.yml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/cibuildwheel.yml b/.github/workflows/cibuildwheel.yml index cc2b203153b2..95b46216cf39 100644 --- a/.github/workflows/cibuildwheel.yml +++ b/.github/workflows/cibuildwheel.yml @@ -139,7 +139,7 @@ jobs: path: dist/ - name: Build wheels for CPython 3.12 - uses: pypa/cibuildwheel@0b04ab1040366101259658b355777e4ff2d16f83 # v2.16.4 + uses: pypa/cibuildwheel@ce3fb7832089eb3e723a0a99cab7f3eaccf074fd # v2.16.5 with: package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} env: @@ -147,7 +147,7 @@ jobs: CIBW_ARCHS: ${{ matrix.cibw_archs }} - name: Build wheels for CPython 3.11 - uses: pypa/cibuildwheel@0b04ab1040366101259658b355777e4ff2d16f83 # v2.16.4 + uses: pypa/cibuildwheel@ce3fb7832089eb3e723a0a99cab7f3eaccf074fd # v2.16.5 with: package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} env: @@ -155,7 +155,7 @@ jobs: CIBW_ARCHS: ${{ matrix.cibw_archs }} - name: Build wheels for CPython 3.10 - uses: pypa/cibuildwheel@0b04ab1040366101259658b355777e4ff2d16f83 # v2.16.4 + uses: pypa/cibuildwheel@ce3fb7832089eb3e723a0a99cab7f3eaccf074fd # v2.16.5 with: package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} env: @@ -163,7 +163,7 @@ jobs: CIBW_ARCHS: ${{ matrix.cibw_archs }} - name: Build wheels for CPython 3.9 - uses: pypa/cibuildwheel@0b04ab1040366101259658b355777e4ff2d16f83 # v2.16.4 + uses: pypa/cibuildwheel@ce3fb7832089eb3e723a0a99cab7f3eaccf074fd # v2.16.5 with: package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} env: @@ -171,7 +171,7 @@ jobs: CIBW_ARCHS: ${{ matrix.cibw_archs }} - name: Build wheels for PyPy - uses: pypa/cibuildwheel@0b04ab1040366101259658b355777e4ff2d16f83 # v2.16.4 + uses: pypa/cibuildwheel@ce3fb7832089eb3e723a0a99cab7f3eaccf074fd # v2.16.5 with: package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} env: diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml index 11ea1377b36d..cb69b4d44c17 100644 --- a/.github/workflows/cygwin.yml +++ b/.github/workflows/cygwin.yml @@ -253,4 +253,4 @@ jobs: --cov-report=xml --cov=lib --log-level=DEBUG --color=yes - name: Upload code coverage - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 From 2efbf7ed5cb03b767efc96db925bdb0c04d76787 Mon Sep 17 00:00:00 2001 From: Greg Lucas Date: Tue, 13 Feb 2024 19:51:01 -0700 Subject: [PATCH 76/87] Backport PR #27756: Add protections against infinite loop in bezier calculations --- lib/matplotlib/bezier.py | 8 ++++++++ lib/matplotlib/tests/test_bezier.py | 17 +++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 lib/matplotlib/tests/test_bezier.py diff --git a/lib/matplotlib/bezier.py b/lib/matplotlib/bezier.py index f310f287e2c0..069e20d05916 100644 --- a/lib/matplotlib/bezier.py +++ b/lib/matplotlib/bezier.py @@ -171,9 +171,17 @@ def find_bezier_t_intersecting_with_closedpath( if start_inside ^ middle_inside: t1 = middle_t + if end == middle: + # Edge case where infinite loop is possible + # Caused by large numbers relative to tolerance + return t0, t1 end = middle else: t0 = middle_t + if start == middle: + # Edge case where infinite loop is possible + # Caused by large numbers relative to tolerance + return t0, t1 start = middle start_inside = middle_inside diff --git a/lib/matplotlib/tests/test_bezier.py b/lib/matplotlib/tests/test_bezier.py new file mode 100644 index 000000000000..65e2c616e738 --- /dev/null +++ b/lib/matplotlib/tests/test_bezier.py @@ -0,0 +1,17 @@ +""" +Tests specific to the bezier module. +""" + +from matplotlib.bezier import inside_circle, split_bezier_intersecting_with_closedpath + + +def test_split_bezier_with_large_values(): + # These numbers come from gh-27753 + arrow_path = [(96950809781500.0, 804.7503795623779), + (96950809781500.0, 859.6242585800646), + (96950809781500.0, 914.4981375977513)] + in_f = inside_circle(96950809781500.0, 804.7503795623779, 0.06) + split_bezier_intersecting_with_closedpath(arrow_path, in_f) + # All we are testing is that this completes + # The failure case is an infinite loop resulting from floating point precision + # pytest will timeout if that occurs From ea407e12423f6e98a258fb7caba2d895557fb588 Mon Sep 17 00:00:00 2001 From: Greg Lucas Date: Tue, 13 Feb 2024 20:04:17 -0700 Subject: [PATCH 77/87] Backport PR #27785: FIX: be careful about communicating with subprocess --- lib/matplotlib/backends/backend_pgf.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index ccf4b800a614..46fd66357506 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -257,7 +257,8 @@ def _setup_latex_process(self, *, expect_reply=True): # Open LaTeX process for real work; register it for deletion. On # Windows, we must ensure that the subprocess has quit before being # able to delete the tmpdir in which it runs; in order to do so, we - # must first `kill()` it, and then `communicate()` with it. + # must first `kill()` it, and then `communicate()` with or `wait()` on + # it. try: self.latex = subprocess.Popen( [mpl.rcParams["pgf.texsystem"], "-halt-on-error"], @@ -274,7 +275,10 @@ def _setup_latex_process(self, *, expect_reply=True): def finalize_latex(latex): latex.kill() - latex.communicate() + try: + latex.communicate() + except RuntimeError: + latex.wait() self._finalize_latex = weakref.finalize( self, finalize_latex, self.latex) From bd969559cd9f18221d8cbd4c674c614eb043145b Mon Sep 17 00:00:00 2001 From: Kyle Sunden Date: Wed, 14 Feb 2024 18:11:08 -0600 Subject: [PATCH 78/87] Release prep v3.8.3 --- doc/users/github_stats.rst | 153 +++++++++++++----- .../prev_whats_new/github_stats_3.8.2.rst | 62 +++++++ doc/users/release_notes.rst | 1 + 3 files changed, 178 insertions(+), 38 deletions(-) create mode 100644 doc/users/prev_whats_new/github_stats_3.8.2.rst diff --git a/doc/users/github_stats.rst b/doc/users/github_stats.rst index ab2660464ceb..22226b89fa82 100644 --- a/doc/users/github_stats.rst +++ b/doc/users/github_stats.rst @@ -1,65 +1,142 @@ .. _github-stats: -GitHub statistics for 3.8.2 (Nov 17, 2023) +GitHub statistics for 3.8.3 (Feb 14, 2024) ========================================== -GitHub statistics for 2023/10/31 (tag: v3.8.1) - 2023/11/17 +GitHub statistics for 2023/11/17 (tag: v3.8.2) - 2024/02/14 These lists are automatically generated, and may be incomplete or contain duplicates. -We closed 3 issues and merged 27 pull requests. -The full list can be seen `on GitHub `__ +We closed 18 issues and merged 74 pull requests. +The full list can be seen `on GitHub `__ -The following 10 authors contributed 39 commits. +The following 25 authors contributed 133 commits. +* Allan Haldane * Antony Lee -* dohyun +* Christoph Hasse +* David Stansby +* dependabot[bot] * Elliott Sales de Andrade +* Greg Lucas * hannah +* James Salsman * Jody Klymak +* Joshua Stevenson +* judfs * Kyle Sunden +* Matthew Morrison * Oscar Gustafsson * Ruth Comer +* Samuel Diebolt +* saranti +* sdiebolt +* Shriya Kalakata +* Stefan +* Steffen Rehberg +* stevezhang1999 * Thomas A Caswell * Tim Hoffmann GitHub issues and pull requests: -Pull Requests (27): +Pull Requests (74): -* :ghpull:`27339`: Backport PR #27299 on branch v3.8.x ([MNT] swap xkcd script for humor sans) -* :ghpull:`27338`: Backport PR #27334 on branch v3.8.x (Omit MOVETO lines from nearest contour logic) -* :ghpull:`27299`: [MNT] swap xkcd script for humor sans -* :ghpull:`27334`: Omit MOVETO lines from nearest contour logic -* :ghpull:`27324`: Backport PR #27323 on branch v3.8.x ([DOC] Minor fixes for savefig-docstring) -* :ghpull:`27323`: [DOC] Minor fixes for savefig-docstring -* :ghpull:`27314`: Backport PR #27312 on branch v3.8.x (Doc: Step redirect) -* :ghpull:`27294`: Backport PR #27291 on branch v3.8.x (Expand 3D import to handle any exception not just ImportError) -* :ghpull:`27291`: Expand 3D import to handle any exception not just ImportError -* :ghpull:`27293`: Backport PR #27290 on branch v3.8.x (Ensure GIL while releasing buffer) -* :ghpull:`27283`: Backport PR #27280 on branch v3.8.x (DOC: added rest of licenses to license page) -* :ghpull:`27280`: DOC: added rest of licenses to license page -* :ghpull:`27278`: Backport PR #27276 on branch v3.8.x (Clarify behavior of ``prune`` parameter to MaxNLocator.) -* :ghpull:`27276`: Clarify behavior of ``prune`` parameter to MaxNLocator. -* :ghpull:`27272`: Backport PR #27271 on branch v3.8.x (DOC: minor fixes to dev workflow) -* :ghpull:`27269`: Backport PR #27268 on branch v3.8.x (Copy-edit various examples.) -* :ghpull:`27263`: Backport PR #27213 on branch v3.8.x (DOC: consolidated coding guide and added naming conventions table) -* :ghpull:`27258`: Backport PR #27249 on branch v3.8.x (DOC: reasoning for communications guidelines) -* :ghpull:`27255`: Backport PR #27253 on branch v3.8.x (Copy-edit the standalone colorbar tutorial) -* :ghpull:`27253`: Copy-edit the standalone colorbar tutorial -* :ghpull:`27252`: Backport PR #26669 on branch v3.8.x ([DOC] debug backends) -* :ghpull:`26669`: [DOC] debug backends -* :ghpull:`27250`: Backport PR #27219 on branch v3.8.x (Updated axes_box_aspect.py and angle_annotation.py to regularize formatting) -* :ghpull:`27219`: Updated axes_box_aspect.py and angle_annotation.py to regularize formatting -* :ghpull:`27247`: Backport PR #26703 on branch v3.8.x (moved communications guidelines from governance, updated and clarified process ) -* :ghpull:`27246`: Backport PR #27244 on branch v3.8.x (Clarify semantics of plt.matshow(..., fignum=...).) -* :ghpull:`27244`: Clarify semantics of plt.matshow(..., fignum=...). +* :ghpull:`27790`: Backport PR #27785 on branch v3.8.x (FIX: be careful about communicating with subprocess) +* :ghpull:`27789`: Backport PR #27756 on branch v3.8.x (Add protections against infinite loop in bezier calculations) +* :ghpull:`27785`: FIX: be careful about communicating with subprocess +* :ghpull:`27756`: Add protections against infinite loop in bezier calculations +* :ghpull:`27779`: Manual backport of dependabot cibw upgrades +* :ghpull:`27778`: Backport PR #27773 on branch v3.8.x (MNT: pcolormesh robust underflow) +* :ghpull:`27773`: MNT: pcolormesh robust underflow +* :ghpull:`27777`: Backport PR #27776 on branch v3.8.x (Better document the relation between figure and manager) +* :ghpull:`27776`: Better document the relation between figure and manager +* :ghpull:`27759`: Backport PR #27755 on branch v3.8.x (Allow threads during macos event loop) +* :ghpull:`27755`: Allow threads during macos event loop +* :ghpull:`27742`: Backport PR #27708 on branch v3.8.x (DOC: update colors from colormaps example) +* :ghpull:`27718`: Backport PR #27716 on branch v3.8.x (fix default image format in gtk4 savefig dialog) +* :ghpull:`27716`: fix default image format in gtk4 savefig dialog +* :ghpull:`27697`: Backport PR #27044 on branch v3.8.x (Fix quiver key plot when angles='xy' and/or scale_units='xy') +* :ghpull:`27044`: Fix quiver key plot when angles='xy' and/or scale_units='xy' +* :ghpull:`27691`: Backport PR #27681 on branch v3.8.x (doc: fix Patch.contains_point docstring example) +* :ghpull:`27681`: doc: fix Patch.contains_point docstring example +* :ghpull:`27683`: Backport PR #27670 on branch v3.8.x (Implement macos AppDelegate) +* :ghpull:`27670`: Implement macos AppDelegate +* :ghpull:`27680`: Backport PR #27678 on branch v3.8.x (DOC: selecting individual colors from a colormap) +* :ghpull:`27664`: Backport PR #27581: CI: install German language packs on ubuntu test … +* :ghpull:`27661`: Backport of pr 27647 on v3.8.x +* :ghpull:`27662`: Backport PR #27657 on branch v3.8.x (Fix Numpy 2.0 related test failures) +* :ghpull:`27657`: Fix Numpy 2.0 related test failures +* :ghpull:`27647`: Fix error that occurs when minorticks are on multi-Axes Figure with more than one boxplot +* :ghpull:`27660`: Backport PR #27624 on branch v3.8.x (Prepare for Pytest v8) +* :ghpull:`27624`: Prepare for Pytest v8 +* :ghpull:`27636`: Backport PR #27634 on branch v3.8.x (circle: Make deploy stage into a normal step) +* :ghpull:`27622`: Backport PR #27620 on branch v3.8.x (DOC: simplify histogram animation example) +* :ghpull:`27612`: Backport PR #27606 on branch v3.8.x (Pin black version) +* :ghpull:`27606`: Pin black version +* :ghpull:`27598`: Backport PR #27594 on branch v3.8.x (Cleanup viewlims example.) +* :ghpull:`27597`: Backport PR #27595 on branch v3.8.x (Fix is_sorted_and_has_non_nan for byteswapped inputs.) +* :ghpull:`27595`: Fix is_sorted_and_has_non_nan for byteswapped inputs. +* :ghpull:`27586`: Backport PR #27578 on branch v3.8.x (Fix polar labels with negative theta limit) +* :ghpull:`27578`: Fix polar labels with negative theta limit +* :ghpull:`27581`: CI: install German language packs on ubuntu test runners +* :ghpull:`27544`: Backport PR #27527 on branch v3.8.x (FIX: Add macos timers to the main thread) +* :ghpull:`27527`: FIX: Add macos timers to the main thread +* :ghpull:`27537`: Backport PR #27535 on branch v3.8.x (Update ax.legend input types) +* :ghpull:`27535`: Update ax.legend input types +* :ghpull:`27536`: Backport PR #27534 on branch v3.8.x (Clarify AxLine Params) +* :ghpull:`27534`: Clarify AxLine Params +* :ghpull:`27530`: Backport PR #27528 on branch v3.8.x (FIX: Remove runloop execution while waiting for stdin) +* :ghpull:`27528`: FIX: Remove runloop execution while waiting for stdin +* :ghpull:`27510`: Backport PR #27346 on branch v3.8.x (DOC: Show and correct default alignment parameters in text.py) +* :ghpull:`27346`: DOC: Show and correct default alignment parameters in text.py +* :ghpull:`27506`: Backport PR #27504 on branch v3.8.x (DOC: correct return type for axline) +* :ghpull:`27504`: DOC: correct return type for axline +* :ghpull:`27501`: Backport PR #27496 on branch v3.8.x (Bump actions/setup-python from 4 to 5) +* :ghpull:`27496`: Bump actions/setup-python from 4 to 5 +* :ghpull:`27484`: Backport PR #27481 on branch v3.8.x (Fixing Pylab documentation in API interface overview) +* :ghpull:`27481`: Fixing Pylab documentation in API interface overview +* :ghpull:`27467`: Manual backport of #27395 on v3.8.x +* :ghpull:`27464`: Backport PR #27316 on branch v3.8.x (DOC: Synchronize LICENSE_STIX files) +* :ghpull:`27316`: DOC: Synchronize LICENSE_STIX files +* :ghpull:`27453`: Backport PR #27434 on branch v3.8.x (FIX: Expand stairs plot-type entry intro (reattempt)) +* :ghpull:`27446`: Backport PR #27397 on branch v3.8.x (SpanSelector widget: Improve doc for ``extents``) +* :ghpull:`27397`: SpanSelector widget: Improve doc for ``extents`` +* :ghpull:`27444`: Backport PR #27441 on branch v3.8.x (Fix some minor issues with hexbin bins argument) +* :ghpull:`27441`: Fix some minor issues with hexbin bins argument +* :ghpull:`27429`: Backport PR #27411 on branch v3.8.x (DOC: multilevel tick example) +* :ghpull:`27420`: Backport PR #27325 on branch v3.8.x (Fixing Sentence Case on Section Titles in users_explain) +* :ghpull:`27413`: Backport PR #27412 on branch v3.8.x (ci: Block PyQt6 6.6.0 on Ubuntu) +* :ghpull:`27412`: ci: Block PyQt6 6.6.0 on Ubuntu +* :ghpull:`27403`: Backport PR #27386 on branch v3.8.x (Doc: add a "please use dev version" to top of contribute docs) +* :ghpull:`27384`: Backport PR #27377 on branch v3.8.x (TST: Make test_movie_writer_invalid_path locale-agnostic) +* :ghpull:`27377`: TST: Make test_movie_writer_invalid_path locale-agnostic +* :ghpull:`27379`: Backport PR #27376 on branch v3.8.x ([MNT] fix type annotations of ``fignum_exists``) +* :ghpull:`27376`: [MNT] fix type annotations of ``fignum_exists`` +* :ghpull:`27369`: Backport PR #27365 on branch v3.8.x ([DOC]: Fix menu example) +* :ghpull:`27365`: [DOC]: Fix menu example +* :ghpull:`27354`: Backport PR #27348 on branch v3.8.x (updated api/animation documentation as per standards) -Issues (3): +Issues (18): -* :ghissue:`27333`: [Bug]: Spurious lines added with some manually add contour labels -* :ghissue:`27274`: [Bug]: prune parameter of MaxNLocator has no effect -* :ghissue:`27262`: [Bug]: Segmentation fault when resizing on Python 3.12 and MacOS 14 +* :ghissue:`27437`: [Bug]: PGF backend crashes at program exit after creating a plot +* :ghissue:`27770`: [Bug]: pcolormesh issue with np.seterr(under='raise') +* :ghissue:`27720`: [Bug]: pyplot hangs at pause in sonoma 14.3 with backend MacOSX +* :ghissue:`26316`: [Bug]: quiverkey shows multiple arrows under geographical projection and angle='xy' +* :ghissue:`23178`: [Bug]: ``contains_point()`` does not appear to work? +* :ghissue:`27389`: [Bug]: Warning after update to macOS 14 "WARNING: Secure coding is not enabled for restorable state! Enable secure coding by implementing NSApplicationDelegate.applicationSupportsSecureRestorableState: and returning YES." +* :ghissue:`27645`: [TST] Upcoming dependency test failures +* :ghissue:`26484`: [Bug]: Turning on minor gridlines in a multi-Axes Figure, created with subplots(), that contains >1 boxplot results in a ValueError +* :ghissue:`27596`: [Bug]: Markers with numeric name like CARETLEFT cannot be specified using a cycler +* :ghissue:`25995`: [Bug]: _path.is_sorted is wrong for the non-native byteorder case +* :ghissue:`25568`: [Bug]: unexpected thetalim behavior in polar plot +* :ghissue:`27507`: [Bug]: Argument types for ``handles`` and ``labels`` are too strict for method ``Axes.legend`` +* :ghissue:`27503`: [Bug]: Cannot Create lines.AxLine +* :ghissue:`27515`: [Bug]: Python interpreter becomes slow at reading inputs after plotting with matplotlib +* :ghissue:`27345`: [Doc]: text alignment defaults +* :ghissue:`27461`: [Doc]: API interface overview pylab incorrect import statement: from matplotlib.pyplot import * +* :ghissue:`27383`: [Bug]: Error in Hexbin plot in Matplotlib 3.0 onward +* :ghissue:`27358`: [Doc]: Garbled menu widget example output Previous GitHub statistics diff --git a/doc/users/prev_whats_new/github_stats_3.8.2.rst b/doc/users/prev_whats_new/github_stats_3.8.2.rst new file mode 100644 index 000000000000..0e5852be394b --- /dev/null +++ b/doc/users/prev_whats_new/github_stats_3.8.2.rst @@ -0,0 +1,62 @@ +.. _github-stats-3-8-2: + +GitHub statistics for 3.8.2 (Nov 17, 2023) +========================================== + +GitHub statistics for 2023/10/31 (tag: v3.8.1) - 2023/11/17 + +These lists are automatically generated, and may be incomplete or contain duplicates. + +We closed 3 issues and merged 27 pull requests. +The full list can be seen `on GitHub `__ + +The following 10 authors contributed 39 commits. + +* Antony Lee +* dohyun +* Elliott Sales de Andrade +* hannah +* Jody Klymak +* Kyle Sunden +* Oscar Gustafsson +* Ruth Comer +* Thomas A Caswell +* Tim Hoffmann + +GitHub issues and pull requests: + +Pull Requests (27): + +* :ghpull:`27339`: Backport PR #27299 on branch v3.8.x ([MNT] swap xkcd script for humor sans) +* :ghpull:`27338`: Backport PR #27334 on branch v3.8.x (Omit MOVETO lines from nearest contour logic) +* :ghpull:`27299`: [MNT] swap xkcd script for humor sans +* :ghpull:`27334`: Omit MOVETO lines from nearest contour logic +* :ghpull:`27324`: Backport PR #27323 on branch v3.8.x ([DOC] Minor fixes for savefig-docstring) +* :ghpull:`27323`: [DOC] Minor fixes for savefig-docstring +* :ghpull:`27314`: Backport PR #27312 on branch v3.8.x (Doc: Step redirect) +* :ghpull:`27294`: Backport PR #27291 on branch v3.8.x (Expand 3D import to handle any exception not just ImportError) +* :ghpull:`27291`: Expand 3D import to handle any exception not just ImportError +* :ghpull:`27293`: Backport PR #27290 on branch v3.8.x (Ensure GIL while releasing buffer) +* :ghpull:`27283`: Backport PR #27280 on branch v3.8.x (DOC: added rest of licenses to license page) +* :ghpull:`27280`: DOC: added rest of licenses to license page +* :ghpull:`27278`: Backport PR #27276 on branch v3.8.x (Clarify behavior of ``prune`` parameter to MaxNLocator.) +* :ghpull:`27276`: Clarify behavior of ``prune`` parameter to MaxNLocator. +* :ghpull:`27272`: Backport PR #27271 on branch v3.8.x (DOC: minor fixes to dev workflow) +* :ghpull:`27269`: Backport PR #27268 on branch v3.8.x (Copy-edit various examples.) +* :ghpull:`27263`: Backport PR #27213 on branch v3.8.x (DOC: consolidated coding guide and added naming conventions table) +* :ghpull:`27258`: Backport PR #27249 on branch v3.8.x (DOC: reasoning for communications guidelines) +* :ghpull:`27255`: Backport PR #27253 on branch v3.8.x (Copy-edit the standalone colorbar tutorial) +* :ghpull:`27253`: Copy-edit the standalone colorbar tutorial +* :ghpull:`27252`: Backport PR #26669 on branch v3.8.x ([DOC] debug backends) +* :ghpull:`26669`: [DOC] debug backends +* :ghpull:`27250`: Backport PR #27219 on branch v3.8.x (Updated axes_box_aspect.py and angle_annotation.py to regularize formatting) +* :ghpull:`27219`: Updated axes_box_aspect.py and angle_annotation.py to regularize formatting +* :ghpull:`27247`: Backport PR #26703 on branch v3.8.x (moved communications guidelines from governance, updated and clarified process ) +* :ghpull:`27246`: Backport PR #27244 on branch v3.8.x (Clarify semantics of plt.matshow(..., fignum=...).) +* :ghpull:`27244`: Clarify semantics of plt.matshow(..., fignum=...). + +Issues (3): + +* :ghissue:`27333`: [Bug]: Spurious lines added with some manually add contour labels +* :ghissue:`27274`: [Bug]: prune parameter of MaxNLocator has no effect +* :ghissue:`27262`: [Bug]: Segmentation fault when resizing on Python 3.12 and MacOS 14 diff --git a/doc/users/release_notes.rst b/doc/users/release_notes.rst index bb0ee1e67a7e..88296e60c08f 100644 --- a/doc/users/release_notes.rst +++ b/doc/users/release_notes.rst @@ -22,6 +22,7 @@ Version 3.8 ../api/prev_api_changes/api_changes_3.8.1.rst ../api/prev_api_changes/api_changes_3.8.0.rst github_stats.rst + prev_whats_new/github_stats_3.8.2.rst prev_whats_new/github_stats_3.8.1.rst prev_whats_new/github_stats_3.8.0.rst From 9e18a343fb58a2978a8e27df03190ed21c61c343 Mon Sep 17 00:00:00 2001 From: Kyle Sunden Date: Wed, 14 Feb 2024 18:32:26 -0600 Subject: [PATCH 79/87] REL: v3.8.3 This is the third micro release of the 3.8 series. Highlights of the 3.8.3 release include: - Improvements to the MacOS backend - Fix hanging on `plt.pause` - Fix warnings about "Secure coding is not enabled for restorable state" - Fix crash at exit for PGF backend From 6a8ea6d60dc5b78c94e5332f452845a6d67b000e Mon Sep 17 00:00:00 2001 From: Kyle Sunden Date: Wed, 14 Feb 2024 18:33:58 -0600 Subject: [PATCH 80/87] Bump from v3.8.3 From 6175ebf59d60bd31d4a1de0e0b15154511841479 Mon Sep 17 00:00:00 2001 From: Kyle Sunden Date: Wed, 14 Feb 2024 18:44:11 -0600 Subject: [PATCH 81/87] Zenodo for v3.8.3 --- doc/_static/zenodo_cache/10661079.svg | 35 +++++++++++++++++++++++++++ doc/users/project/citing.rst | 3 +++ tools/cache_zenodo_svg.py | 1 + 3 files changed, 39 insertions(+) create mode 100644 doc/_static/zenodo_cache/10661079.svg diff --git a/doc/_static/zenodo_cache/10661079.svg b/doc/_static/zenodo_cache/10661079.svg new file mode 100644 index 000000000000..ac659bcc870f --- /dev/null +++ b/doc/_static/zenodo_cache/10661079.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.10661079 + + + 10.5281/zenodo.10661079 + + + \ No newline at end of file diff --git a/doc/users/project/citing.rst b/doc/users/project/citing.rst index 68a57650ea9c..e162a427d8b4 100644 --- a/doc/users/project/citing.rst +++ b/doc/users/project/citing.rst @@ -29,6 +29,9 @@ By version .. START OF AUTOGENERATED +v3.8.3 + .. image:: ../../_static/zenodo_cache/10661079.svg + :target: https://doi.org/10.5281/zenodo.10661079 v3.8.2 .. image:: ../../_static/zenodo_cache/10150955.svg :target: https://doi.org/10.5281/zenodo.10150955 diff --git a/tools/cache_zenodo_svg.py b/tools/cache_zenodo_svg.py index 5ac425b3efd9..b838511fe3b0 100644 --- a/tools/cache_zenodo_svg.py +++ b/tools/cache_zenodo_svg.py @@ -63,6 +63,7 @@ def _get_xdg_cache_dir(): if __name__ == "__main__": data = { + "v3.8.3": "10661079", "v3.8.2": "10150955", "v3.8.1": "10059757", "v3.8.0": "8347255", From 8e7dbc9793e777e040491402c4b27e164d2b7b74 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Sat, 17 Feb 2024 08:40:29 -0800 Subject: [PATCH 82/87] Backport PR #27794: Remove old reference to 72 DPI in figure_size_units.py --- .../examples/subplots_axes_and_figures/figure_size_units.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/galleries/examples/subplots_axes_and_figures/figure_size_units.py b/galleries/examples/subplots_axes_and_figures/figure_size_units.py index 4b87ff658f97..0ce49c937d50 100644 --- a/galleries/examples/subplots_axes_and_figures/figure_size_units.py +++ b/galleries/examples/subplots_axes_and_figures/figure_size_units.py @@ -65,8 +65,7 @@ # %% # .. [#] Unfortunately, this does not work well for the ``matplotlib inline`` -# backend in Jupyter because that backend uses a different default of -# ``rcParams['figure.dpi'] = 72``. Additionally, it saves the figure +# backend in Jupyter because that backend saves the figure # with ``bbox_inches='tight'``, which crops the figure and makes the # actual size unpredictable. From c52fd3299246ba815f365bc1943b905797676e26 Mon Sep 17 00:00:00 2001 From: Ruth Comer <10599679+rcomer@users.noreply.github.com> Date: Sun, 3 Mar 2024 13:10:40 +0000 Subject: [PATCH 83/87] Backport PR #27846: Make example in legend_elements doc more generalisable --- lib/matplotlib/collections.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 81db24d0c026..81cd13567bc0 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -1038,12 +1038,12 @@ def legend_elements(self, prop="colors", num="auto", Create legend handles and labels for a PathCollection. Each legend handle is a `.Line2D` representing the Path that was drawn, - and each label is a string what each Path represents. + and each label is a string that represents the Path. This is useful for obtaining a legend for a `~.Axes.scatter` plot; e.g.:: - scatter = plt.scatter([1, 2, 3], [4, 5, 6], c=[7, 2, 3]) + scatter = plt.scatter([1, 2, 3], [4, 5, 6], c=[7, 2, 3], num=None) plt.legend(*scatter.legend_elements()) creates three legend elements, one for each color with the numerical From 6893bc843d79dac9539130c7977a25daf6d33455 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Sun, 3 Mar 2024 18:25:52 +0100 Subject: [PATCH 84/87] Backport PR #27754: fix quiver3d incorrect arrow colors Update figure test image --- lib/mpl_toolkits/mplot3d/axes3d.py | 13 ++++-------- .../test_axes3d/quiver3d_colorcoded.png | Bin 0 -> 56401 bytes lib/mpl_toolkits/mplot3d/tests/test_axes3d.py | 19 ++++++++++++++++-- 3 files changed, 21 insertions(+), 11 deletions(-) create mode 100644 lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/quiver3d_colorcoded.png diff --git a/lib/mpl_toolkits/mplot3d/axes3d.py b/lib/mpl_toolkits/mplot3d/axes3d.py index a74c11f54e60..6bcd7a6b65a8 100644 --- a/lib/mpl_toolkits/mplot3d/axes3d.py +++ b/lib/mpl_toolkits/mplot3d/axes3d.py @@ -2727,15 +2727,10 @@ def calc_arrows(UVW): UVW = np.column_stack(input_args[3:]).astype(float) # Normalize rows of UVW - norm = np.linalg.norm(UVW, axis=1) - - # If any row of UVW is all zeros, don't make a quiver for it - mask = norm > 0 - XYZ = XYZ[mask] if normalize: - UVW = UVW[mask] / norm[mask].reshape((-1, 1)) - else: - UVW = UVW[mask] + norm = np.linalg.norm(UVW, axis=1) + norm[norm == 0] = 1 + UVW = UVW / norm.reshape((-1, 1)) if len(XYZ) > 0: # compute the shaft lines all at once with an outer product @@ -2749,7 +2744,7 @@ def calc_arrows(UVW): # transpose to get a list of lines heads = heads.swapaxes(0, 1) - lines = [*shafts, *heads] + lines = [*shafts, *heads[::2], *heads[1::2]] else: lines = [] diff --git a/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/quiver3d_colorcoded.png b/lib/mpl_toolkits/mplot3d/tests/baseline_images/test_axes3d/quiver3d_colorcoded.png new file mode 100644 index 0000000000000000000000000000000000000000..a7ef3e53a96388cff4030471640ace7b3e9058cf GIT binary patch literal 56401 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 B2YCPh literal 0 HcmV?d00001 diff --git a/lib/mpl_toolkits/mplot3d/tests/test_axes3d.py b/lib/mpl_toolkits/mplot3d/tests/test_axes3d.py index 9cebc8a33efc..b3fcffcc6e24 100644 --- a/lib/mpl_toolkits/mplot3d/tests/test_axes3d.py +++ b/lib/mpl_toolkits/mplot3d/tests/test_axes3d.py @@ -806,7 +806,8 @@ def test_mixedsamplesraises(): ax.plot_surface(X, Y, Z, cstride=50, rcount=10) -@mpl3d_image_comparison(['quiver3d.png'], style='mpl20') +# remove tolerance when regenerating the test image +@mpl3d_image_comparison(['quiver3d.png'], style='mpl20', tol=0.003) def test_quiver3d(): fig = plt.figure() ax = fig.add_subplot(projection='3d') @@ -853,6 +854,19 @@ def test_quiver3d_masked(): ax.quiver(x, y, z, u, v, w, length=0.1, pivot='tip', normalize=True) +@mpl3d_image_comparison(['quiver3d_colorcoded.png'], style='mpl20') +def test_quiver3d_colorcoded(): + fig = plt.figure() + ax = fig.add_subplot(projection='3d') + + x = y = dx = dz = np.zeros(10) + z = dy = np.arange(10.) + + color = plt.cm.Reds(dy/dy.max()) + ax.quiver(x, y, z, dx, dy, dz, colors=color) + ax.set_ylim(0, 10) + + def test_patch_modification(): fig = plt.figure() ax = fig.add_subplot(projection="3d") @@ -1519,7 +1533,8 @@ def test_minor_ticks(): ax.set_zticklabels(["half"], minor=True) -@mpl3d_image_comparison(['errorbar3d_errorevery.png'], style='mpl20') +# remove tolerance when regenerating the test image +@mpl3d_image_comparison(['errorbar3d_errorevery.png'], style='mpl20', tol=0.003) def test_errorbar3d_errorevery(): """Tests errorevery functionality for 3D errorbars.""" t = np.arange(0, 2*np.pi+.1, 0.01) From bc3f0b77d21ad409f6b54cae54d2961540369c0d Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 4 Mar 2024 11:44:05 -0500 Subject: [PATCH 85/87] Backport PR #27858: pin pytest --- environment.yml | 2 +- requirements/testing/all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/environment.yml b/environment.yml index 15cba9a01c32..eea12387f431 100644 --- a/environment.yml +++ b/environment.yml @@ -55,7 +55,7 @@ dependencies: - psutil - pre-commit - pydocstyle>=5.1.0 - - pytest!=4.6.0,!=5.4.0 + - pytest!=4.6.0,!=5.4.0,!=8.1.0 - pytest-cov - pytest-rerunfailures - pytest-timeout diff --git a/requirements/testing/all.txt b/requirements/testing/all.txt index 4ca786fcf73e..e386924a9b67 100644 --- a/requirements/testing/all.txt +++ b/requirements/testing/all.txt @@ -4,7 +4,7 @@ black<24 certifi coverage!=6.3 psutil -pytest!=4.6.0,!=5.4.0 +pytest!=4.6.0,!=5.4.0,!=8.1.0 pytest-cov pytest-rerunfailures pytest-timeout From 967d2f82a385441068f3eacc64132f9354893cfb Mon Sep 17 00:00:00 2001 From: Ruth Comer <10599679+rcomer@users.noreply.github.com> Date: Fri, 8 Mar 2024 18:40:11 +0000 Subject: [PATCH 86/87] Backport PR #27888: DOC: fix stray release note entry --- doc/api/prev_api_changes/api_changes_3.7.0/behaviour.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/api/prev_api_changes/api_changes_3.7.0/behaviour.rst b/doc/api/prev_api_changes/api_changes_3.7.0/behaviour.rst index 6057bfa9af4c..b3b80f839166 100644 --- a/doc/api/prev_api_changes/api_changes_3.7.0/behaviour.rst +++ b/doc/api/prev_api_changes/api_changes_3.7.0/behaviour.rst @@ -120,14 +120,13 @@ dtype and *levels* is not specified, *levels* now defaults to ``[0.5]`` for ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This can occur if the user explicitly passes a ``levels`` array with no values +between ``z.min()`` and ``z.max()``; or if ``z`` has the same value everywhere. ``AxesImage.set_extent`` now raises ``TypeError`` for unknown keyword arguments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ It previously raised a `ValueError`. -etween ``z.min()`` and ``z.max()``; or if ``z`` has the same value everywhere. - Change of ``legend(loc="best")`` behavior ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 27470763261740ff7f7f10137bf919b3e4e895cc Mon Sep 17 00:00:00 2001 From: David Stansby Date: Mon, 11 Mar 2024 12:57:00 +0000 Subject: [PATCH 87/87] Fix relative links to zenodo badges --- doc/project/citing.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/project/citing.rst b/doc/project/citing.rst index c633cda42b61..9c99d7b0b389 100644 --- a/doc/project/citing.rst +++ b/doc/project/citing.rst @@ -31,10 +31,10 @@ By version v3.8.3 - .. image:: ../../_static/zenodo_cache/10661079.svg + .. image:: ../_static/zenodo_cache/10661079.svg :target: https://doi.org/10.5281/zenodo.10661079 v3.8.2 - .. image:: ../../_static/zenodo_cache/10150955.svg + .. image:: ../_static/zenodo_cache/10150955.svg :target: https://doi.org/10.5281/zenodo.10150955 v3.8.1 .. image:: ../_static/zenodo_cache/10059757.svg pFad - Phonifier reborn

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

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


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy