diff --git a/.appveyor.yml b/.appveyor.yml index b48726bb3e51..87f6cbde6384 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -2,6 +2,7 @@ # http://tjelvarolsson.com/blog/how-to-continuously-test-your-python-code-on-windows-using-appveyor/ # https://packaging.python.org/en/latest/appveyor/ # https://github.com/rmcgibbo/python-appveyor-conda-example +--- # Backslashes in quotes need to be escaped: \ -> "\\" branches: @@ -24,21 +25,18 @@ environment: global: PYTHONFAULTHANDLER: 1 PYTHONIOENCODING: UTF-8 - PYTEST_ARGS: -raR --numprocesses=auto --timeout=300 --durations=25 + PYTEST_ARGS: -rfEsXR --numprocesses=auto --timeout=300 --durations=25 --cov-report= --cov=lib --log-level=DEBUG matrix: - - PYTHON_VERSION: "3.9" + - PYTHON_VERSION: "3.11" CONDA_INSTALL_LOCN: "C:\\Miniconda3-x64" - TEST_ALL: "no" - - PYTHON_VERSION: "3.10" - CONDA_INSTALL_LOCN: "C:\\Miniconda3-x64" - TEST_ALL: "no" + TEST_ALL: "yes" # We always use a 64-bit machine, but can build x86 distributions # with the PYTHON_ARCH variable platform: - - x64 + - x64 # all our python builds have to happen in tests_script... build: false @@ -70,19 +68,23 @@ install: test_script: # Now build the thing.. - set LINK=/LIBPATH:%cd%\lib - - pip install -ve . + - pip install -v --no-build-isolation --config-settings=setup-args="--vsenv" --editable .[dev] # this should show no freetype dll... - set "DUMPBIN=%VS140COMNTOOLS%\..\..\VC\bin\dumpbin.exe" - '"%DUMPBIN%" /DEPENDENTS lib\matplotlib\ft2font*.pyd | findstr freetype.*.dll && exit /b 1 || exit /b 0' # this are optional dependencies so that we don't skip so many tests... - - if x%TEST_ALL% == xyes conda install -q ffmpeg inkscape miktex + - if x%TEST_ALL% == xyes conda install -q ffmpeg inkscape + # miktex is available on conda, but seems to fail with permission errors. # missing packages on conda-forge for imagemagick # This install sometimes failed randomly :-( - #- choco install imagemagick + # - choco install imagemagick # Test import of tkagg backend - - python -c "import matplotlib as m; m.use('tkagg'); import matplotlib.pyplot as plt; print(plt.get_backend())" + - python -c + "import matplotlib as m; m.use('tkagg'); + import matplotlib.pyplot as plt; + print(plt.get_backend())" # tests - echo The following args are passed to pytest %PYTEST_ARGS% - pytest %PYTEST_ARGS% @@ -90,11 +92,11 @@ test_script: artifacts: - path: result_images\* name: result_images - type: zip + type: Zip on_finish: - conda install codecov - - codecov -e PYTHON_VERSION PLATFORM + - codecov -e PYTHON_VERSION PLATFORM -n "$PYTHON_VERSION Windows" on_failure: # Generate a html for visual tests diff --git a/.circleci/config.yml b/.circleci/config.yml index e120f95f5ee8..eef9ca87f30d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -17,7 +17,8 @@ commands: export git_log=$(git log --max-count=1 --pretty=format:"%B" | tr "\n" " ") echo "Got commit message:" echo "${git_log}" - if [[ -v CIRCLE_PULL_REQUEST ]] && ([[ "$git_log" == *"[skip circle]"* ]] || [[ "$git_log" == *"[circle skip]"* ]]); then + if [[ -v CIRCLE_PULL_REQUEST ]] && + [[ $git_log =~ (\[skip circle\]|\[circle skip\]|\[skip doc\]|\[doc skip\]) ]]; then echo "Skip detected, exiting job ${CIRCLE_JOB} for PR ${CIRCLE_PULL_REQUEST}." circleci-agent step halt; fi @@ -42,25 +43,26 @@ commands: - run: name: Install apt packages command: | - sudo apt -qq update - sudo apt install -y \ - inkscape \ - ffmpeg \ + sudo apt-get -qq update + sudo apt-get install -yy --no-install-recommends \ + cm-super \ dvipng \ + ffmpeg \ + fonts-crosextra-carlito \ + fonts-freefont-otf \ + fonts-noto-cjk \ + fonts-wqy-zenhei \ + graphviz \ + inkscape \ lmodern \ - cm-super \ + ninja-build \ + optipng \ + texlive-fonts-recommended \ texlive-latex-base \ texlive-latex-extra \ - texlive-fonts-recommended \ texlive-latex-recommended \ texlive-pictures \ - texlive-xetex \ - ttf-wqy-zenhei \ - graphviz \ - fonts-crosextra-carlito \ - fonts-freefont-otf \ - fonts-noto-cjk \ - optipng + texlive-xetex fonts-install: steps: @@ -70,8 +72,12 @@ commands: 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 + 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-4 @@ -97,6 +103,7 @@ commands: - run: name: Install Python dependencies command: | + python -m pip install --user meson-python numpy pybind11 setuptools-scm python -m pip install --user \ numpy<< parameters.numpy_version >> \ -r requirements/doc/doc-requirements.txt @@ -112,15 +119,13 @@ commands: version=${version#v} python -m pip install matplotlib==${version} else - python -m pip install --user -ve . + python -m pip install --user --verbose \ + --no-build-isolation --editable .[dev] fi - save_cache: - key: build-deps-1 + key: build-deps-2 paths: - # FreeType 2.6.1 tarball. - - ~/.cache/matplotlib/0a3c7dfbda6da1e8fce29232e8e96d987ababbbf71ebc8c75659e4132c367014 - # Qhull 2020.2 tarball. - - ~/.cache/matplotlib/b5c2d7eb833278881b952c8a52d20179eab87766b00b865000469a45c1838b7e + - subprojects/packagecache doc-build: steps: @@ -140,7 +145,7 @@ commands: export RELEASE_TAG='-t release' fi mkdir -p logs - make html O="-T $RELEASE_TAG -j4 -w /tmp/sphinxerrorswarnings.log" + make html O="-T $RELEASE_TAG -j1 -w /tmp/sphinxerrorswarnings.log" rm -r build/html/_sources working_directory: doc - save_cache: @@ -222,8 +227,8 @@ jobs: - fonts-install - pip-install - - mpl-install - doc-deps-install + - mpl-install - doc-build - doc-show-errors-warnings diff --git a/.flake8 b/.flake8 index ee739cdf4231..36e8bcf5476f 100644 --- a/.flake8 +++ b/.flake8 @@ -18,7 +18,9 @@ ignore = D301, D400, D401, D403, D404 # ignored by pydocstyle numpy docstring convention - D107, D203, D212, D213, D402, D413, D415, D416, D417, + D107, D203, D212, D402, D413, D415, D416, D417, + # while D213 is ignored by the numpy docstring convention, it's left out above, + # because we want to enforce it. exclude = .git @@ -31,30 +33,21 @@ exclude = .eggs per-file-ignores = - setup.py: E402 - - lib/matplotlib/__init__.py: E402, F401 - lib/matplotlib/_animation_data.py: E501 - lib/matplotlib/_api/__init__.py: F401 - lib/matplotlib/_cm.py: E122, E202, E203, E302 + lib/matplotlib/_cm.py: E202, E203, E302 lib/matplotlib/_mathtext.py: E221, E251 - lib/matplotlib/_mathtext_data.py: E122, E203, E261 - lib/matplotlib/axes/__init__.py: F401, F403 + lib/matplotlib/_mathtext_data.py: E203, E261 lib/matplotlib/backends/backend_template.py: F401 - lib/matplotlib/font_manager.py: E501 - lib/matplotlib/image.py: F401, F403 lib/matplotlib/mathtext.py: E221 lib/matplotlib/pylab.py: F401, F403 - lib/matplotlib/pyplot.py: F401, F811 + lib/matplotlib/pyplot.py: F811 lib/matplotlib/tests/test_mathtext.py: E501 lib/matplotlib/transforms.py: E201, E202, E203 lib/matplotlib/tri/_triinterpolate.py: E201, E221 lib/mpl_toolkits/axes_grid1/axes_size.py: E272 - lib/mpl_toolkits/axisartist/__init__.py: F401 lib/mpl_toolkits/axisartist/angle_helper.py: E221 - lib/pylab.py: F401, F403 doc/conf.py: E402 + galleries/users_explain/quick_start.py: E402 galleries/users_explain/artists/paths.py: E402 galleries/users_explain/artists/patheffects_guide.py: E402 galleries/users_explain/artists/transforms_tutorial.py: E402, E501 diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index c4198cdcdf14..cb27bbf19d46 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1 +1 @@ -Please refer to the [developers guide](https://matplotlib.org/devel/index.html). +Please refer to the [contributing guide](https://matplotlib.org/devel/index.html). diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 5c9afed3c02b..a474d51d6f64 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,4 @@ +--- # These are supported funding model platforms github: [matplotlib, numfocus] custom: https://numfocus.org/donate-to-matplotlib diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 985762649b67..045386dc7402 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,3 +1,4 @@ +--- name: Bug Report description: Report a bug or issue with Matplotlib. title: "[Bug]: " @@ -6,26 +7,24 @@ body: id: summary attributes: label: Bug summary - description: Describe the bug in 1-2 short sentences - placeholder: - value: + description: Describe the bug in 1-2 short sentences validations: required: true - type: textarea id: reproduction attributes: label: Code for reproduction - description: | + description: >- If possible, please provide a minimum self-contained example. placeholder: Paste your code here. This field is automatically formatted as Python code. - render: python + render: Python validations: required: true - type: textarea id: actual attributes: label: Actual outcome - description: | + description: >- Paste the output produced by the code provided above, e.g. console output, images/videos produced by the code, any relevant screenshots/screencasts, etc. validations: diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 10c9d5c0d580..dc80f6d7c91d 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,7 @@ -# Ref: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser -blank_issues_enabled: true # default +# Reference: +# https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser +--- +blank_issues_enabled: true # default contact_links: - name: Question/Support/Other url: https://discourse.matplotlib.org diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml index ea0eb385baaf..5f7a0d6c7176 100644 --- a/.github/ISSUE_TEMPLATE/documentation.yml +++ b/.github/ISSUE_TEMPLATE/documentation.yml @@ -1,3 +1,4 @@ +--- name: Documentation description: Create a report to help us improve the documentation title: "[Doc]: " @@ -7,9 +8,9 @@ body: id: link attributes: label: Documentation Link - description: | - Link to any documentation or examples that you are referencing. - Suggested improvements should be based on the development version of the docs: https://matplotlib.org/devdocs/ + description: >- + Link to any documentation or examples that you are referencing. Suggested improvements should be based + on [the development version of the docs](https://matplotlib.org/devdocs/) placeholder: https://matplotlib.org/devdocs/... - type: textarea id: problem diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 5274c287569c..e174fb8994aa 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,3 +1,4 @@ +--- name: Feature Request description: Suggest something to add to Matplotlib! title: "[ENH]: " @@ -5,8 +6,9 @@ labels: [New feature] body: - type: markdown attributes: - value: | - Please search the [issues](https://github.com/matplotlib/matplotlib/issues) for relevant feature requests before creating a new feature request. + value: >- + Please search the [issues](https://github.com/matplotlib/matplotlib/issues) for relevant feature + requests before creating a new feature request. - type: textarea id: problem attributes: diff --git a/.github/ISSUE_TEMPLATE/maintenance.yml b/.github/ISSUE_TEMPLATE/maintenance.yml index 746ab55ef0e3..6ebb64c0c3e9 100644 --- a/.github/ISSUE_TEMPLATE/maintenance.yml +++ b/.github/ISSUE_TEMPLATE/maintenance.yml @@ -1,3 +1,4 @@ +--- name: Maintenance description: Help improve performance, usability and/or consistency. title: "[MNT]: " @@ -7,7 +8,7 @@ body: id: summary attributes: label: Summary - description: Please provide 1-2 short sentences that succinctly describes what could be improved. + description: Please provide 1-2 short sentences that succinctly describes what could be improved. validations: required: true - type: textarea diff --git a/.github/ISSUE_TEMPLATE/tag_proposal.yml b/.github/ISSUE_TEMPLATE/tag_proposal.yml new file mode 100644 index 000000000000..aa3345336089 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/tag_proposal.yml @@ -0,0 +1,28 @@ +--- +name: Tag Proposal +description: Suggest a new tag or subcategory for the gallery of examples +title: "[Tag]: " +labels: [Tag proposal] +body: + - type: markdown + attributes: + value: >- + Please search the [tag glossary]() for relevant tags before creating a new tag proposal. + - type: textarea + id: need + attributes: + label: Need + description: Briefly describe the need this tag will fill. (1-4 sentences) + placeholder: | + * A tag is needed for examples that share [...] + * Existing tags do not work because [...] + * Current gallery examples that would use this tag include [...] + * Indicate which subcategory this tag falls under, or whether a new subcategory is proposed. + validations: + required: true + - type: textarea + id: solution + attributes: + label: Proposed solution + description: >- + What should the tag be? All tags are in the format `subcategory: tag` diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index fa84d5cac9f5..7f17a80dc4b6 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,40 +1,30 @@ + + ## PR summary + + ## PR checklist - [ ] "closes #0000" is in the body of the PR description to [link the related issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) - [ ] new and changed code is [tested](https://matplotlib.org/devdocs/devel/testing.html) -- [ ] *Plotting related* features are demonstrated in an [example](https://matplotlib.org/devdocs/devel/documenting_mpl.html#writing-examples-and-tutorials) -- [ ] *New Features* and *API Changes* are noted with a [directive and release note](https://matplotlib.org/devdocs/devel/coding_guide.html#new-features-and-api-changes) -- [ ] Documentation complies with [general](https://matplotlib.org/devdocs/devel/documenting_mpl.html#writing-rest-pages) and [docstring](https://matplotlib.org/devdocs/devel/documenting_mpl.html#writing-docstrings) guidelines - - +back on your PR.--> diff --git a/.github/codecov.yml b/.github/codecov.yml index 14a17e58e1b9..00e7612bd1e6 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -1,10 +1,11 @@ # codecov used to be able to find this anywhere, now we have to manually # tell it where to look +--- comment: false codecov: notify: - require_ci_to_pass: no + require_ci_to_pass: false coverage: status: @@ -13,20 +14,20 @@ coverage: target: 50% if_no_uploads: error if_not_found: success - if_ci_failed: failure + if_ci_failed: error project: default: false library: target: 50% if_no_uploads: error if_not_found: success - if_ci_failed: failure + if_ci_failed: error paths: - '!lib/.*/tests/.*' tests: target: auto if_no_uploads: error if_not_found: success - if_ci_failed: failure + if_ci_failed: error paths: - 'lib/.*/tests/.*' diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5ace4600a1f2..34902e5236df 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,6 +1,11 @@ +--- version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" + groups: + actions: + patterns: + - "*" diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 000000000000..43a1246ba68a --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,281 @@ +--- +"CI: Run cibuildwheel": + - changed-files: + - any-glob-to-any-file: ['.github/workflows/cibuildwheel.yml'] +"CI: Run cygwin": + - changed-files: + - any-glob-to-any-file: ['.github/workflows/cygwin.yml'] + +"backend: agg": + - changed-files: + - any-glob-to-any-file: + - 'extern/agg24-svn/' + - 'lib/matplotlib/backends/_backend_agg.pyi' + - 'lib/matplotlib/backends/backend_agg.py*' + - 'src/_backend_agg*' +"backend: cairo": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backends/backend_*cairo.py*' +"backend: pdf": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backends/_backend_pdf_ps.py' + - 'lib/matplotlib/backends/backend_pdf.py' +"backend: pgf": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backends/backend_pgf.py' +"backend: ps": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backends/_backend_pdf_ps.py' + - 'lib/matplotlib/backends/backend_ps.py' +"backend: svg": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backends/backend_svg.py' + +"GUI: gtk": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backends/_backend_gtk.py*' + - 'lib/matplotlib/backends/backend_gtk*' +"GUI: MacOSX": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backends/*_macosx.py*' + - 'src/_macosx.m' +"GUI: nbagg": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backends/*_nbagg*.py*' + - 'lib/matplotlib/backends/web_backend/js/nbagg_mpl.js' +"GUI: Qt": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backends/backend_qt*' + - 'lib/matplotlib/backends/qt_compat.py' + - 'lib/matplotlib/backends/qt_editor/**' +"GUI: tk": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backends/*backend_tk*' + - 'lib/matplotlib/backends/_tkagg.pyi' + - 'src/_tkagg.cpp' + - 'src/_tkmini.h' +"GUI: webagg": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backends/*_webagg*.py*' + - 'lib/matplotlib/backends/web_backend/**' +"GUI: wx": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backends/backend_wx*.py*' + +"Documentation: API": + - all: + - changed-files: + - any-glob-to-any-file: + # Also files in lib/**, but we can't be sure those are only documentation. + - 'doc/api/**' + - all-globs-to-all-files: + - '!doc/api/next_api_changes/**' + +"Documentation: build": + - changed-files: + - any-glob-to-any-file: + - 'doc/conf.py' + - 'doc/Makefile' + - 'doc/make.bat' +"Documentation: devdocs": + - changed-files: + - any-glob-to-any-file: + - 'doc/devel/**' +"Documentation: examples": + - changed-files: + - any-glob-to-any-file: + - 'galleries/examples/**' +"Documentation: plot types": + - changed-files: + - any-glob-to-any-file: + - 'galleries/plot_types/**' +"Documentation: tutorials": + - changed-files: + - any-glob-to-any-file: + - 'galleries/tutorials/**' +"Documentation: user guide": + - all: + - changed-files: + - any-glob-to-any-file: + - 'doc/users/**' + - 'galleries/users_explain/**' + - all-globs-to-all-files: + - '!doc/users/next_whats_new/**' + +"topic: animation": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/animation.py*' + - 'lib/matplotlib/_animation_data.py*' +"topic: axes": + - changed-files: + - any-glob-to-any-file: + # Note, axes.py is not included here because it contains many plotting + # methods, for which changes would not be considered on topic. + - 'lib/matplotlib/axes/_base.py*' +"topic: canvas and figure manager": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backend_bases.py*' +"topic: categorical": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/category.py*' +"topic: collections and mappables": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/collections.py*' +"topic: color/color & colormaps": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/colorbar.py*' + - 'lib/matplotlib/colors.py*' + - 'lib/matplotlib/_color_data.py*' + - 'lib/matplotlib/cm.py*' + - 'lib/matplotlib/_cm.py*' + - 'lib/matplotlib/_cm_listed.py*' +"topic: contour": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/contour.py*' + - 'src/_qhull_wrapper.cpp' +"topic: date handling": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/dates.py*' +"topic: figures and subfigures": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/figure.py*' +"topic: geometry manager": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/_constrained_layout.py*' + - 'lib/matplotlib/_layoutgrid.py*' + - 'lib/matplotlib/_tight_bbox.py*' + - 'lib/matplotlib/_tight_layout.py*' + - 'lib/matplotlib/gridspec.py*' + - 'lib/matplotlib/layout_engine.py*' +"topic: hatch": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/hatch.py*' +"topic: images": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/image.py*' + - 'lib/matplotlib/_image.pyi' + - 'src/_image_*' +"topic: legend": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/legend.py*' + - 'lib/matplotlib/legend_handler.py*' +"topic: markers": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/markers.py*' +"topic: mpl_toolkit": + - all: + - changed-files: + - any-glob-to-any-file: + - 'lib/mpl_toolkits/**' + - all-globs-to-all-files: + - '!lib/mpl_toolkits/mplot3d/**' +"topic: mplot3d": + - changed-files: + - any-glob-to-any-file: + - 'lib/mpl_toolkits/mplot3d/**' +"topic: path handling": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/path.py*' + - 'lib/matplotlib/patheffects.py*' + - 'lib/matplotlib/_path.pyi' + - 'src/*path*' +"topic: polar": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/projections/polar.py*' +"topic: pyplot API": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/pyplot.py' + - 'lib/matplotlib/_pylab_helpers.py*' +"topic: rcparams": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/rcsetup.py*' +"topic: sankey": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/sankey.py*' +"topic: sphinx extension": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/sphinxext/**' +"topic: styles": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/mpl-data/stylelib/**' + - 'lib/matplotlib/style/**' +"topic: table": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/table.py*' +"topic: text": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/text.py*' + - 'lib/matplotlib/textpath.py*' +"topic: text/fonts": + - changed-files: + - any-glob-to-any-file: + - 'src/checkdep_freetype2.c' + - 'src/ft2font*' +"topic: text/mathtext": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/mathtext.py*' + - 'lib/matplotlib/_mathtext.py*' + - 'lib/matplotlib/_mathtext_data.py*' +"topic: ticks axis labels": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/axis.py*' + - 'lib/matplotlib/ticker.py*' +"topic: toolbar": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backend_managers.py*' + - 'lib/matplotlib/backend_tools.py*' +"topic: transforms and scales": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/scale.py*' + - 'lib/matplotlib/transforms.py*' +"topic: tri": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/tri/**' + - 'src/tri/**' +"topic: units and array ducktypes": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/units.py*' +"topic: widgets/UI": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/widgets.py*' diff --git a/.github/workflows/cibuildwheel.yml b/.github/workflows/cibuildwheel.yml index d1dbbb2c47bf..9de63b14c4fd 100644 --- a/.github/workflows/cibuildwheel.yml +++ b/.github/workflows/cibuildwheel.yml @@ -23,7 +23,7 @@ permissions: jobs: build_sdist: - if: | + if: >- github.event_name == 'push' || github.event_name == 'pull_request' && ( ( @@ -64,22 +64,19 @@ jobs: run: | python -m build --sdist python ci/export_sdist_name.py - env: - # Prevent including development runtime dependencies in metadata. - CIBUILDWHEEL: 1 - name: Check README rendering for PyPI run: twine check dist/* - name: Upload sdist result - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: sdist + name: cibw-sdist path: dist/*.tar.gz if-no-files-found: error build_wheels: - if: | + if: >- github.event_name == 'push' || github.event_name == 'pull_request' && ( ( @@ -94,23 +91,28 @@ jobs: runs-on: ${{ matrix.os }} env: CIBW_BEFORE_BUILD: >- - pip install certifi && rm -rf {package}/build CIBW_BEFORE_BUILD_WINDOWS: >- - pip install certifi delvewheel && + pip install delvewheel && rm -rf {package}/build CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: >- delvewheel repair -w {dest_dir} {wheel} CIBW_AFTER_BUILD: >- twine check {wheel} && python {package}/ci/check_wheel_licenses.py {wheel} + # On Windows, we explicitly request MSVC compilers (as GitHub Action runners have + # MinGW on PATH that would be picked otherwise), switch to a static build for + # runtimes, but use dynamic linking for `VCRUNTIME140.dll`, `VCRUNTIME140_1.dll`, + # and the UCRT. This avoids requiring specific versions of `MSVCP140.dll`, while + # keeping shared state with the rest of the Python process/extensions. + CIBW_CONFIG_SETTINGS_WINDOWS: >- + setup-args="--vsenv" + setup-args="-Db_vscrt=mt" + setup-args="-Dcpp_link_args=['ucrt.lib','vcruntime.lib','/nodefaultlib:libucrt.lib','/nodefaultlib:libvcruntime.lib']" CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 CIBW_SKIP: "*-musllinux_aarch64" CIBW_TEST_COMMAND: >- python {package}/ci/check_version_number.py - # Apple Silicon machines are not available for testing, so silence the - # warning from cibuildwheel. Remove the skip when they're available. - CIBW_TEST_SKIP: "*-macosx_arm64" MACOSX_DEPLOYMENT_TARGET: "10.12" MPL_DISABLE_FH4: "yes" strategy: @@ -122,24 +124,46 @@ jobs: cibw_archs: "aarch64" - os: windows-latest cibw_archs: "auto64" - - os: macos-11 - cibw_archs: "x86_64 arm64" + - os: macos-12 + cibw_archs: "x86_64" + - os: macos-14 + cibw_archs: "arm64" steps: - name: Set up QEMU if: matrix.cibw_archs == 'aarch64' - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 with: platforms: arm64 - name: Download sdist - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: - name: sdist + name: cibw-sdist path: dist/ + - name: Build wheels for CPython 3.13 + uses: pypa/cibuildwheel@bd033a44476646b606efccdd5eed92d5ea1d77ad # v2.20.0 + with: + package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} + env: + CIBW_BUILD: "cp313-* cp313t-*" + # No free-threading wheels for NumPy; musllinux skipped for main builds also. + CIBW_SKIP: "cp313t-win_amd64 *-musllinux_aarch64" + CIBW_BUILD_FRONTEND: + "pip; args: --pre --extra-index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple" + CIBW_FREE_THREADED_SUPPORT: true + # No free-threading wheels available for aarch64 on Pillow. + CIBW_TEST_SKIP: "cp313t-manylinux_aarch64" + # We need pre-releases to get the nightly wheels. + CIBW_BEFORE_TEST: >- + pip install --pre + --extra-index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple + contourpy numpy pillow + CIBW_ARCHS: ${{ matrix.cibw_archs }} + - name: Build wheels for CPython 3.12 - uses: pypa/cibuildwheel@ce3fb7832089eb3e723a0a99cab7f3eaccf074fd # v2.16.5 + uses: pypa/cibuildwheel@bd033a44476646b606efccdd5eed92d5ea1d77ad # v2.20.0 with: package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} env: @@ -147,7 +171,7 @@ jobs: CIBW_ARCHS: ${{ matrix.cibw_archs }} - name: Build wheels for CPython 3.11 - uses: pypa/cibuildwheel@ce3fb7832089eb3e723a0a99cab7f3eaccf074fd # v2.16.5 + uses: pypa/cibuildwheel@bd033a44476646b606efccdd5eed92d5ea1d77ad # v2.20.0 with: package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} env: @@ -155,7 +179,7 @@ jobs: CIBW_ARCHS: ${{ matrix.cibw_archs }} - name: Build wheels for CPython 3.10 - uses: pypa/cibuildwheel@ce3fb7832089eb3e723a0a99cab7f3eaccf074fd # v2.16.5 + uses: pypa/cibuildwheel@bd033a44476646b606efccdd5eed92d5ea1d77ad # v2.20.0 with: package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} env: @@ -163,7 +187,7 @@ jobs: CIBW_ARCHS: ${{ matrix.cibw_archs }} - name: Build wheels for CPython 3.9 - uses: pypa/cibuildwheel@ce3fb7832089eb3e723a0a99cab7f3eaccf074fd # v2.16.5 + uses: pypa/cibuildwheel@7e5a838a63ac8128d71ab2dfd99e4634dd1bca09 # v2.19.2 with: package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} env: @@ -171,7 +195,7 @@ jobs: CIBW_ARCHS: ${{ matrix.cibw_archs }} - name: Build wheels for PyPy - uses: pypa/cibuildwheel@ce3fb7832089eb3e723a0a99cab7f3eaccf074fd # v2.16.5 + uses: pypa/cibuildwheel@bd033a44476646b606efccdd5eed92d5ea1d77ad # v2.20.0 with: package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} env: @@ -179,8 +203,37 @@ jobs: CIBW_ARCHS: ${{ matrix.cibw_archs }} if: matrix.cibw_archs != 'aarch64' - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: - name: wheels + name: cibw-wheels-${{ runner.os }}-${{ matrix.cibw_archs }} path: ./wheelhouse/*.whl if-no-files-found: error + + publish: + if: github.event_name == 'push' && github.ref_type == 'tag' + name: Upload release to PyPI + needs: [build_sdist, build_wheels] + runs-on: ubuntu-latest + environment: release + permissions: + id-token: write + attestations: write + contents: read + steps: + - name: Download packages + uses: actions/download-artifact@v4 + with: + pattern: cibw-* + path: dist + merge-multiple: true + + - name: Print out packages + run: ls dist + + - name: Generate artifact attestation for sdist and wheel + uses: actions/attest-build-provenance@210c1913531870065f03ce1f9440dd87bc0938cd # v1.4.0 + with: + subject-path: dist/matplotlib-* + + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@ec4db0b4ddc65acdf4bff5fa45ac92d78b56bdf0 # v1.9.0 diff --git a/.github/workflows/circleci.yml b/.github/workflows/circleci.yml index 8f9e3190c5e2..3aead720cf20 100644 --- a/.github/workflows/circleci.yml +++ b/.github/workflows/circleci.yml @@ -10,7 +10,8 @@ jobs: name: Run CircleCI artifacts redirector steps: - name: GitHub Action step - uses: larsoner/circleci-artifacts-redirector-action@master + uses: + scientific-python/circleci-artifacts-redirector-action@4e13a10d89177f4bfc8007a7064bdbeda848d8d1 # v1.0.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} api-token: ${{ secrets.CIRCLECI_TOKEN }} diff --git a/.github/workflows/clean_pr.yml b/.github/workflows/clean_pr.yml index f3ccb3195d77..77e49f7c1d9e 100644 --- a/.github/workflows/clean_pr.yml +++ b/.github/workflows/clean_pr.yml @@ -48,5 +48,6 @@ jobs: - name: Check for branches opened against main if: github.ref_name == 'main' run: | - printf 'PR branch should not be main. See https://matplotlib.org/devdocs/devel/development_workflow.html#make-a-new-feature-branch' + echo 'PR branch should not be main.' + echo 'See https://matplotlib.org/devdocs/devel/development_workflow.html#make-a-new-feature-branch' exit 1 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index f0041ece02ee..203b0eee9ca4 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -22,41 +22,22 @@ jobs: strategy: fail-fast: false matrix: - language: ['cpp', 'javascript', 'python'] + language: ['c-cpp', 'javascript', 'python'] steps: - name: Checkout repository uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v5 - if: matrix.language != 'javascript' - with: - python-version: '3.x' - - name: Install dependencies - if: matrix.language != 'javascript' - run: | - python -m pip install --upgrade pip setuptools wheel - # TODO: Use pip-tools instead when it supports build-system - # dependencies so we don't need another copy here. - # https://github.com/jazzband/pip-tools/pull/1681 - python -m pip install --upgrade \ - certifi contourpy cycler fonttools kiwisolver importlib_resources \ - numpy packaging pillow pyparsing python-dateutil setuptools-scm \ - pybind11 - echo "CODEQL_PYTHON=$(which python)" >> $GITHUB_ENV - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} - setup-python-dependencies: false - name: Build compiled code - if: matrix.language == 'cpp' + if: matrix.language == 'c-cpp' run: | - mkdir ~/.cache/matplotlib - $CODEQL_PYTHON setup.py build + pip install --user --upgrade pip + pip install --user -v . - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/conflictcheck.yml b/.github/workflows/conflictcheck.yml index 48be8ba510c5..3110839e5150 100644 --- a/.github/workflows/conflictcheck.yml +++ b/.github/workflows/conflictcheck.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check if PRs have merge conflicts - uses: eps1lon/actions-label-merge-conflict@releases/2.x + uses: eps1lon/actions-label-merge-conflict@1b1b1fcde06a9b3d089f3464c96417961dde1168 # v3.0.2 with: dirtyLabel: "status: needs rebase" repoToken: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml index cb69b4d44c17..58c132315b6f 100644 --- a/.github/workflows/cygwin.yml +++ b/.github/workflows/cygwin.yml @@ -49,7 +49,7 @@ jobs: test-cygwin: runs-on: windows-latest name: Python 3.${{ matrix.python-minor-version }} on Cygwin - if: | + if: >- github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' || ( @@ -118,8 +118,7 @@ jobs: # GitHub Actions runs everything as Administrator. I don't # know how to test for this, so set the uid for the CI job so # that the existing unix root detection will work. - run: | - /bin/mkpasswd.exe -c | sed -e "s/$(id -u)/0/" >/etc/passwd + run: /bin/mkpasswd.exe -c | sed -e "s/$(id -u)/0/" >/etc/passwd - name: Mark test repo safe shell: bash.exe -eo pipefail -o igncr "{0}" @@ -139,22 +138,21 @@ jobs: # FreeType build fails with bash, succeeds with dash - name: Cache pip - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: C:\cygwin\home\runneradmin\.cache\pip key: Cygwin-py3.${{ matrix.python-minor-version }}-pip-${{ hashFiles('requirements/*/*.txt') }} - restore-keys: | - ${{ matrix.os }}-py3.${{ matrix.python-minor-version }}-pip- + restore-keys: ${{ matrix.os }}-py3.${{ matrix.python-minor-version }}-pip- - name: Cache ccache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: C:\cygwin\home\runneradmin\.ccache key: Cygwin-py3.${{ matrix.python-minor-version }}-ccache-${{ hashFiles('src/*') }} restore-keys: Cygwin-py3.${{ matrix.python-minor-version }}-ccache- - name: Cache Matplotlib - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | C:\cygwin\home\runneradmin\.cache\matplotlib @@ -174,8 +172,8 @@ jobs: - name: Install Python dependencies shell: bash.exe -eo pipefail -o igncr "{0}" run: | - python -m pip install --upgrade pip 'setuptools<60' wheel - python -m pip install kiwisolver 'numpy!=1.21.*' pillow importlib_resources + python -m pip install --upgrade pip setuptools wheel + python -m pip install kiwisolver 'numpy>=1.22,<1.26' pillow importlib_resources grep -v -F -e psutil requirements/testing/all.txt >requirements_test.txt python -m pip install meson-python pybind11 export PATH="/usr/local/bin:$PATH" @@ -203,25 +201,20 @@ jobs: AUTOCONF: /usr/bin/autoconf-2.69 MAKEFLAGS: dw run: | + export PATH="/usr/local/bin:$PATH" ccache -s git describe - cat <> mplsetup.cfg - [rc_options] - backend=Agg - - [libs] - system_freetype = False - system_qhull = True - EOT - cat mplsetup.cfg # All dependencies must have been pre-installed, so that the minver # constraints are held. - python -m pip install --no-deps -ve . + python -m pip install --no-deps --no-build-isolation --verbose \ + --config-settings=setup-args="-DrcParams-backend=Agg" \ + --editable .[dev] - name: Find DLLs to rebase shell: bash.exe -eo pipefail -o igncr "{0}" run: | - find {/usr,/usr/local}/{bin,lib/python3.*/site-packages} /usr/lib/lapack . -name \*.exe -o -name \*.dll -print >files_to_rebase.txt + find {/usr,/usr/local}/{bin,lib/python3.*/site-packages} /usr/lib/lapack . \ + -name \*.exe -o -name \*.dll -print >files_to_rebase.txt - name: Rebase DLL list shell: ash.exe "{0}" @@ -242,15 +235,14 @@ jobs: oldmplrc=$(python -c "from matplotlib import matplotlib_fname as mplrc_file; print(mplrc_file())") echo "${oldmplrc}" mkdir -p ~/.matplotlib/ - sed -E -e 's~#animation\.ffmpeg_path:.+~animation.ffmpeg_path: /usr/bin/ffmpeg.exe~' "${oldmplrc}" >~/.matplotlib/matplotlibrc + sed -E \ + -e 's~#animation\.ffmpeg_path:.+~animation.ffmpeg_path: /usr/bin/ffmpeg.exe~' \ + "${oldmplrc}" >~/.matplotlib/matplotlibrc - name: Run pytest shell: bash.exe -eo pipefail -o igncr "{0}" id: cygwin-run-pytest run: | - xvfb-run python -mpytest -raR -n auto \ + xvfb-run pytest-3.${{ matrix.python-minor-version }} -rfEsXR -n auto \ --maxfail=50 --timeout=300 --durations=25 \ - --cov-report=xml --cov=lib --log-level=DEBUG --color=yes - - - name: Upload code coverage - uses: codecov/codecov-action@v4 + --cov-report=term --cov=lib --log-level=DEBUG --color=yes diff --git a/.github/workflows/do_not_merge.yml b/.github/workflows/do_not_merge.yml new file mode 100644 index 000000000000..dde5bfb5ec81 --- /dev/null +++ b/.github/workflows/do_not_merge.yml @@ -0,0 +1,30 @@ +--- +name: Do Not Merge + +# action to block merging on specific labels +on: + pull_request: + types: [synchronize, opened, reopened, labeled, unlabeled] + +permissions: {} + +jobs: + do-not-merge: + name: Prevent Merging + runs-on: ubuntu-latest + env: + has_tag: >- + ${{contains(github.event.pull_request.labels.*.name, 'status: needs comment/discussion') || + contains(github.event.pull_request.labels.*.name, 'status: waiting for other PR')}} + steps: + - name: Check for label + if: ${{'true' == env.has_tag}} + run: | + echo "This PR cannot be merged because it has one of the following labels: " + echo "* status: needs comment/discussion" + echo "* status: waiting for other PR" + echo "${{env.has_tag}}" + exit 1 + - name: Allow merging + if: ${{'false' == env.has_tag}} + run: exit 0 diff --git a/.github/workflows/good-first-issue.yml b/.github/workflows/good-first-issue.yml index baac893a6a7a..8905511fc01d 100644 --- a/.github/workflows/good-first-issue.yml +++ b/.github/workflows/good-first-issue.yml @@ -1,3 +1,4 @@ +--- name: Add comment on good first issues on: issues: @@ -17,8 +18,13 @@ jobs: body: | ### Good first issue - notes for new contributors - This issue is suited to new contributors because it does not require understanding of the Matplotlib internals. To get started, please see our [contributing guide](https://matplotlib.org/stable/devel/index). + This issue is suited to new contributors because it does not require understanding of the + Matplotlib internals. To get started, please see our [contributing + guide](https://matplotlib.org/stable/devel/index). - **We do not assign issues**. Check the *Development* section in the sidebar for linked pull requests (PRs). If there are none, feel free to start working on it. If there is an open PR, please collaborate on the work by reviewing it rather than duplicating it in a competing PR. + **We do not assign issues**. Check the *Development* section in the sidebar for linked pull + requests (PRs). If there are none, feel free to start working on it. If there is an open PR, please + collaborate on the work by reviewing it rather than duplicating it in a competing PR. - If something is unclear, please reach out on any of our [communication channels](https://matplotlib.org/stable/devel/contributing.html#get-connected). + If something is unclear, please reach out on any of our [communication + channels](https://matplotlib.org/stable/devel/contributing.html#get-connected). diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 000000000000..dc7a0716bfe8 --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,15 @@ +--- +name: "Pull Request Labeler" +on: + - pull_request_target + +jobs: + labeler: + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: actions/labeler@v5 + with: + sync-labels: true diff --git a/.github/workflows/mypy-stubtest.yml b/.github/workflows/mypy-stubtest.yml index efd37a38b15b..969aacccad74 100644 --- a/.github/workflows/mypy-stubtest.yml +++ b/.github/workflows/mypy-stubtest.yml @@ -18,29 +18,19 @@ jobs: with: python-version: 3.9 - - name: Install mypy - run: | - pip3 install -r requirements/testing/mypy.txt \ - -r requirements/testing/all.txt - pip3 install -e . - - name: Set up reviewdog - run: | - mkdir -p "$HOME/bin" - curl -sfL \ - https://github.com/reviewdog/reviewdog/raw/master/install.sh | \ - sh -s -- -b "$HOME/bin" - echo "$HOME/bin" >> $GITHUB_PATH + uses: reviewdog/action-setup@v1 + + - name: Install tox + run: python -m pip install tox - name: Run mypy stubtest env: REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | set -o pipefail - MPLBACKEND=agg python -m mypy.stubtest \ - --mypy-config-file pyproject.toml \ - --allowlist ci/mypy-stubtest-allowlist.txt \ - matplotlib | \ + tox -e stubtest | \ + sed -e "s!.tox/stubtest/lib/python3.9/site-packages!lib!g" | \ reviewdog \ -efm '%Eerror: %m' \ -efm '%CStub: in file %f:%l' \ diff --git a/.github/workflows/nightlies.yml b/.github/workflows/nightlies.yml index 31d043f55819..54e81f06b166 100644 --- a/.github/workflows/nightlies.yml +++ b/.github/workflows/nightlies.yml @@ -32,7 +32,7 @@ jobs: PROJECT_REPO="matplotlib/matplotlib" BRANCH="main" WORKFLOW_NAME="cibuildwheel.yml" - ARTIFACT_NAME="wheels" + ARTIFACT_PATTERN="cibw-wheels-*" gh run --repo "${PROJECT_REPO}" \ list --branch "${BRANCH}" \ @@ -52,14 +52,14 @@ jobs: ) gh run --repo "${PROJECT_REPO}" view "${RUN_ID}" gh run --repo "${PROJECT_REPO}" \ - download "${RUN_ID}" --name "${ARTIFACT_NAME}" + download "${RUN_ID}" --pattern "${ARTIFACT_PATTERN}" mkdir dist - mv *.whl dist/ + mv ${ARTIFACT_PATTERN}/*.whl dist/ ls -l dist/ - name: Upload wheels to Anaconda Cloud as nightlies - uses: scientific-python/upload-nightly-action@8f0394fd2aa0c85d7364a9958652e8994e06b23c # 0.1.0 + uses: scientific-python/upload-nightly-action@b67d7fcc0396e1128a474d1ab2b48aa94680f9fc # 0.5.0 with: artifacts_path: dist anaconda_nightly_upload_token: ${{ secrets.ANACONDA_ORG_UPLOAD_TOKEN }} diff --git a/.github/workflows/reviewdog.yml b/.github/workflows/reviewdog.yml index f4495eda8b84..fbd724571d80 100644 --- a/.github/workflows/reviewdog.yml +++ b/.github/workflows/reviewdog.yml @@ -23,12 +23,7 @@ jobs: run: pip3 install -r requirements/testing/flake8.txt - name: Set up reviewdog - run: | - mkdir -p "$HOME/bin" - curl -sfL \ - https://github.com/reviewdog/reviewdog/raw/master/install.sh | \ - sh -s -- -b "$HOME/bin" - echo "$HOME/bin" >> $GITHUB_PATH + uses: reviewdog/action-setup@v1 - name: Run flake8 env: @@ -53,20 +48,14 @@ jobs: run: pip3 install -r requirements/testing/mypy.txt -r requirements/testing/all.txt - name: Set up reviewdog - run: | - mkdir -p "$HOME/bin" - curl -sfL \ - https://github.com/reviewdog/reviewdog/raw/master/install.sh | \ - sh -s -- -b "$HOME/bin" - echo "$HOME/bin" >> $GITHUB_PATH + uses: reviewdog/action-setup@v1 - name: Run mypy env: REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | set -o pipefail - mypy --config pyproject.toml lib/matplotlib \ - --follow-imports silent | \ + mypy --config pyproject.toml | \ reviewdog -f=mypy -name=mypy \ -tee -reporter=github-check -filter-mode nofilter diff --git a/.github/workflows/stale-tidy.yml b/.github/workflows/stale-tidy.yml new file mode 100644 index 000000000000..92a81ee856e4 --- /dev/null +++ b/.github/workflows/stale-tidy.yml @@ -0,0 +1,24 @@ +--- +name: 'Close inactive issues' +on: + schedule: + - cron: '30 1 * * 2,4,6' + +jobs: + stale: + if: github.repository == 'matplotlib/matplotlib' + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v9 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + operations-per-run: 300 + days-before-stale: -1 + stale-pr-label: "status: inactive" + days-before-pr-close: -1 + stale-issue-label: "status: inactive" + close-issue-label: "status: closed as inactive" + days-before-issue-close: 30 + ascending: true + exempt-issue-labels: "keep" + exempt-pr-labels: "keep,status: orphaned PR" diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index c4e168216145..c606d4288bd2 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -1,3 +1,4 @@ +--- name: 'Label inactive PRs' on: schedule: @@ -8,15 +9,26 @@ jobs: if: github.repository == 'matplotlib/matplotlib' runs-on: ubuntu-latest steps: - - uses: actions/stale@v8 + - uses: actions/stale@v9 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - operations-per-run: 150 - stale-pr-message: 'Since this Pull Request has not been updated in 60 days, it has been marked "inactive." This does not mean that it will be closed, though it may be moved to a "Draft" state. This helps maintainers prioritize their reviewing efforts. You can pick the PR back up anytime - please ping us if you need a review or guidance to move the PR forward! If you do not plan on continuing the work, please let us know so that we can either find someone to take the PR over, or close it.' + operations-per-run: 20 + stale-pr-message: >- + Since this Pull Request has not been updated in 60 days, it has been marked "inactive." This does + not mean that it will be closed, though it may be moved to a "Draft" state. This helps maintainers + prioritize their reviewing efforts. You can pick the PR back up anytime - please ping us if you + need a review or guidance to move the PR forward! If you do not plan on continuing the work, please + let us know so that we can either find someone to take the PR over, or close it. stale-pr-label: "status: inactive" days-before-pr-stale: 60 days-before-pr-close: -1 - stale-issue-message: 'This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!' + stale-issue-message: >- + This issue has been marked "inactive" because it has been 365 days since the last comment. If this + issue is still present in recent Matplotlib releases, or the feature request is still wanted, + please leave a comment and this label will be removed. If there are no updates in another 30 days, + this issue will be automatically closed, but you are free to re-open or create a new issue if + needed. We value issue reports, and this procedure is meant to help us resurface and prioritize + issues that have not been addressed yet, not make them disappear. Thanks for your help! stale-issue-label: "status: inactive" close-issue-label: "status: closed as inactive" days-before-issue-stale: 365 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a9724425b60d..634c83fa57fd 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,6 +9,7 @@ on: branches-ignore: - auto-backport-of-pr-[0-9]+ - v[0-9]+.[0-9]+.[0-9x]+-doc + - dependabot/** pull_request: branches-ignore: - v[0-9]+.[0-9]+.[0-9x]+-doc @@ -29,7 +30,7 @@ env: jobs: test: - if: | + if: >- github.event_name == 'workflow_dispatch' || ( github.repository == 'matplotlib/matplotlib' && @@ -56,14 +57,14 @@ jobs: pyside2-ver: '==5.15.1' # oldest version with working Py3.9 wheel. pyside6-ver: '==6.0.0' delete-font-cache: true - no-build-isolation: true - os: ubuntu-20.04 python-version: 3.9 - extra-requirements: '-r requirements/testing/extra.txt' + # One CI run tests ipython/matplotlib-inline before backend mapping moved to mpl + extra-requirements: '-r requirements/testing/extra.txt "ipython==7.19" "matplotlib-inline<0.1.7"' CFLAGS: "-fno-lto" # Ensure that disabling LTO works. # https://github.com/matplotlib/matplotlib/pull/26052#issuecomment-1574595954 # https://www.riverbankcomputing.com/pipermail/pyqt/2023-November/045606.html - pyqt6-ver: '!=6.5.1,!=6.6.0' + pyqt6-ver: '!=6.5.1,!=6.6.0,!=6.7.1' # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 pyside6-ver: '!=6.5.1' - os: ubuntu-20.04 @@ -71,7 +72,7 @@ jobs: extra-requirements: '-r requirements/testing/extra.txt' # https://github.com/matplotlib/matplotlib/pull/26052#issuecomment-1574595954 # https://www.riverbankcomputing.com/pipermail/pyqt/2023-November/045606.html - pyqt6-ver: '!=6.5.1,!=6.6.0' + pyqt6-ver: '!=6.5.1,!=6.6.0,!=6.7.1' # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 pyside6-ver: '!=6.5.1' - os: ubuntu-22.04 @@ -87,10 +88,14 @@ jobs: pyqt6-ver: '!=6.6.0' # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 pyside6-ver: '!=6.5.1' - - os: macos-latest + - os: macos-12 # This runnre is on Intel chips. python-version: 3.9 # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 pyside6-ver: '!=6.5.1' + - os: macos-14 # This runner is on M1 (arm64) chips. + python-version: '3.12' + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 + pyside6-ver: '!=6.5.1' steps: - uses: actions/checkout@v4 @@ -108,12 +113,14 @@ jobs: Linux) echo 'Acquire::Retries "3";' | sudo tee /etc/apt/apt.conf.d/80-retries sudo apt-get update -yy - sudo apt-get install -yy \ + sudo apt-get install -yy --no-install-recommends \ ccache \ cm-super \ dvipng \ ffmpeg \ + fonts-freefont-otf \ fonts-noto-cjk \ + fonts-wqy-zenhei \ gdb \ gir1.2-gtk-3.0 \ graphviz \ @@ -135,8 +142,7 @@ jobs: libxcb-render-util0 \ libxcb-xinerama0 \ lmodern \ - fonts-freefont-otf \ - texlive-pictures \ + ninja-build \ pkg-config \ qtbase5-dev \ texlive-fonts-recommended \ @@ -144,23 +150,24 @@ jobs: texlive-latex-extra \ texlive-latex-recommended \ texlive-luatex \ - texlive-xetex \ - ttf-wqy-zenhei + texlive-pictures \ + texlive-xetex if [[ "${{ matrix.os }}" = ubuntu-20.04 ]]; then - sudo apt-get install -yy libopengl0 + sudo apt-get install -yy --no-install-recommends libopengl0 else # ubuntu-22.04 - sudo apt-get install -yy gir1.2-gtk-4.0 libnotify4 + sudo apt-get install -yy --no-install-recommends \ + gir1.2-gtk-4.0 libnotify4 fi ;; macOS) - brew install ccache - brew tap homebrew/cask-fonts - brew install font-noto-sans-cjk gobject-introspection gtk4 + brew update + brew install ccache ghostscript gobject-introspection gtk4 ninja + brew install --cask font-noto-sans-cjk inkscape ;; esac - name: Cache pip - uses: actions/cache@v3 + uses: actions/cache@v4 if: startsWith(runner.os, 'Linux') with: path: ~/.cache/pip @@ -168,7 +175,7 @@ jobs: restore-keys: | ${{ matrix.os }}-py${{ matrix.python-version }}-pip- - name: Cache pip - uses: actions/cache@v3 + uses: actions/cache@v4 if: startsWith(runner.os, 'macOS') with: path: ~/Library/Caches/pip @@ -176,7 +183,7 @@ jobs: restore-keys: | ${{ matrix.os }}-py${{ matrix.python-version }}-pip- - name: Cache ccache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ~/.ccache @@ -184,16 +191,16 @@ jobs: restore-keys: | ${{ matrix.os }}-py${{ matrix.python-version }}-ccache- - name: Cache Matplotlib - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ~/.cache/matplotlib !~/.cache/matplotlib/tex.cache !~/.cache/matplotlib/test_cache - key: 3-${{ runner.os }}-py${{ matrix.python-version }}-mpl-${{ github.ref }}-${{ github.sha }} + key: 4-${{ runner.os }}-py${{ matrix.python-version }}-mpl-${{ github.ref }}-${{ github.sha }} restore-keys: | - 3-${{ runner.os }}-py${{ matrix.python-version }}-mpl-${{ github.ref }}- - 3-${{ runner.os }}-py${{ matrix.python-version }}-mpl- + 4-${{ runner.os }}-py${{ matrix.python-version }}-mpl-${{ github.ref }}- + 4-${{ runner.os }}-py${{ matrix.python-version }}-mpl- - name: Install Python dependencies run: | @@ -208,17 +215,14 @@ jobs: fi # Install dependencies from PyPI. + # Preinstall build requirements to enable no-build-isolation builds. python -m pip install --upgrade $PRE \ 'contourpy>=1.0.1' cycler fonttools kiwisolver importlib_resources \ numpy packaging pillow 'pyparsing!=3.1.0' python-dateutil setuptools-scm \ + 'meson-python>=0.13.1' 'pybind11>=2.6' \ -r requirements/testing/all.txt \ ${{ matrix.extra-requirements }} - # Preinstall pybind11 on no-build-isolation builds. - if [[ "${{ matrix.no-build-isolation }}" == 'true' ]]; then - python -m pip install 'pybind11>=2.6' - fi - # Install optional dependencies from PyPI. # Sphinx is needed to run sphinxext tests python -m pip install --upgrade sphinx!=6.1.2 @@ -229,7 +233,6 @@ jobs: # (sometimes, the install appears to be successful but shared # libraries cannot be loaded at runtime, so an actual import is a # better check). - # PyGObject, pycairo, and cariocffi do not install on OSX 10.12. python -m pip install --upgrade pycairo 'cairocffi>=0.8' PyGObject && ( python -c 'import gi; gi.require_version("Gtk", "4.0"); from gi.repository import Gtk' && @@ -239,49 +242,39 @@ jobs: echo 'PyGObject 3 is available' || echo 'PyGObject 3 is not available' ) - # There are no functioning wheels available for OSX 10.12 (as of - # Sept 2020) for either pyqt5 (there are only wheels for 10.13+) or - # pyside2 (the latest version (5.13.2) with 10.12 wheels has a - # fatal to us bug, it was fixed in 5.14.0 which has 10.13 wheels) python -mpip install --upgrade pyqt5${{ matrix.pyqt5-ver }} && python -c 'import PyQt5.QtCore' && echo 'PyQt5 is available' || echo 'PyQt5 is not available' - if [[ "${{ runner.os }}" != 'macOS' + # Even though PySide2 wheels can be installed on Python 3.12, they are broken and since PySide2 is + # deprecated, they are unlikely to be fixed. For the same deprecation reason, there are no wheels + # on M1 macOS, so don't bother there either. + if [[ "${{ matrix.os }}" != 'macos-14' && "${{ matrix.python-version }}" != '3.12' ]]; then python -mpip install --upgrade pyside2${{ matrix.pyside2-ver }} && python -c 'import PySide2.QtCore' && echo 'PySide2 is available' || echo 'PySide2 is not available' fi - if [[ "${{ runner.os }}" != 'macOS' ]]; then - python -mpip install --upgrade pyqt6${{ matrix.pyqt6-ver }} && - python -c 'import PyQt6.QtCore' && - echo 'PyQt6 is available' || - echo 'PyQt6 is not available' - fi - if [[ "${{ runner.os }}" != 'macOS' - && "${{ matrix.python-version }}" != '3.12' ]]; then - python -mpip install --upgrade pyside6${{ matrix.pyside6-ver }} && - python -c 'import PySide6.QtCore' && - echo 'PySide6 is available' || - echo 'PySide6 is not available' - fi + python -mpip install --upgrade pyqt6${{ matrix.pyqt6-ver }} && + python -c 'import PyQt6.QtCore' && + echo 'PyQt6 is available' || + echo 'PyQt6 is not available' + python -mpip install --upgrade pyside6${{ matrix.pyside6-ver }} && + python -c 'import PySide6.QtCore' && + echo 'PySide6 is available' || + echo 'PySide6 is not available' - if [[ "${{ matrix.python-version }}" != '3.12' ]]; then - python -mpip install --upgrade \ - -f "https://extras.wxpython.org/wxPython4/extras/linux/gtk3/${{ matrix.os }}" \ - wxPython && - python -c 'import wx' && - echo 'wxPython is available' || - echo 'wxPython is not available' - fi + python -mpip install --upgrade \ + -f "https://extras.wxpython.org/wxPython4/extras/linux/gtk3/${{ matrix.os }}" \ + wxPython && + python -c 'import wx' && + echo 'wxPython is available' || + echo 'wxPython is not available' - name: Install the nightly dependencies # Only install the nightly dependencies during the scheduled event - if: | - github.event_name == 'schedule' && - matrix.name-suffix != '(Minimum Versions)' + if: github.event_name == 'schedule' && matrix.name-suffix != '(Minimum Versions)' run: | python -m pip install pytz tzdata # Must be installed for Pandas. python -m pip install \ @@ -295,29 +288,17 @@ jobs: # Set flag in a delayed manner to avoid issues with installing other # packages - if [[ "${{ runner.os }}" != 'macOS' ]]; then - if [[ "$(lsb_release -r -s)" == "20.04" ]]; then - export CPPFLAGS='--coverage -fprofile-abs-path' - else - export CPPFLAGS='--coverage' - fi - fi - - cat <> mplsetup.cfg - [rc_options] - backend=Agg - EOT - - cat mplsetup.cfg - - if [[ "${{ matrix.no-build-isolation }}" == 'true' ]]; then - # Minimum versions run does not use build isolation so that it - # builds against the pre-installed minver dependencies. - python -m pip install --no-deps --no-build-isolation -ve . + if [[ "${{ runner.os }}" == 'macOS' ]]; then + export CPPFLAGS='-fprofile-instr-generate=default.%m.profraw' + export CPPFLAGS="$CPPFLAGS -fcoverage-mapping" else - python -m pip install --no-deps -ve . + export CPPFLAGS='--coverage -fprofile-abs-path' fi + python -m pip install --no-deps --no-build-isolation --verbose \ + --config-settings=setup-args="-DrcParams-backend=Agg" \ + --editable .[dev] + if [[ "${{ runner.os }}" != 'macOS' ]]; then unset CPPFLAGS fi @@ -329,22 +310,34 @@ jobs: - name: Run pytest run: | - python -mpytest -raR -n auto \ + pytest -rfEsXR -n auto \ --maxfail=50 --timeout=300 --durations=25 \ --cov-report=xml --cov=lib --log-level=DEBUG --color=yes - name: Filter C coverage + if: ${{ !cancelled() && github.event_name != 'schedule' }} run: | - lcov --rc lcov_branch_coverage=1 --capture --directory . --output-file coverage.info - lcov --rc lcov_branch_coverage=1 --output-file coverage.info \ - --extract coverage.info $PWD/src/'*' $PWD/lib/'*' - lcov --rc lcov_branch_coverage=1 --list coverage.info - find . -name '*.gc*' -delete - if: ${{ runner.os != 'macOS' }} + if [[ "${{ runner.os }}" != 'macOS' ]]; then + lcov --rc lcov_branch_coverage=1 --capture --directory . \ + --output-file coverage.info + lcov --rc lcov_branch_coverage=1 --output-file coverage.info \ + --extract coverage.info $PWD/src/'*' $PWD/lib/'*' + lcov --rc lcov_branch_coverage=1 --list coverage.info + find . -name '*.gc*' -delete + else + xcrun llvm-profdata merge -sparse default.*.profraw \ + -o default.profdata + xcrun llvm-cov export -format="lcov" build/*/src/*.so \ + -instr-profile default.profdata > info.lcov + fi - name: Upload code coverage - uses: codecov/codecov-action@v3 + if: ${{ !cancelled() && github.event_name != 'schedule' }} + uses: codecov/codecov-action@v4 + with: + name: "${{ matrix.python-version }} ${{ matrix.os }} ${{ matrix.name-suffix }}" + token: ${{ secrets.CODECOV_TOKEN }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: failure() with: name: "${{ matrix.python-version }} ${{ matrix.os }} ${{ matrix.name-suffix }} result images" diff --git a/.gitignore b/.gitignore index 9c6bb2263c99..b6f9e1ee74f4 100644 --- a/.gitignore +++ b/.gitignore @@ -29,10 +29,11 @@ # Python files # ################ -# setup.py working directory +# meson-python working directory build +.mesonpy* -# setup.py dist directory +# meson-python/build frontend dist directory dist # Egg metadata *.egg-info @@ -41,9 +42,9 @@ dist pip-wheel-metadata/* # tox testing tool .tox -mplsetup.cfg -# generated by setuptools_scm -lib/matplotlib/_version.py +# build subproject files +subprojects/*/ +!subprojects/packagefiles/ # OS generated files # ###################### @@ -82,6 +83,8 @@ galleries/examples/*/*.svgz result_images doc/_static/constrained_layout*.png doc/.mpl_skip_subdirs.yaml +doc/_tags +sg_execution_times.rst # Nose/Pytest generated files # ############################### diff --git a/.meeseeksdev.yml b/.meeseeksdev.yml index 8bfd1b8e4257..f9d44d44cfdf 100644 --- a/.meeseeksdev.yml +++ b/.meeseeksdev.yml @@ -1,3 +1,4 @@ +--- users: Carreau: can: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e213bf188f4c..14817e95929f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,3 +1,4 @@ +--- ci: autofix_prs: false autoupdate_schedule: 'quarterly' @@ -13,7 +14,7 @@ exclude: | ) repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-added-large-files - id: check-docstring-first @@ -23,31 +24,99 @@ repos: - id: mixed-line-ending - id: name-tests-test args: ["--pytest-test-first"] - - id: no-commit-to-branch #default is master and main + - id: no-commit-to-branch # Default is master and main. - id: trailing-whitespace exclude_types: [svg] - + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.9.0 + hooks: + - id: mypy + additional_dependencies: + - pandas-stubs + - types-pillow + - types-python-dateutil + - types-psutil + - types-docutils + - types-PyYAML + args: ["--config-file=pyproject.toml", "lib/matplotlib"] + files: lib/matplotlib # Only run when files in lib/matplotlib are changed. + pass_filenames: false - repo: https://github.com/pycqa/flake8 - rev: 6.0.0 + rev: 7.0.0 hooks: - id: flake8 - additional_dependencies: [pydocstyle>5.1.0, flake8-docstrings>1.4.0, flake8-force] + additional_dependencies: + - pydocstyle>5.1.0 + - flake8-docstrings>1.4.0 + - flake8-force args: ["--docstring-convention=all"] - repo: https://github.com/codespell-project/codespell - rev: v2.2.5 + rev: v2.2.6 hooks: - id: codespell files: ^.*\.(py|c|cpp|h|m|md|rst|yml)$ - args: [ - "--ignore-words", - "ci/codespell-ignore-words.txt", - "--skip", - "doc/users/project/credits.rst" - ] - + args: + - "--ignore-words" + - "ci/codespell-ignore-words.txt" + - "--skip" + - "doc/project/credits.rst" - repo: https://github.com/pycqa/isort - rev: 5.12.0 + rev: 5.13.2 hooks: - id: isort name: isort (python) files: ^galleries/tutorials/|^galleries/examples/|^galleries/plot_types/ + - repo: https://github.com/rstcheck/rstcheck + rev: v6.2.0 + hooks: + - id: rstcheck + additional_dependencies: + - sphinx>=1.8.1 + - tomli + - repo: https://github.com/adrienverge/yamllint + rev: v1.35.1 + hooks: + - id: yamllint + args: ["--strict", "--config-file=.yamllint.yml"] + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.28.4 + hooks: + # TODO: Re-enable this when https://github.com/microsoft/azure-pipelines-vscode/issues/567 is fixed. + # - id: check-azure-pipelines + - id: check-dependabot + - id: check-github-workflows + # NOTE: If any of the below schema files need to be changed, be sure to + # update the `ci/vendor_schemas.py` script. + - id: check-jsonschema + name: "Validate AppVeyor config" + files: ^\.appveyor\.yml$ + args: ["--verbose", "--schemafile", "ci/schemas/appveyor.json"] + - id: check-jsonschema + name: "Validate CircleCI config" + files: ^\.circleci/config\.yml$ + args: ["--verbose", "--schemafile", "ci/schemas/circleciconfig.json"] + - id: check-jsonschema + name: "Validate GitHub funding file" + files: ^\.github/FUNDING\.yml$ + args: ["--verbose", "--schemafile", "ci/schemas/github-funding.json"] + - id: check-jsonschema + name: "Validate GitHub issue config" + files: ^\.github/ISSUE_TEMPLATE/config\.yml$ + args: ["--verbose", "--schemafile", "ci/schemas/github-issue-config.json"] + - id: check-jsonschema + name: "Validate GitHub issue templates" + files: ^\.github/ISSUE_TEMPLATE/.*\.yml$ + exclude: ^\.github/ISSUE_TEMPLATE/config\.yml$ + args: ["--verbose", "--schemafile", "ci/schemas/github-issue-forms.json"] + - id: check-jsonschema + name: "Validate CodeCov config" + files: ^\.github/codecov\.yml$ + args: ["--verbose", "--schemafile", "ci/schemas/codecov.json"] + - id: check-jsonschema + name: "Validate GitHub labeler config" + files: ^\.github/labeler\.yml$ + args: ["--verbose", "--schemafile", "ci/schemas/pull-request-labeler-5.json"] + - id: check-jsonschema + name: "Validate Conda environment file" + files: ^environment\.yml$ + args: ["--verbose", "--schemafile", "ci/schemas/conda-environment.json"] diff --git a/.yamllint.yml b/.yamllint.yml new file mode 100644 index 000000000000..2be81b28c7fb --- /dev/null +++ b/.yamllint.yml @@ -0,0 +1,9 @@ +--- +extends: default + +rules: + line-length: + max: 120 + allow-non-breakable-words: true + truthy: + check-keys: false diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 657eb14336b9..8fbbe8e7d6f3 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,6 +1,6 @@ Our Code of Conduct is at -https://matplotlib.org/stable/users/project/code_of_conduct.html +https://matplotlib.org/stable/project/code_of_conduct.html -It is rendered from `doc/users/project/code_of_conduct.rst` +It is rendered from `doc/project/code_of_conduct.rst` diff --git a/INSTALL.rst b/INSTALL.rst index ac24c70ac518..3fb01c58d259 100644 --- a/INSTALL.rst +++ b/INSTALL.rst @@ -1 +1 @@ -See doc/users/installing/index.rst +See doc/install/index.rst diff --git a/README.md b/README.md index 5e15c645c9a2..7b9c99597c0d 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ [![Azure pipelines status](https://dev.azure.com/matplotlib/matplotlib/_apis/build/status/matplotlib.matplotlib?branchName=main)](https://dev.azure.com/matplotlib/matplotlib/_build/latest?definitionId=1&branchName=main) [![AppVeyor status](https://ci.appveyor.com/api/projects/status/github/matplotlib/matplotlib?branch=main&svg=true)](https://ci.appveyor.com/project/matplotlib/matplotlib) [![Codecov status](https://codecov.io/github/matplotlib/matplotlib/badge.svg?branch=main&service=github)](https://app.codecov.io/gh/matplotlib/matplotlib) +[![EffVer Versioning](https://img.shields.io/badge/version_scheme-EffVer-0097a7)](https://jacobtomlinson.dev/effver) ![Matplotlib logotype](https://matplotlib.org/_static/logo2.svg) @@ -31,7 +32,7 @@ and various graphical user interface toolkits. See the [install documentation](https://matplotlib.org/stable/users/installing/index.html), -which is generated from `/doc/users/installing/index.rst` +which is generated from `/doc/install/index.rst` ## Contribute @@ -42,7 +43,7 @@ You've worked out a way to fix it — even better! You want to tell us about it — best of all! Start at the [contributing -guide](https://matplotlib.org/devdocs/devel/contributing.html)! +guide](https://matplotlib.org/devdocs/devel/contribute.html)! ## Contact diff --git a/SECURITY.md b/SECURITY.md index 88d523bec637..ce022ca60a0f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -8,8 +8,9 @@ versions. | Version | Supported | | ------- | ------------------ | +| 3.9.x | :white_check_mark: | | 3.8.x | :white_check_mark: | -| 3.7.x | :white_check_mark: | +| 3.7.x | :x: | | 3.6.x | :x: | | 3.5.x | :x: | | 3.4.x | :x: | diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4e01d67f7385..4c50c543846a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,8 +1,10 @@ # Python package # Create and test a Python package on multiple Python versions. -# Add steps that analyze code, save the dist with the build record, publish to a PyPI-compatible index, and more: +# Add steps that analyze code, save the dist with the build record, publish to a PyPI-compatible index, and +# more: # https://docs.microsoft.com/en-us/azure/devops/pipelines/ecosystems/python?view=azure-devops +--- trigger: branches: exclude: @@ -18,149 +20,268 @@ pr: stages: -- stage: Check - jobs: - - job: Skip - pool: - vmImage: 'ubuntu-latest' - variables: - DECODE_PERCENTS: 'false' - RET: 'true' - steps: - - bash: | - git_log=`git log --max-count=1 --skip=1 --pretty=format:"%B" | tr "\n" " "` - echo "##vso[task.setvariable variable=log]$git_log" - - bash: echo "##vso[task.setvariable variable=RET]false" - condition: or(contains(variables.log, '[skip azp]'), contains(variables.log, '[azp skip]'), contains(variables.log, '[skip ci]'), contains(variables.log, '[ci skip]'), contains(variables.log, '[ci doc]')) - - bash: echo "##vso[task.setvariable variable=start_main;isOutput=true]$RET" - name: result + - stage: Check + jobs: + - job: Skip + pool: + vmImage: 'ubuntu-latest' + variables: + DECODE_PERCENTS: 'false' + RET: 'true' + steps: + - bash: | + git_log=`git log --max-count=1 --skip=1 --pretty=format:"%B" | tr "\n" " "` + echo "##vso[task.setvariable variable=log]$git_log" + - bash: echo "##vso[task.setvariable variable=RET]false" + condition: >- + or(contains(variables.log, '[skip azp]'), + contains(variables.log, '[azp skip]'), + contains(variables.log, '[skip ci]'), + contains(variables.log, '[ci skip]'), + contains(variables.log, '[ci doc]')) + - bash: echo "##vso[task.setvariable variable=start_main;isOutput=true]$RET" + name: result -- stage: Main - condition: and(succeeded(), eq(dependencies.Check.outputs['Skip.result.start_main'], 'true')) - dependsOn: Check - jobs: - - job: Pytest - strategy: - matrix: - Linux_py39: - vmImage: 'ubuntu-20.04' # keep one job pinned to the oldest image - python.version: '3.9' - Linux_py310: - vmImage: 'ubuntu-latest' - python.version: '3.10' - Linux_py311: - vmImage: 'ubuntu-latest' - python.version: '3.11' - macOS_py39: - vmImage: 'macOS-latest' - python.version: '3.9' - macOS_py310: - vmImage: 'macOS-latest' - python.version: '3.10' - macOS_py311: - vmImage: 'macOS-latest' - python.version: '3.11' - Windows_py39: - vmImage: 'windows-2019' # keep one job pinned to the oldest image - python.version: '3.9' - Windows_py310: - vmImage: 'windows-latest' - python.version: '3.10' - Windows_py311: - vmImage: 'windows-latest' - python.version: '3.11' - maxParallel: 4 - pool: - vmImage: '$(vmImage)' - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: '$(python.version)' - architecture: 'x64' - displayName: 'Use Python $(python.version)' - condition: and(succeeded(), ne(variables['python.version'], 'Pre')) + - stage: Main + condition: and(succeeded(), eq(dependencies.Check.outputs['Skip.result.start_main'], 'true')) + dependsOn: Check + jobs: + - job: Pytest + strategy: + matrix: + Linux_py39: + vmImage: 'ubuntu-20.04' # keep one job pinned to the oldest image + python.version: '3.9' + Linux_py310: + vmImage: 'ubuntu-latest' + python.version: '3.10' + Linux_py311: + vmImage: 'ubuntu-latest' + python.version: '3.11' + macOS_py39: + vmImage: 'macOS-latest' + python.version: '3.9' + macOS_py310: + vmImage: 'macOS-latest' + python.version: '3.10' + macOS_py311: + vmImage: 'macOS-latest' + python.version: '3.11' + Windows_py39: + vmImage: 'windows-2019' # keep one job pinned to the oldest image + python.version: '3.9' + Windows_py310: + vmImage: 'windows-latest' + python.version: '3.10' + Windows_py311: + vmImage: 'windows-latest' + python.version: '3.11' + maxParallel: 4 + pool: + vmImage: '$(vmImage)' + steps: + - task: UsePythonVersion@0 + inputs: + versionSpec: '$(python.version)' + architecture: 'x64' + displayName: 'Use Python $(python.version)' - - task: stevedower.python.InstallPython.InstallPython@1 - displayName: 'Use prerelease Python' - inputs: - prerelease: true - condition: and(succeeded(), eq(variables['python.version'], 'Pre')) + - bash: | + set -e + case "$AGENT_OS" in + Linux) + echo 'Acquire::Retries "3";' | sudo tee /etc/apt/apt.conf.d/80-retries + sudo apt update + sudo apt install --no-install-recommends \ + cm-super \ + dvipng \ + ffmpeg \ + fonts-freefont-otf \ + fonts-noto-cjk \ + fonts-wqy-zenhei \ + gdb \ + gir1.2-gtk-3.0 \ + graphviz \ + inkscape \ + language-pack-de \ + lcov \ + libcairo2 \ + libgirepository-1.0-1 \ + lmodern \ + ninja-build \ + poppler-utils \ + texlive-fonts-recommended \ + texlive-latex-base \ + texlive-latex-extra \ + texlive-latex-recommended \ + texlive-luatex \ + texlive-pictures \ + texlive-xetex + ;; + Darwin) + brew update + brew install --cask xquartz + brew install ccache ffmpeg imagemagick mplayer ninja pkg-config + brew install --cask font-noto-sans-cjk-sc + ;; + Windows_NT) + choco install ninja + ;; + *) + exit 1 + ;; + esac + displayName: 'Install dependencies' - - bash: | - set -e - case "$(python -c 'import sys; print(sys.platform)')" in - linux) - echo 'Acquire::Retries "3";' | sudo tee /etc/apt/apt.conf.d/80-retries - sudo apt update - sudo apt install \ - cm-super \ - dvipng \ - ffmpeg \ - fonts-noto-cjk \ - gdb \ - gir1.2-gtk-3.0 \ - graphviz \ - inkscape \ - language-pack-de \ - libcairo2 \ - libgirepository-1.0-1 \ - lmodern \ - fonts-freefont-otf \ - poppler-utils \ - texlive-pictures \ - texlive-fonts-recommended \ - texlive-latex-base \ - texlive-latex-extra \ - texlive-latex-recommended \ - texlive-xetex texlive-luatex \ - ttf-wqy-zenhei - ;; - darwin) - brew install --cask xquartz - brew install pkg-config ffmpeg imagemagick mplayer ccache - brew tap homebrew/cask-fonts - brew install font-noto-sans-cjk-sc - ;; - win32) - ;; - *) - exit 1 - ;; - esac - displayName: 'Install dependencies' + - bash: | + python -m pip install --upgrade pip + python -m pip install --upgrade meson-python numpy pybind11 setuptools-scm + python -m pip install -r requirements/testing/all.txt -r requirements/testing/extra.txt + displayName: 'Install dependencies with pip' - - bash: | - python -m pip install --upgrade pip - python -m pip install -r requirements/testing/all.txt -r requirements/testing/extra.txt || - [[ "$PYTHON_VERSION" = 'Pre' ]] - displayName: 'Install dependencies with pip' + - bash: | + case "$AGENT_OS" in + Linux) + export CPPFLAGS='--coverage -fprofile-abs-path' + ;; + Darwin) + export CPPFLAGS='-fprofile-instr-generate=default.%m.profraw' + export CPPFLAGS="$CPPFLAGS -fcoverage-mapping" + ;; + Windows_NT) + CONFIG='--config-settings=setup-args=--vsenv' + CONFIG="$CONFIG --config-settings=setup-args=-Dcpp_link_args=-PROFILE" + CONFIG="$CONFIG --config-settings=setup-args=-Dbuildtype=debug" + ;; + *) + exit 1 + ;; + esac - - bash: | - python -m pip install -ve . || - [[ "$PYTHON_VERSION" = 'Pre' ]] - displayName: "Install self" + python -m pip install \ + --no-build-isolation $CONFIG \ + --verbose --editable .[dev] + displayName: "Install self" - - script: env - displayName: 'print env' + - script: env + displayName: 'print env' - - script: pip list - displayName: 'print pip' + - script: pip list + displayName: 'print pip' - - bash: | - PYTHONFAULTHANDLER=1 python -m pytest --junitxml=junit/test-results.xml -raR --maxfail=50 --timeout=300 --durations=25 --cov-report= --cov=lib -n 2 || - [[ "$PYTHON_VERSION" = 'Pre' ]] - displayName: 'pytest' + - bash: | + set -e + if [[ "$AGENT_OS" == 'Windows_NT' ]]; then + SESSION_ID=$(python -c "import uuid; print(uuid.uuid4(), end='')") + echo "Coverage session ID: ${SESSION_ID}" + VS=$(ls -d /c/Program\ Files*/Microsoft\ Visual\ Studio/*/Enterprise) + echo "Visual Studio: ${VS}" + DIR="$VS/Common7/IDE/Extensions/Microsoft/CodeCoverage.Console" + if [[ -d $DIR ]]; then + # This is for MSVC 2022 (on windows-latest). + TOOL="$DIR/Microsoft.CodeCoverage.Console.exe" + for f in build/cp*/src/*.pyd; do + echo $f + echo "==============================" + "$TOOL" instrument $f --session-id $SESSION_ID \ + --log-level Verbose --log-file instrument.log + cat instrument.log + rm instrument.log + done + echo "Starting $TOOL in server mode" + "$TOOL" collect \ + --session-id $SESSION_ID --server-mode \ + --output-format cobertura --output extensions.xml \ + --log-level Verbose --log-file extensions.log & + VS_VER=2022 + else + DIR="$VS"/Team\ Tools/Dynamic\ Code\ Coverage\ Tools/amd64 + if [[ -d $DIR ]]; then + # This is for MSVC 2019 (on windows-2019). + VSINSTR="$VS"/Team\ Tools/Performance\ Tools/vsinstr.exe + for f in build/cp*/src/*.pyd; do + "$VSINSTR" $f -Verbose -Coverage + done + TOOL="$DIR/CodeCoverage.exe" + cat > extensions.config << EOF + + true + + + .*\\.*\.pyd + + + + EOF + echo "Starting $TOOL in server mode" + "$TOOL" collect \ + -config:extensions.config -session:$SESSION_ID \ + -output:extensions.coverage -verbose & + echo "Started $TOOL" + VS_VER=2019 + fi + fi + echo "##vso[task.setvariable variable=VS_COVERAGE_TOOL]$TOOL" + fi + PYTHONFAULTHANDLER=1 pytest -rfEsXR -n 2 \ + --maxfail=50 --timeout=300 --durations=25 \ + --junitxml=junit/test-results.xml --cov-report=xml --cov=lib + if [[ -n $SESSION_ID ]]; then + if [[ $VS_VER == 2022 ]]; then + "$TOOL" shutdown $SESSION_ID + echo "Coverage collection log" + echo "=======================" + cat extensions.log + else + "$TOOL" shutdown -session:$SESSION_ID + fi + fi + displayName: 'pytest' - - bash: | - bash <(curl -s https://codecov.io/bash) -f "!*.gcov" -X gcov - displayName: 'Upload to codecov.io' + - bash: | + case "$AGENT_OS" in + Linux) + lcov --rc lcov_branch_coverage=1 --capture --directory . \ + --output-file coverage.info + lcov --rc lcov_branch_coverage=1 --output-file coverage.info \ + --extract coverage.info $PWD/src/'*' $PWD/lib/'*' + lcov --rc lcov_branch_coverage=1 --list coverage.info + find . -name '*.gc*' -delete + ;; + Darwin) + xcrun llvm-profdata merge -sparse default.*.profraw \ + -o default.profdata + xcrun llvm-cov export -format="lcov" build/*/src/*.so \ + -instr-profile default.profdata > info.lcov + ;; + Windows_NT) + if [[ -f extensions.coverage ]]; then + # For MSVC 2019. + "$VS_COVERAGE_TOOL" analyze -output:extensions.xml \ + -include_skipped_functions -include_skipped_modules \ + extensions.coverage + rm extensions.coverage + fi + ;; + *) + exit 1 + ;; + esac + displayName: 'Filter C coverage' + condition: succeededOrFailed() + - bash: | + bash <(curl -s https://codecov.io/bash) \ + -n "$PYTHON_VERSION $AGENT_OS" \ + -f 'coverage.xml' -f 'extensions.xml' + displayName: 'Upload to codecov.io' + condition: succeededOrFailed() - - task: PublishTestResults@2 - inputs: - testResultsFiles: '**/test-results.xml' - testRunTitle: 'Python $(python.version)' - condition: succeededOrFailed() + - task: PublishTestResults@2 + inputs: + testResultsFiles: '**/test-results.xml' + testRunTitle: 'Python $(python.version)' + condition: succeededOrFailed() - - publish: $(System.DefaultWorkingDirectory)/result_images - artifact: $(Agent.JobName)-result_images - condition: and(failed(), ne(variables['python.version'], 'Pre')) + - publish: $(System.DefaultWorkingDirectory)/result_images + artifact: $(Agent.JobName)-result_images + condition: failed() diff --git a/ci/codespell-ignore-words.txt b/ci/codespell-ignore-words.txt index 1a29d054cdc3..acbb2e8f39b5 100644 --- a/ci/codespell-ignore-words.txt +++ b/ci/codespell-ignore-words.txt @@ -1,23 +1,17 @@ aas -ans axises -ba -cannotation -ccompiler coo curvelinear -dedented -falsy +filll flate fpt hax -hist inh inout ment nd +oint oly -resizeable te thisy whis diff --git a/ci/mypy-stubtest-allowlist.txt b/ci/mypy-stubtest-allowlist.txt index 778494446d38..73dfb1d8ceb0 100644 --- a/ci/mypy-stubtest-allowlist.txt +++ b/ci/mypy-stubtest-allowlist.txt @@ -18,8 +18,6 @@ matplotlib.pyplot.* matplotlib.typing.* # Other decorator modifying signature -# Runtime picks up *args **kwargs, but only decorated by a decorator that uses @wraps so? -matplotlib.axis.Axis.draw # Backcompat decorator which does not modify runtime reported signature matplotlib.offsetbox.*Offset[Bb]ox.get_offset @@ -37,103 +35,9 @@ matplotlib.figure.Figure.set_constrained_layout matplotlib.figure.Figure.set_constrained_layout_pads matplotlib.figure.Figure.set_tight_layout -# 3.7 deprecations -matplotlib.cm.register_cmap -matplotlib.cm.unregister_cmap -matplotlib.collections.PolyCollection.span_where -matplotlib.gridspec.GridSpecBase.get_grid_positions -matplotlib.widgets.MultiCursor.needclear - -# 3.8 deprecations -matplotlib.cbook.get_sample_data -matplotlib.ticker.LogLocator.__init__ -matplotlib.ticker.LogLocator.set_params - # positional-only argument name lacking leading underscores matplotlib.axes._base._AxesBase.axis -# Aliases (dynamically generated, not type hinted) -matplotlib.collections.Collection.get_aa -matplotlib.collections.Collection.get_antialiaseds -matplotlib.collections.Collection.get_dashes -matplotlib.collections.Collection.get_ec -matplotlib.collections.Collection.get_edgecolors -matplotlib.collections.Collection.get_facecolors -matplotlib.collections.Collection.get_fc -matplotlib.collections.Collection.get_linestyles -matplotlib.collections.Collection.get_linewidths -matplotlib.collections.Collection.get_ls -matplotlib.collections.Collection.get_lw -matplotlib.collections.Collection.get_transOffset -matplotlib.collections.Collection.set_aa -matplotlib.collections.Collection.set_antialiaseds -matplotlib.collections.Collection.set_dashes -matplotlib.collections.Collection.set_ec -matplotlib.collections.Collection.set_edgecolors -matplotlib.collections.Collection.set_facecolors -matplotlib.collections.Collection.set_fc -matplotlib.collections.Collection.set_linestyles -matplotlib.collections.Collection.set_linewidths -matplotlib.collections.Collection.set_ls -matplotlib.collections.Collection.set_lw -matplotlib.collections.Collection.set_transOffset -matplotlib.lines.Line2D.get_aa -matplotlib.lines.Line2D.get_c -matplotlib.lines.Line2D.get_ds -matplotlib.lines.Line2D.get_ls -matplotlib.lines.Line2D.get_lw -matplotlib.lines.Line2D.get_mec -matplotlib.lines.Line2D.get_mew -matplotlib.lines.Line2D.get_mfc -matplotlib.lines.Line2D.get_mfcalt -matplotlib.lines.Line2D.get_ms -matplotlib.lines.Line2D.set_aa -matplotlib.lines.Line2D.set_c -matplotlib.lines.Line2D.set_ds -matplotlib.lines.Line2D.set_ls -matplotlib.lines.Line2D.set_lw -matplotlib.lines.Line2D.set_mec -matplotlib.lines.Line2D.set_mew -matplotlib.lines.Line2D.set_mfc -matplotlib.lines.Line2D.set_mfcalt -matplotlib.lines.Line2D.set_ms -matplotlib.patches.Patch.get_aa -matplotlib.patches.Patch.get_ec -matplotlib.patches.Patch.get_fc -matplotlib.patches.Patch.get_ls -matplotlib.patches.Patch.get_lw -matplotlib.patches.Patch.set_aa -matplotlib.patches.Patch.set_ec -matplotlib.patches.Patch.set_fc -matplotlib.patches.Patch.set_ls -matplotlib.patches.Patch.set_lw -matplotlib.text.Text.get_c -matplotlib.text.Text.get_family -matplotlib.text.Text.get_font -matplotlib.text.Text.get_font_properties -matplotlib.text.Text.get_ha -matplotlib.text.Text.get_name -matplotlib.text.Text.get_size -matplotlib.text.Text.get_style -matplotlib.text.Text.get_va -matplotlib.text.Text.get_variant -matplotlib.text.Text.get_weight -matplotlib.text.Text.set_c -matplotlib.text.Text.set_family -matplotlib.text.Text.set_font -matplotlib.text.Text.set_font_properties -matplotlib.text.Text.set_ha -matplotlib.text.Text.set_ma -matplotlib.text.Text.set_name -matplotlib.text.Text.set_size -matplotlib.text.Text.set_stretch -matplotlib.text.Text.set_style -matplotlib.text.Text.set_va -matplotlib.text.Text.set_variant -matplotlib.text.Text.set_weight -matplotlib.axes._base._AxesBase.get_fc -matplotlib.axes._base._AxesBase.set_fc - # Maybe should be abstractmethods, required for subclasses, stubs define once matplotlib.tri.*TriInterpolator.__call__ matplotlib.tri.*TriInterpolator.gradient diff --git a/ci/schemas/README.md b/ci/schemas/README.md new file mode 100644 index 000000000000..087fd31d2ab8 --- /dev/null +++ b/ci/schemas/README.md @@ -0,0 +1,5 @@ +YAML Schemas for linting and validation +======================================= + +Since pre-commit CI doesn't have Internet access, we need to bundle these files +in the repo. The schemas can be updated using `vendor_schemas.py`. diff --git a/ci/schemas/appveyor.json b/ci/schemas/appveyor.json new file mode 100644 index 000000000000..d19a10f23b75 --- /dev/null +++ b/ci/schemas/appveyor.json @@ -0,0 +1,781 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "allOf": [ + { + "$ref": "#/definitions/job" + } + ], + "definitions": { + "possiblySecretString": { + "anyOf": [ + { + "type": "string", + "description": "This value will be used directly (regular string)" + }, + { + "type": "number", + "description": "This value will be treated as a string even though it is a number" + }, + { + "title": "secret string", + "type": "object", + "additionalProperties": false, + "properties": { + "secure": { + "type": "string", + "description": "This should have been encrypted by the same user account to which the project belongs" + } + } + } + ] + }, + "commitFilter": { + "title": "commit filter", + "type": "object", + "additionalProperties": false, + "properties": { + "message": { + "type": "string", + "format": "regex", + "description": "Regex for matching commit message" + }, + "author": { + "description": "Commit author's username, name, email or regexp matching one of these.", + "anyOf": [ + { + "type": "string", + "format": "regex" + }, + { + "type": "string" + } + ] + }, + "files": { + "type": "array", + "description": "Only specific files (glob patterns)", + "items": { + "type": "string" + } + } + } + }, + "command": { + "title": "command", + "oneOf": [ + { + "type": "string", + "description": "Run a batch command" + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "ps": { + "type": "string", + "description": "Run a PowerShell command" + } + } + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "pwsh": { + "type": "string", + "description": "Run a PowerShell Core command" + } + } + }, + { + "type": "object", + "description": "Run a batch command", + "additionalProperties": false, + "properties": { + "cmd": { + "type": "string" + } + } + }, + { + "type": "object", + "description": "Run a Bash command", + "additionalProperties": false, + "properties": { + "sh": { + "type": "string" + } + } + } + ] + }, + "envVarHash": { + "title": "environment variable hash", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/possiblySecretString" + } + }, + "platform": { + "enum": ["x86", "x64", "ARM", "ARM64", "Win32", "Any CPU"] + }, + "configuration": { + "type": "string" + }, + "imageName": { + "enum": [ + "macOS", + "macOS-Mojave", + "macos-bigsur", + "macos-monterey", + "Previous macOS", + "Previous macOS-Mojave", + "Ubuntu", + "Ubuntu1604", + "Ubuntu1804", + "Ubuntu2004", + "Ubuntu2204", + "Previous Ubuntu", + "Previous Ubuntu1604", + "Previous Ubuntu1804", + "Previous Ubuntu2004", + "Visual Studio 2013", + "Visual Studio 2015", + "Visual Studio 2017", + "Visual Studio 2019", + "Visual Studio 2022", + "Visual Studio 2017 Preview", + "Visual Studio 2019 Preview", + "Previous Visual Studio 2013", + "Previous Visual Studio 2015", + "Previous Visual Studio 2017", + "Previous Visual Studio 2019", + "Previous Visual Studio 2022", + "zhaw18", + "WMF 5" + ] + }, + "image": { + "description": "Build worker image (VM template) -DEV_VERSION", + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/definitions/imageName" + } + }, + { + "$ref": "#/definitions/imageName" + } + ] + }, + "jobScalars": { + "title": "job scalars", + "type": "object", + "properties": { + "image": { + "$ref": "#/definitions/image" + }, + "platform": { + "description": "Build platform, i.e. x86, x64, Any CPU. This setting is optional", + "oneOf": [ + { + "$ref": "#/definitions/platform" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/platform" + } + } + ] + }, + "configuration": { + "description": "Build Configuration, i.e. Debug, Release, etc.", + "oneOf": [ + { + "$ref": "#/definitions/configuration" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/configuration" + } + } + ] + } + }, + "allOf": [ + { + "not": { + "required": ["skip_tags"] + } + }, + { + "not": { + "required": ["skip_commits"] + } + }, + { + "not": { + "required": ["skip_branch_with_pr"] + } + }, + { + "not": { + "required": ["skip_non_tags"] + } + } + ] + }, + "job": { + "title": "job", + "type": "object", + "properties": { + "version": { + "description": "Version format", + "type": "string" + }, + "branches": { + "title": "branch options", + "type": "object", + "description": "Branches to build", + "additionalProperties": false, + "properties": { + "only": { + "description": "Whitelist", + "type": "array", + "items": { + "type": "string" + } + }, + "except": { + "type": "array", + "description": "Blacklist", + "items": { + "type": "string" + } + } + } + }, + "skip_tags": { + "type": "boolean", + "description": "Do not build on tags (GitHub and BitBucket)" + }, + "skip_non_tags": { + "type": "boolean", + "description": "Start builds on tags only (GitHub and BitBucket)" + }, + "skip_commits": { + "$ref": "#/definitions/commitFilter", + "description": "Skipping commits with particular message or from specific user" + }, + "only_commits": { + "$ref": "#/definitions/commitFilter", + "description": "Including commits with particular message or from specific user" + }, + "skip_branch_with_pr": { + "type": "boolean", + "description": "Do not build feature branch with open Pull Requests" + }, + "max_jobs": { + "description": "Maximum number of concurrent jobs for the project", + "type": "integer" + }, + "notifications": { + "type": "array", + "items": { + "title": "notification", + "type": "object" + } + }, + "image": { + "$ref": "#/definitions/image" + }, + "init": { + "description": "Scripts that are called at very beginning, before repo cloning", + "type": "array", + "items": { + "$ref": "#/definitions/command" + } + }, + "clone_folder": { + "type": "string", + "description": "Clone directory" + }, + "shallow_clone": { + "type": "boolean", + "description": "Fetch repository as zip archive", + "default": false + }, + "clone_depth": { + "description": "Set git clone depth", + "type": "integer" + }, + "hosts": { + "title": "host options", + "type": "object", + "description": "Setting up etc\\hosts file", + "additionalProperties": { + "type": "string", + "anyOf": [ + { + "format": "ipv4" + }, + { + "format": "ipv6" + } + ] + } + }, + "environment": { + "description": "Environment variables", + "anyOf": [ + { + "title": "environment options", + "type": "object", + "properties": { + "global": { + "$ref": "#/definitions/envVarHash", + "description": "variables defined here are no different than those defined at top level of 'environment' node" + }, + "matrix": { + "type": "array", + "description": "an array of environment variables, each member of which is one dimension in the build matrix calculation", + "items": { + "$ref": "#/definitions/envVarHash" + } + } + } + }, + { + "$ref": "#/definitions/envVarHash" + } + ] + }, + "matrix": { + "title": "matrix options", + "type": "object", + "additionalProperties": false, + "properties": { + "fast_finish": { + "type": "boolean", + "description": "Set this flag to immediately finish build once one of the jobs fails" + }, + "allow_failures": { + "type": "array", + "description": "This is how to allow failing jobs in the matrix", + "items": { + "$ref": "#/definitions/jobScalars" + } + }, + "exclude": { + "type": "array", + "description": "Exclude configuration from the matrix. Works similarly to 'allow_failures' but build not even being started for excluded combination.", + "items": { + "$ref": "#/definitions/job" + } + } + } + }, + "cache": { + "type": "array", + "description": "Build cache to preserve files/folders between builds", + "items": { + "type": "string" + } + }, + "services": { + "type": "array", + "description": "Enable service required for build/tests", + "items": { + "enum": [ + "docker", + "iis", + "mongodb", + "msmq", + "mssql", + "mssql2008r2sp2", + "mssql2008r2sp2rs", + "mssql2012sp1", + "mssql2012sp1rs", + "mssql2014", + "mssql2014rs", + "mssql2016", + "mssql2017", + "mysql", + "postgresql", + "postgresql93", + "postgresql94", + "postgresql95", + "postgresql96", + "postgresql10", + "postgresql11", + "postgresql12", + "postgresql13" + ] + } + }, + "install": { + "description": "Scripts that run after cloning repository", + "type": "array", + "items": { + "$ref": "#/definitions/command" + } + }, + "assembly_info": { + "title": "assembly options", + "type": "object", + "description": "Enable patching of AssemblyInfo.* files", + "additionalProperties": false, + "properties": { + "patch": { + "type": "boolean" + }, + "file": { + "type": "string" + }, + "assembly_version": { + "type": "string" + }, + "assembly_file_version": { + "type": "string" + }, + "assembly_informational_version": { + "type": "string" + } + } + }, + "nuget": { + "title": "NuGet options", + "type": "object", + "description": "Automatically register private account and/or project AppVeyor NuGet feeds", + "properties": { + "account_feed": { + "type": "boolean" + }, + "project_feed": { + "type": "boolean" + }, + "disable_publish_on_pr": { + "type": "boolean", + "description": "Disable publishing of .nupkg artifacts to account/project feeds for pull request builds" + } + } + }, + "platform": { + "description": "Build platform, i.e. x86, x64, Any CPU. This setting is optional", + "oneOf": [ + { + "$ref": "#/definitions/platform" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/platform" + } + } + ] + }, + "configuration": { + "description": "Build Configuration, i.e. Debug, Release, etc.", + "oneOf": [ + { + "$ref": "#/definitions/configuration" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/configuration" + } + } + ] + }, + "build": { + "oneOf": [ + { + "type": "boolean", + "enum": [false] + }, + { + "title": "build options", + "type": "object", + "additionalProperties": false, + "properties": { + "parallel": { + "type": "boolean", + "description": "Enable MSBuild parallel builds" + }, + "project": { + "type": "string", + "description": "Path to Visual Studio solution or project" + }, + "publish_wap": { + "type": "boolean", + "description": "Package Web Application Projects (WAP) for Web Deploy" + }, + "publish_wap_xcopy": { + "type": "boolean", + "description": "Package Web Application Projects (WAP) for XCopy deployment" + }, + "publish_wap_beanstalk": { + "type": "boolean", + "description": "Package Web Applications for AWS Elastic Beanstalk deployment" + }, + "publish_wap_octopus": { + "type": "boolean", + "description": "Package Web Applications for Octopus deployment" + }, + "publish_azure_webjob": { + "type": "boolean", + "description": "Package Azure WebJobs for Zip Push deployment" + }, + "publish_azure": { + "type": "boolean", + "description": "Package Azure Cloud Service projects and push to artifacts" + }, + "publish_aspnet_core": { + "type": "boolean", + "description": "Package ASP.NET Core projects" + }, + "publish_core_console": { + "type": "boolean", + "description": "Package .NET Core console projects" + }, + "publish_nuget": { + "type": "boolean", + "description": "Package projects with .nuspec files and push to artifacts" + }, + "publish_nuget_symbols": { + "type": "boolean", + "description": "Generate and publish NuGet symbol packages" + }, + "include_nuget_references": { + "type": "boolean", + "description": "Add -IncludeReferencedProjects option while packaging NuGet artifacts" + }, + "verbosity": { + "enum": ["quiet", "minimal", "normal", "detailed"], + "description": "MSBuild verbosity level" + } + } + } + ] + }, + "before_build": { + "description": "Scripts to run before build", + "type": "array", + "items": { + "$ref": "#/definitions/command" + } + }, + "before_package": { + "description": "Scripts to run *after* solution is built and *before* automatic packaging occurs (web apps, NuGet packages, Azure Cloud Services)", + "type": "array", + "items": { + "$ref": "#/definitions/command" + } + }, + "after_build": { + "description": "Scripts to run after build", + "type": "array", + "items": { + "$ref": "#/definitions/command" + } + }, + "build_script": { + "description": "To run your custom scripts instead of automatic MSBuild", + "type": "array", + "items": { + "$ref": "#/definitions/command" + } + }, + "before_test": { + "description": "Scripts to run before tests", + "type": "array", + "items": { + "$ref": "#/definitions/command" + } + }, + "test": { + "oneOf": [ + { + "type": "boolean", + "enum": [false], + "description": "To disable automatic tests" + }, + { + "title": "test options", + "description": "To run tests again only selected assemblies and/or categories", + "type": "object", + "additionalProperties": false, + "properties": { + "assemblies": { + "title": "assembly options", + "type": "object", + "additionalProperties": false, + "properties": { + "only": { + "type": "array", + "items": { + "type": "string" + } + }, + "except": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "categories": { + "oneOf": [ + { + "title": "category options", + "type": "object", + "additionalProperties": false, + "properties": { + "only": { + "type": "array", + "items": { + "type": "string" + } + }, + "except": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + { + "description": "To run tests from different categories as separate jobs in parallel", + "type": "array", + "items": { + "oneOf": [ + { + "type": "string", + "description": "A category common for all jobs" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + ] + } + } + } + ] + }, + "test_script": { + "description": "To run your custom scripts instead of automatic tests", + "type": "array", + "items": { + "$ref": "#/definitions/command" + } + }, + "after_test": { + "type": "array", + "description": "Scripts to run after tests", + "items": { + "$ref": "#/definitions/command" + } + }, + "artifacts": { + "type": "array", + "items": { + "title": "artifact options", + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "enum": [ + "Auto", + "WebDeployPackage", + "NuGetPackage", + "AzureCloudService", + "AzureCloudServiceConfig", + "SsdtPackage", + "Zip", + "File" + ] + } + }, + "required": ["path"] + } + }, + "before_deploy": { + "type": "array", + "description": "Scripts to run before deployment", + "items": { + "$ref": "#/definitions/command" + } + }, + "deploy": { + "oneOf": [ + { + "enum": ["off"] + }, + { + "type": "array", + "items": { + "title": "deployment options", + "type": "object" + } + } + ] + }, + "deploy_script": { + "description": "To run your custom scripts instead of provider deployments", + "type": "array", + "items": { + "$ref": "#/definitions/command" + } + }, + "after_deploy": { + "type": "array", + "description": "Scripts to run after deployment", + "items": { + "$ref": "#/definitions/command" + } + }, + "on_success": { + "type": "array", + "description": "On successful build", + "items": { + "$ref": "#/definitions/command" + } + }, + "on_failure": { + "type": "array", + "description": "On build failure", + "items": { + "$ref": "#/definitions/command" + } + }, + "on_finish": { + "type": "array", + "description": "After build failure or success", + "items": { + "$ref": "#/definitions/command" + } + } + } + } + }, + "id": "https://json.schemastore.org/appveyor.json", + "title": "JSON schema for AppVeyor CI configuration files" +} diff --git a/ci/schemas/circleciconfig.json b/ci/schemas/circleciconfig.json new file mode 100644 index 000000000000..076944098440 --- /dev/null +++ b/ci/schemas/circleciconfig.json @@ -0,0 +1,1411 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/circleciconfig.json", + "definitions": { + "logical": { + "description": "https://circleci.com/docs/configuration-reference#logic-statements \n\nA logical statement to be used in dynamic configuration", + "oneOf": [ + { + "type": ["string", "boolean", "integer", "number"] + }, + { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "maxProperties": 1, + "properties": { + "and": { + "description": "https://circleci.com/docs/configuration-reference#logic-statements \n\nLogical and: true when all statements in the list are true", + "type": "array", + "items": { + "$ref": "#/definitions/logical" + } + }, + "or": { + "description": "https://circleci.com/docs/configuration-reference#logic-statements \n\nLogical or: true when at least one statements in the list is true", + "type": "array", + "items": { + "$ref": "#/definitions/logical" + } + }, + "not": { + "$ref": "#/definitions/logical", + "description": "https://circleci.com/docs/configuration-reference#logic-statements \n\nLogical not: true when statement is false" + }, + "equal": { + "description": "https://circleci.com/docs/configuration-reference#logic-statements \n\nTrue when all elements in the list are equal", + "type": "array" + }, + "matches": { + "description": "https://circleci.com/docs/configuration-reference#logic-statements \n\nTrue when value matches the pattern", + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "value": { + "type": "string" + } + } + } + } + } + ] + }, + "filter": { + "description": "A map defining rules for execution on specific branches", + "type": "object", + "additionalProperties": false, + "properties": { + "only": { + "description": "Either a single branch specifier, or a list of branch specifiers", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "ignore": { + "description": "Either a single branch specifier, or a list of branch specifiers", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + }, + "orbs": { + "description": "https://circleci.com/docs/configuration-reference#orbs-requires-version-21\n\nOrbs are reusable packages of CircleCI configuration that you may share across projects, enabling you to create encapsulated, parameterized commands, jobs, and executors that can be used across multiple projects.", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "description": "https://circleci.com/docs/creating-orbs#semantic-versioning-in-orbs\n\nAn orb to depend on and its semver range, or volatile for the most recent release.", + "type": "string", + "pattern": "^[a-z][a-z0-9_-]+/[a-z][a-z0-9_-]+@(dev:[\\.a-z0-9_-]+|\\d+|\\d+\\.\\d+|\\d+\\.\\d+\\.\\d+|volatile)$" + }, + { + "description": "https://circleci.com/docs/creating-orbs#creating-inline-orbs\n\nInline orbs can be handy during development of an orb or as a convenience for name-spacing jobs and commands in lengthy configurations, particularly if you later intend to share the orb with others.", + "type": "object", + "properties": { + "orbs": { + "$ref": "#/definitions/orbs" + }, + "commands": { + "$ref": "#/definitions/commands" + }, + "executors": { + "$ref": "#/definitions/executors" + }, + "jobs": { + "$ref": "#/definitions/jobs" + } + } + } + ] + } + }, + "commands": { + "description": "https://circleci.com/docs/configuration-reference#commands-requires-version-21\n\nA command definition defines a sequence of steps as a map to be executed in a job, enabling you to reuse a single command definition across multiple jobs.", + "type": "object", + "additionalProperties": { + "description": "https://circleci.com/docs/configuration-reference#commands-requires-version-21\n\nDefinition of a custom command.", + "type": "object", + "required": ["steps"], + "properties": { + "steps": { + "description": "A sequence of steps run inside the calling job of the command.", + "type": "array", + "items": { + "$ref": "#/definitions/step" + } + }, + "parameters": { + "description": "https://circleci.com/docs/reusing-config#using-the-parameters-declaration\n\nA map of parameter keys.", + "type": "object", + "patternProperties": { + "^[a-z][a-z0-9_-]+$": { + "oneOf": [ + { + "description": "https://circleci.com/docs/reusing-config#string\n\nA string parameter.", + "type": "object", + "required": ["type"], + "properties": { + "type": { + "enum": ["string"] + }, + "description": { + "type": "string" + }, + "default": { + "type": "string" + } + } + }, + { + "description": "https://circleci.com/docs/reusing-config#boolean\n\nA boolean parameter.", + "type": "object", + "required": ["type"], + "properties": { + "type": { + "enum": ["boolean"] + }, + "description": { + "type": "string" + }, + "default": { + "type": "boolean" + } + } + }, + { + "description": "https://circleci.com/docs/reusing-config#integer\n\nAn integer parameter.", + "type": "object", + "required": ["type"], + "properties": { + "type": { + "enum": ["integer"] + }, + "description": { + "type": "string" + }, + "default": { + "type": "integer" + } + } + }, + { + "description": "https://circleci.com/docs/reusing-config#enum\n\nThe `enum` parameter may be a list of any values. Use the `enum` parameter type when you want to enforce that the value must be one from a specific set of string values.", + "type": "object", + "required": ["type", "enum"], + "properties": { + "type": { + "enum": ["enum"] + }, + "enum": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "description": { + "type": "string" + }, + "default": { + "type": "string" + } + } + }, + { + "description": "https://circleci.com/docs/reusing-config#executor\n\nUse an `executor` parameter type to allow the invoker of a job to decide what executor it will run on.", + "type": "object", + "required": ["type"], + "properties": { + "type": { + "enum": ["executor"] + }, + "description": { + "type": "string" + }, + "default": { + "type": "string" + } + } + }, + { + "description": "https://circleci.com/docs/reusing-config#steps\n\nSteps are used when you have a job or command that needs to mix predefined and user-defined steps. When passed in to a command or job invocation, the steps passed as parameters are always defined as a sequence, even if only one step is provided.", + "type": "object", + "required": ["type"], + "properties": { + "type": { + "enum": ["steps"] + }, + "description": { + "type": "string" + }, + "default": { + "type": "array", + "items": { + "$ref": "#/definitions/step" + } + } + } + }, + { + "description": "https://circleci.com/docs/reusing-config#environment-variable-name\n\nThe environment variable name parameter is a string that must match a POSIX_NAME regexp (e.g. no spaces or special characters) and is a more meaningful parameter type that enables additional checks to be performed. ", + "type": "object", + "required": ["type"], + "properties": { + "type": { + "enum": ["env_var_name"] + }, + "description": { + "type": "string" + }, + "default": { + "type": "string", + "pattern": "^[a-zA-Z][a-zA-Z0-9_-]+$" + } + } + } + ] + } + } + }, + "description": { + "description": "A string that describes the purpose of the command.", + "type": "string" + } + } + } + }, + "dockerLayerCaching": { + "description": "Set to `true` to enable [Docker Layer Caching](https://circleci.com/docs/docker-layer-caching). Note: If you haven't already, you must open a support ticket to have a CircleCI Sales representative contact you about enabling this feature on your account for an additional fee.", + "type": "boolean", + "default": "true" + }, + "dockerExecutor": { + "description": "Options for the [docker executor](https://circleci.com/docs/configuration-reference#docker)", + "required": ["docker"], + "properties": { + "docker": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["image"], + "properties": { + "image": { + "description": "The name of a custom docker image to use", + "type": "string" + }, + "name": { + "description": "The name the container is reachable by. By default, container services are accessible through `localhost`", + "type": "string" + }, + "entrypoint": { + "description": "The command used as executable when launching the container", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "command": { + "description": "The command used as pid 1 (or args for entrypoint) when launching the container", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "user": { + "description": "Which user to run the command as", + "type": "string" + }, + "environment": { + "description": "A map of environment variable names and values", + "type": "object", + "additionalProperties": { + "type": ["string", "number", "boolean"] + } + }, + "auth": { + "description": "Authentication for registries using standard `docker login` credentials", + "type": "object", + "additionalProperties": false, + "properties": { + "username": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "aws_auth": { + "description": "Authentication for AWS EC2 Container Registry (ECR). You can use the access/secret keys or OIDC.", + "type": "object", + "additionalProperties": false, + "properties": { + "aws_access_key_id": { + "type": "string" + }, + "aws_secret_access_key": { + "type": "string" + }, + "oidc_role_arn": { + "type": "string" + } + } + } + } + } + }, + "resource_class": { + "description": "Amount of CPU and RAM allocated for each job. Note: A performance plan is required to access this feature.", + "type": "string", + "enum": [ + "small", + "medium", + "medium+", + "large", + "xlarge", + "2xlarge", + "2xlarge+", + "arm.medium", + "arm.large", + "arm.xlarge", + "arm.2xlarge" + ] + } + } + }, + "machineExecutor": { + "description": "Options for the [machine executor](https://circleci.com/docs/configuration-reference#machine)", + "type": "object", + "required": ["machine"], + "oneOf": [ + { + "properties": { + "machine": { + "oneOf": [ + { "const": "default" }, + { "const": true }, + { + "type": "object", + "additionalProperties": false, + "required": ["image"], + "properties": { + "image": { + "description": "The VM image to use. View [available images](https://circleci.com/docs/configuration-reference/#available-linux-machine-images-cloud). **Note:** This key is **not** supported on the installable CircleCI. For information about customizing machine executor images on CircleCI installed on your servers, see our [VM Service documentation](https://circleci.com/docs/vm-service).", + "type": "string", + "enum": [ + "ubuntu-2004:2023.10.1", + "ubuntu-2004:2023.07.1", + "ubuntu-2004:2023.04.2", + "ubuntu-2004:2023.04.1", + "ubuntu-2004:2023.02.1", + "ubuntu-2004:2022.10.1", + "ubuntu-2004:2022.07.1", + "ubuntu-2004:2022.04.2", + "ubuntu-2004:2022.04.1", + "ubuntu-2004:202201-02", + "ubuntu-2004:202201-01", + "ubuntu-2004:202111-02", + "ubuntu-2004:202111-01", + "ubuntu-2004:202107-02", + "ubuntu-2004:202104-01", + "ubuntu-2004:202101-01", + "ubuntu-2004:202010-01", + "ubuntu-2004:current", + "ubuntu-2004:edge", + "ubuntu-2204:2023.10.1", + "ubuntu-2204:2023.07.2", + "ubuntu-2204:2023.04.2", + "ubuntu-2204:2023.04.1", + "ubuntu-2204:2023.02.1", + "ubuntu-2204:2022.10.2", + "ubuntu-2204:2022.10.1", + "ubuntu-2204:2022.07.2", + "ubuntu-2204:2022.07.1", + "ubuntu-2204:2022.04.2", + "ubuntu-2204:2022.04.1", + "ubuntu-2204:current", + "ubuntu-2204:edge", + "android:2023.11.1", + "android:2023.10.1", + "android:2023.09.1", + "android:2023.08.1", + "android:2023.07.1", + "android:2023.06.1", + "android:2023.05.1", + "android:2023.04.1", + "android:2023.03.1", + "android:2023.02.1", + "android:2022.12.1", + "android:2022.09.1", + "android:2022.08.1", + "android:2022.07.1", + "android:2022.06.2", + "android:2022.06.1", + "android:2022.04.1", + "android:2022.03.1", + "android:2022.01.1", + "android:2021.12.1", + "android:2021.10.1", + "android:202102-01" + ] + }, + "docker_layer_caching": { + "$ref": "#/definitions/dockerLayerCaching" + } + } + } + ] + }, + "resource_class": { + "description": "Amount of CPU and RAM allocated for each job. View [available resource classes](https://circleci.com/docs/configuration-reference/#linuxvm-execution-environment)", + "type": "string", + "enum": [ + "medium", + "large", + "xlarge", + "2xlarge", + "2xlarge+", + "arm.medium", + "arm.large", + "arm.xlarge", + "arm.2xlarge" + ] + } + } + }, + { + "properties": { + "machine": { + "type": "object", + "additionalProperties": false, + "required": ["image"], + "properties": { + "image": { + "description": "The VM image to use. View [available images](https://circleci.com/docs/configuration-reference/#available-linux-gpu-images). **Note:** This key is **not** supported on the installable CircleCI. For information about customizing machine executor images on CircleCI installed on your servers, see our [VM Service documentation](https://circleci.com/docs/vm-service).", + "type": "string", + "enum": ["linux-cuda-11:default", "linux-cuda-12:default"] + }, + "docker_layer_caching": { + "$ref": "#/definitions/dockerLayerCaching" + } + } + }, + "resource_class": { + "description": "Amount of CPU and RAM allocated for each job. View [available resource classes](https://circleci.com/docs/configuration-reference/#gpu-execution-environment-linux)", + "type": "string", + "enum": ["gpu.nvidia.medium", "gpu.nvidia.large"] + } + } + }, + { + "properties": { + "machine": { + "type": "object", + "additionalProperties": false, + "required": ["image"], + "properties": { + "image": { + "description": "The VM image to use. View [available images](https://circleci.com/docs/configuration-reference/#available-windows-machine-images-cloud). **Note:** This key is **not** supported on the installable CircleCI. For information about customizing machine executor images on CircleCI installed on your servers, see our [VM Service documentation](https://circleci.com/docs/vm-service).", + "type": "string", + "enum": [ + "windows-server-2022-gui:2023.10.1", + "windows-server-2022-gui:2023.09.1", + "windows-server-2022-gui:2023.08.1", + "windows-server-2022-gui:2023.07.1", + "windows-server-2022-gui:2023.06.1", + "windows-server-2022-gui:2023.05.1", + "windows-server-2022-gui:2023.04.1", + "windows-server-2022-gui:2023.03.1", + "windows-server-2022-gui:2022.08.1", + "windows-server-2022-gui:2022.07.1", + "windows-server-2022-gui:2022.06.1", + "windows-server-2022-gui:2022.04.1", + "windows-server-2022-gui:current", + "windows-server-2022-gui:edge", + "windows-server-2019:2023.10.1", + "windows-server-2019:2023.08.1", + "windows-server-2019:2023.04.1", + "windows-server-2019:2022.08.1", + "windows-server-2019:current", + "windows-server-2019:edge" + ] + }, + "docker_layer_caching": { + "$ref": "#/definitions/dockerLayerCaching" + } + } + }, + "resource_class": { + "description": "Amount of CPU and RAM allocated for each job. View [available resource classes](https://circleci.com/docs/configuration-reference/#windows-execution-environment)", + "type": "string", + "enum": [ + "windows.medium", + "windows.large", + "windows.xlarge", + "windows.2xlarge" + ] + } + } + }, + { + "properties": { + "machine": { + "type": "object", + "additionalProperties": false, + "required": ["image"], + "properties": { + "image": { + "description": "The VM image to use. View [available images](https://circleci.com/docs/configuration-reference/#available-windows-gpu-image). **Note:** This key is **not** supported on the installable CircleCI. For information about customizing machine executor images on CircleCI installed on your servers, see our [VM Service documentation](https://circleci.com/docs/vm-service).", + "type": "string", + "enum": [ + "windows-server-2019-cuda:current", + "windows-server-2019-cuda:edge" + ] + }, + "docker_layer_caching": { + "$ref": "#/definitions/dockerLayerCaching" + } + } + }, + "resource_class": { + "description": "Amount of CPU and RAM allocated for each job. View [available resource classes](https://circleci.com/docs/configuration-reference/#gpu-execution-environment-windows)", + "type": "string", + "enum": ["windows.gpu.nvidia.medium"] + } + } + } + ] + }, + "macosExecutor": { + "description": "Options for the [macOS executor](https://circleci.com/docs/configuration-reference#macos)", + "type": "object", + "required": ["macos"], + "properties": { + "macos": { + "type": "object", + "additionalProperties": false, + "required": ["xcode"], + "properties": { + "xcode": { + "description": "The version of Xcode that is installed on the virtual machine, see the [Supported Xcode Versions section of the Testing iOS](https://circleci.com/docs/testing-ios#supported-xcode-versions) document for the complete list.", + "type": "string", + "enum": [ + "15.2.0", + "15.1.0", + "15.0.0", + "14.3.1", + "14.2.0", + "14.1.0", + "14.0.1", + "13.4.1", + "12.5.1" + ] + } + } + }, + "resource_class": { + "description": "Amount of CPU and RAM allocated for each job. View [available resource classes](https://circleci.com/docs/configuration-reference/#macos-execution-environment)", + "type": "string", + "enum": [ + "macos.x86.medium.gen2", + "macos.m1.medium.gen1", + "macos.m1.large.gen1" + ] + } + } + }, + "executorChoice": { + "type": "object", + "oneOf": [ + { + "$ref": "#/definitions/dockerExecutor" + }, + { + "$ref": "#/definitions/machineExecutor" + }, + { + "$ref": "#/definitions/macosExecutor" + } + ] + }, + "executors": { + "description": "Executors define the environment in which the steps of a job will be run, allowing you to reuse a single executor definition across multiple jobs.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/executorChoice", + "type": "object", + "properties": { + "shell": { + "description": "Shell to use for execution command in all steps. Can be overridden by shell in each step (default: See [Default Shell Options](https://circleci.com/docs/configuration-reference#default-shell-options)", + "type": "string" + }, + "working_directory": { + "description": "In which directory to run the steps.", + "type": "string" + }, + "environment": { + "description": "A map of environment variable names and values.", + "type": "object", + "additionalProperties": { + "type": ["string", "number"] + } + } + } + } + }, + "builtinSteps": { + "documentation": { + "run": { + "description": "https://circleci.com/docs/configuration-reference#run\n\nUsed for invoking all command-line programs, taking either a map of configuration values, or, when called in its short-form, a string that will be used as both the `command` and `name`. Run commands are executed using non-login shells by default, so you must explicitly source any dotfiles as part of the command." + }, + "checkout": { + "description": "https://circleci.com/docs/configuration-reference#checkout\n\nSpecial step used to check out source code to the configured `path` (defaults to the `working_directory`). The reason this is a special step is because it is more of a helper function designed to make checking out code easy for you. If you require doing git over HTTPS you should not use this step as it configures git to checkout over ssh." + }, + "setup_remote_docker": { + "description": "https://circleci.com/docs/configuration-reference#setup_remote_docker\n\nCreates a remote Docker environment configured to execute Docker commands." + }, + "save_cache": { + "description": "https://circleci.com/docs/configuration-reference#save_cache\n\nGenerates and stores a cache of a file or directory of files such as dependencies or source code in our object storage. Later jobs can restore this cache using the `restore_cache` step." + }, + "restore_cache": { + "description": "https://circleci.com/docs/configuration-reference#restore_cache\n\nRestores a previously saved cache based on a `key`. Cache needs to have been saved first for this key using the `save_cache` step." + }, + "deploy": { + "description": "https://circleci.com/docs/configuration-reference#deploy\n\nSpecial step for deploying artifacts. `deploy` uses the same configuration map and semantics as run step. Jobs may have more than one deploy step. In general deploy step behaves just like run with two exceptions:\n* In a job with parallelism, the deploy step will only be executed by node #0 and only if all nodes succeed. Nodes other than #0 will skip this step.\n* In a job that runs with SSH, the deploy step will not execute" + }, + "store_artifacts": { + "description": "https://circleci.com/docs/configuration-reference#store_artifacts\n\nStep to store artifacts (for example logs, binaries, etc) to be available in the web app or through the API." + }, + "store_test_results": { + "description": "https://circleci.com/docs/configuration-reference#storetestresults\n\nSpecial step used to upload test results so they display in builds' Test Summary section and can be used for timing analysis. To also see test result as build artifacts, please use the `store_artifacts` step." + }, + "persist_to_workspace": { + "description": "https://circleci.com/docs/configuration-reference#persist_to_workspace\n\nSpecial step used to persist a temporary file to be used by another job in the workflow" + }, + "attach_workspace": { + "description": "https://circleci.com/docs/configuration-reference#attach_workspace\n\nSpecial step used to attach the workflow's workspace to the current container. The full contents of the workspace are downloaded and copied into the directory the workspace is being attached at." + }, + "add_ssh_keys": { + "description": "https://circleci.com/docs/configuration-reference#add_ssh_keys\n\nSpecial step that adds SSH keys from a project's settings to a container. Also configures SSH to use these keys." + }, + "when": { + "description": "https://circleci.com/docs/configuration-reference#the-when-step-requires-version-21 \n\nConditional step to run on custom conditions (determined at config-compile time) that are checked before a workflow runs" + }, + "unless": { + "description": "https://circleci.com/docs/configuration-reference#the-when-step-requires-version-21 \n\nConditional step to run when custom conditions aren't met (determined at config-compile time) that are checked before a workflow runs" + } + }, + "configuration": { + "run": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/run" + } + ], + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": ["command"], + "properties": { + "command": { + "description": "Command to run via the shell", + "type": "string" + }, + "name": { + "description": "Title of the step to be shown in the CircleCI UI (default: full `command`)", + "type": "string" + }, + "shell": { + "description": "Shell to use for execution command", + "type": "string" + }, + "environment": { + "description": "Additional environmental variables, locally scoped to command", + "type": "object", + "additionalProperties": { + "type": ["string", "number"] + } + }, + "background": { + "description": "Whether or not this step should run in the background (default: false)", + "default": false, + "type": "boolean" + }, + "working_directory": { + "description": "In which directory to run this step (default: `working_directory` of the job", + "type": "string" + }, + "no_output_timeout": { + "description": "Elapsed time the command can run without output. The string is a decimal with unit suffix, such as \"20m\", \"1.25h\", \"5s\" (default: 10 minutes)", + "type": "string", + "pattern": "\\d+(\\.\\d+)?[mhs]", + "default": "10m" + }, + "when": { + "description": "Specify when to enable or disable the step. Takes the following values: `always`, `on_success`, `on_fail` (default: `on_success`)", + "enum": ["always", "on_success", "on_fail"] + } + } + } + ] + }, + "checkout": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/checkout" + } + ], + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "description": "Title of the step to be shown in the CircleCI UI", + "type": "string" + }, + "path": { + "description": "Checkout directory (default: job's `working_directory`)", + "type": "string" + } + } + }, + "setup_remote_docker": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/setup_remote_docker" + } + ], + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "description": "Title of the step to be shown in the CircleCI UI", + "type": "string" + }, + "docker_layer_caching": { + "description": "When `docker_layer_caching` is set to `true`, CircleCI will try to reuse Docker Images (layers) built during a previous job or workflow (Paid feature)", + "type": "boolean", + "default": false + }, + "version": { + "description": "If your build requires a specific docker image, you can set it as an image attribute", + "anyOf": [ + { + "type": "string", + "enum": [ + "20.10.24", + "20.10.23", + "20.10.18", + "20.10.17", + "20.10.14", + "20.10.12", + "20.10.11", + "20.10.7", + "20.10.6", + "20.10.2", + "19.03.13" + ] + }, + { + "type": "string" + } + ] + } + } + }, + "save_cache": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/save_cache" + } + ], + "type": "object", + "additionalProperties": false, + "required": ["paths", "key"], + "properties": { + "paths": { + "description": "List of directories which should be added to the cache", + "type": "array", + "items": { + "type": "string" + } + }, + "key": { + "description": "Unique identifier for this cache", + "type": "string" + }, + "name": { + "type": "string", + "description": "Title of the step to be shown in the CircleCI UI (default: 'Saving Cache')" + }, + "when": { + "description": "Specify when to enable or disable the step. Takes the following values: `always`, `on_success`, `on_fail` (default: `on_success`)", + "enum": ["always", "on_success", "on_fail"] + } + } + }, + "restore_cache": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/restore_cache" + } + ], + "oneOf": [ + { + "type": "object", + "additionalProperties": false, + "required": ["key"], + "properties": { + "key": { + "type": "string", + "description": "Single cache key to restore" + }, + "name": { + "type": "string", + "description": "Title of the step to be shown in the CircleCI UI (default: 'Restoring Cache')" + } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": ["keys"], + "properties": { + "name": { + "type": "string", + "description": "Title of the step to be shown in the CircleCI UI (default: 'Restoring Cache')" + }, + "keys": { + "description": "List of cache keys to lookup for a cache to restore. Only first existing key will be restored.", + "type": "array", + "items": { + "type": "string" + } + } + } + } + ] + }, + "deploy": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/deploy" + }, + { + "$ref": "#/definitions/builtinSteps/configuration/run" + } + ] + }, + "store_artifacts": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/store_artifacts" + } + ], + "type": "object", + "additionalProperties": false, + "required": ["path"], + "properties": { + "name": { + "description": "Title of the step to be shown in the CircleCI UI", + "type": "string" + }, + "path": { + "description": "Directory in the primary container to save as job artifacts", + "type": "string" + }, + "destination": { + "description": "Prefix added to the artifact paths in the artifacts API (default: the directory of the file specified in `path`)", + "type": "string" + } + } + }, + "store_test_results": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/store_test_results" + } + ], + "type": "object", + "additionalProperties": false, + "required": ["path"], + "properties": { + "name": { + "description": "Title of the step to be shown in the CircleCI UI", + "type": "string" + }, + "path": { + "description": "Path (absolute, or relative to your `working_directory`) to directory containing subdirectories of JUnit XML or Cucumber JSON test metadata files", + "type": "string" + } + } + }, + "persist_to_workspace": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/persist_to_workspace" + } + ], + "type": "object", + "additionalProperties": false, + "required": ["root", "paths"], + "properties": { + "name": { + "description": "Title of the step to be shown in the CircleCI UI", + "type": "string" + }, + "root": { + "description": "Either an absolute path or a path relative to `working_directory`", + "type": "string" + }, + "paths": { + "description": "Glob identifying file(s), or a non-glob path to a directory to add to the shared workspace. Interpreted as relative to the workspace root. Must not be the workspace root itself.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "attach_workspace": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/attach_workspace" + } + ], + "type": "object", + "additionalProperties": false, + "required": ["at"], + "properties": { + "name": { + "description": "Title of the step to be shown in the CircleCI UI", + "type": "string" + }, + "at": { + "description": "Directory to attach the workspace to", + "type": "string" + } + } + }, + "add_ssh_keys": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/add_ssh_keys" + } + ], + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "description": "Title of the step to be shown in the CircleCI UI", + "type": "string" + }, + "fingerprints": { + "description": "Directory to attach the workspace to", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "when": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/when" + } + ], + "type": "object", + "additionalProperties": false, + "properties": { + "condition": { + "$ref": "#/definitions/logical" + }, + "steps": { + "description": "A list of steps to be performed", + "type": "array", + "items": { + "$ref": "#/definitions/step" + } + } + }, + "required": ["condition", "steps"] + }, + "unless": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/unless" + } + ], + "type": "object", + "additionalProperties": false, + "properties": { + "condition": { + "$ref": "#/definitions/logical" + }, + "steps": { + "description": "A list of steps to be performed", + "type": "array", + "items": { + "$ref": "#/definitions/step" + } + } + }, + "required": ["condition", "steps"] + } + } + }, + "step": { + "anyOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/checkout", + "enum": ["checkout"] + }, + { + "$ref": "#/definitions/builtinSteps/documentation/setup_remote_docker", + "enum": ["setup_remote_docker"] + }, + { + "$ref": "#/definitions/builtinSteps/documentation/add_ssh_keys", + "enum": ["add_ssh_keys"] + }, + { + "description": "https://circleci.com/docs/reusing-config#invoking-reusable-commands\n\nA custom command defined via the top level commands key", + "type": "string", + "pattern": "^[a-z][a-z0-9_-]+$" + }, + { + "description": "https://circleci.com/docs/using-orbs#commands\n\nA custom command defined via an orb.", + "type": "string", + "pattern": "^[a-z][a-z0-9_-]+/[a-z][a-z0-9_-]+$" + }, + { + "type": "object", + "minProperties": 1, + "maxProperties": 1, + "properties": { + "run": { + "$ref": "#/definitions/builtinSteps/configuration/run" + }, + "checkout": { + "$ref": "#/definitions/builtinSteps/configuration/checkout" + }, + "setup_remote_docker": { + "$ref": "#/definitions/builtinSteps/configuration/setup_remote_docker" + }, + "save_cache": { + "$ref": "#/definitions/builtinSteps/configuration/save_cache" + }, + "restore_cache": { + "$ref": "#/definitions/builtinSteps/configuration/restore_cache" + }, + "deploy": { + "$ref": "#/definitions/builtinSteps/configuration/deploy" + }, + "store_artifacts": { + "$ref": "#/definitions/builtinSteps/configuration/store_artifacts" + }, + "store_test_results": { + "$ref": "#/definitions/builtinSteps/configuration/store_test_results" + }, + "persist_to_workspace": { + "$ref": "#/definitions/builtinSteps/configuration/persist_to_workspace" + }, + "attach_workspace": { + "$ref": "#/definitions/builtinSteps/configuration/attach_workspace" + }, + "add_ssh_keys": { + "$ref": "#/definitions/builtinSteps/configuration/add_ssh_keys" + }, + "when": { + "$ref": "#/definitions/builtinSteps/configuration/when" + }, + "unless": { + "$ref": "#/definitions/builtinSteps/configuration/unless" + } + }, + "patternProperties": { + "^[a-z][a-z0-9_-]+$": { + "description": "https://circleci.com/docs/reusing-config#invoking-reusable-commands\n\nA custom command defined via the top level commands key" + }, + "^[a-z][a-z0-9_-]+/[a-z][a-z0-9_-]+$": { + "description": "https://circleci.com/docs/using-orbs#commands\n\nA custom command defined via an orb." + } + } + } + ] + }, + "jobRef": { + "description": "Run a job as part of this workflow", + "type": "object", + "additionalProperties": true, + "properties": { + "requires": { + "description": "Jobs are run in parallel by default, so you must explicitly require any dependencies by their job name.", + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "description": "The name key can be used to ensure build numbers are not appended when invoking the same job multiple times (e.g., sayhello-1, sayhello-2). The name assigned needs to be unique, otherwise numbers will still be appended to the job name", + "type": "string" + }, + "context": { + "description": "Either a single context name, or a list of contexts. The default name is `org-global`", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ], + "default": "org-global" + }, + "type": { + "description": "A job may have a `type` of `approval` indicating it must be manually approved before downstream jobs may proceed.", + "enum": ["approval"] + }, + "filters": { + "description": "A map defining rules for execution on specific branches", + "type": "object", + "additionalProperties": false, + "properties": { + "branches": { + "$ref": "#/definitions/filter" + }, + "tags": { + "$ref": "#/definitions/filter" + } + } + }, + "matrix": { + "description": "https://circleci.com/docs/configuration-reference#matrix-requires-version-21\n\nThe matrix stanza allows you to run a parameterized job multiple times with different arguments.", + "type": "object", + "additionalProperties": false, + "required": ["parameters"], + "properties": { + "parameters": { + "description": "A map of parameter names to every value the job should be called with", + "type": "object", + "additionalProperties": { + "type": "array" + } + }, + "exclude": { + "description": "A list of argument maps that should be excluded from the matrix", + "type": "array", + "items": { + "type": "object" + } + }, + "alias": { + "description": "An alias for the matrix, usable from another job's requires stanza. Defaults to the name of the job being executed", + "type": "string" + } + } + } + } + }, + "jobs": { + "description": "Jobs are collections of steps. All of the steps in the job are executed in a single unit, either within a fresh container or VM.", + "type": "object", + "additionalProperties": { + "type": "object", + "oneOf": [ + { + "$ref": "#/definitions/executorChoice" + }, + { + "type": "object", + "required": ["executor"], + "properties": { + "executor": { + "description": "The name of the executor to use (defined via the top level executors map).", + "type": "string" + } + } + }, + { + "type": "object", + "required": ["executor"], + "properties": { + "executor": { + "description": "Executor stanza to use for the job", + "type": "object", + "required": ["name"], + "properties": { + "name": { + "description": "The name of the executor to use (defined via the top level executors map).", + "type": "string" + } + } + } + } + } + ], + "required": ["steps"], + "properties": { + "shell": { + "description": "Shell to use for execution command in all steps. Can be overridden by shell in each step", + "type": "string" + }, + "steps": { + "description": "A list of steps to be performed", + "type": "array", + "items": { + "$ref": "#/definitions/step" + } + }, + "working_directory": { + "description": "In which directory to run the steps. (default: `~/project`. `project` is a literal string, not the name of the project.) You can also refer the directory with `$CIRCLE_WORKING_DIRECTORY` environment variable.", + "type": "string", + "default": "~/project" + }, + "parallelism": { + "description": "Number of parallel instances of this job to run (default: 1)", + "default": 1, + "oneOf": [ + { + "type": "integer" + }, + { + "type": "string", + "pattern": "^<<.+\\..+>>$" + } + ] + }, + "environment": { + "description": "A map of environment variable names and variables (NOTE: these will override any environment variables you set in the CircleCI web interface).", + "type": "object", + "additionalProperties": { + "type": ["string", "number"] + } + }, + "branches": { + "description": "A map defining rules for whitelisting/blacklisting execution of specific branches for a single job that is **not** in a workflow (default: all whitelisted). See Workflows for configuring branch execution for jobs in a workflow.", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "properties": { + "version": { + "description": "The version field is intended to be used in order to issue warnings for deprecation or breaking changes.", + "default": 2.1, + "enum": [2, 2.1] + }, + "orbs": { + "$ref": "#/definitions/orbs" + }, + "commands": { + "$ref": "#/definitions/commands" + }, + "executors": { + "$ref": "#/definitions/executors" + }, + "jobs": { + "$ref": "#/definitions/jobs" + }, + "workflows": { + "description": "Used for orchestrating all jobs. Each workflow consists of the workflow name as a key and a map as a value", + "type": "object", + "properties": { + "version": { + "description": "The Workflows `version` field is used to issue warnings for deprecation or breaking changes during v2 Beta. It is deprecated as of CircleCI v2.1", + "enum": [2] + } + }, + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "triggers": { + "description": "Specifies which triggers will cause this workflow to be executed. Default behavior is to trigger the workflow when pushing to a branch.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "schedule": { + "description": "A workflow may have a schedule indicating it runs at a certain time, for example a nightly build that runs every day at 12am UTC:", + "type": "object", + "properties": { + "cron": { + "description": "See the [crontab man page](http://pubs.opengroup.org/onlinepubs/7908799/xcu/crontab.html)", + "type": "string" + }, + "filters": { + "description": "A map defining rules for execution on specific branches", + "type": "object", + "additionalProperties": false, + "properties": { + "branches": { + "$ref": "#/definitions/filter" + } + } + } + } + } + } + } + }, + "jobs": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/jobRef", + "type": "object" + } + } + ] + } + }, + "when": { + "$ref": "#/definitions/logical", + "description": "Specify when to run the workflow." + }, + "unless": { + "$ref": "#/definitions/logical", + "description": "Specify when *not* to run the workflow." + } + } + } + } + }, + "required": ["version"], + "title": "JSON schema for CircleCI configuration files", + "type": "object" +} diff --git a/ci/schemas/codecov.json b/ci/schemas/codecov.json new file mode 100644 index 000000000000..98decea44415 --- /dev/null +++ b/ci/schemas/codecov.json @@ -0,0 +1,620 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/codecov", + "definitions": { + "default": { + "$comment": "See https://docs.codecov.com/docs/commit-status#basic-configuration", + "properties": { + "target": { + "type": ["string", "number"], + "pattern": "^(([0-9]+\\.?[0-9]*|\\.[0-9]+)%?|auto)$", + "default": "auto" + }, + "threshold": { + "type": "string", + "default": "0%", + "pattern": "^([0-9]+\\.?[0-9]*|\\.[0-9]+)%?$" + }, + "base": { + "type": "string", + "default": "auto", + "deprecated": true + }, + "flags": { + "type": "array", + "default": [] + }, + "paths": { + "type": ["array", "string"], + "default": [] + }, + "branches": { + "type": "array", + "default": [] + }, + "if_not_found": { + "type": "string", + "enum": ["failure", "success"], + "default": "success" + }, + "informational": { + "type": "boolean", + "default": false + }, + "only_pulls": { + "type": "boolean", + "default": false + }, + "if_ci_failed": { + "type": "string", + "enum": ["error", "success"] + }, + "flag_coverage_not_uploaded_behavior": { + "type": "string", + "enum": ["include", "exclude", "pass"] + } + } + }, + "flag": { + "type": "object", + "properties": { + "joined": { + "type": "boolean" + }, + "required": { + "type": "boolean" + }, + "ignore": { + "type": "array", + "items": { + "type": "string" + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "assume": { + "type": ["boolean", "array"], + "items": { + "type": "string" + } + } + } + }, + "layout": { + "anyOf": [ + {}, + { + "enum": [ + "header", + "footer", + "diff", + "file", + "files", + "flag", + "flags", + "reach", + "sunburst", + "uncovered" + ] + } + ] + }, + "notification": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "branches": { + "type": "string" + }, + "threshold": { + "type": "string" + }, + "message": { + "type": "string" + }, + "flags": { + "type": "string" + }, + "base": { + "enum": ["parent", "pr", "auto"] + }, + "only_pulls": { + "type": "boolean" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "description": "Schema for codecov.yml files.", + "properties": { + "codecov": { + "description": "See https://docs.codecov.io/docs/codecov-yaml for details", + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "bot": { + "description": "Team bot. See https://docs.codecov.io/docs/team-bot for details", + "type": "string" + }, + "branch": { + "type": "string" + }, + "ci": { + "description": "Detecting CI services. See https://docs.codecov.io/docs/detecting-ci-services for details.", + "type": "array", + "items": { + "type": "string" + } + }, + "assume_all_flags": { + "type": "boolean" + }, + "strict_yaml_branch": { + "type": "string" + }, + "max_report_age": { + "type": ["string", "integer", "boolean"] + }, + "disable_default_path_fixes": { + "type": "boolean" + }, + "require_ci_to_pass": { + "type": "boolean" + }, + "allow_pseudo_compare": { + "type": "boolean" + }, + "archive": { + "type": "object", + "properties": { + "uploads": { + "type": "boolean" + } + } + }, + "notify": { + "type": "object", + "properties": { + "after_n_builds": { + "type": "integer" + }, + "countdown": { + "type": "integer" + }, + "delay": { + "type": "integer" + }, + "wait_for_ci": { + "type": "boolean" + } + } + }, + "ui": { + "type": "object", + "properties": { + "hide_density": { + "type": ["boolean", "array"], + "items": { + "type": "string" + } + }, + "hide_complexity": { + "type": ["boolean", "array"], + "items": { + "type": "string" + } + }, + "hide_contextual": { + "type": "boolean" + }, + "hide_sunburst": { + "type": "boolean" + }, + "hide_search": { + "type": "boolean" + } + } + } + } + }, + "coverage": { + "description": "Coverage configuration. See https://docs.codecov.io/docs/coverage-configuration for details.", + "type": "object", + "properties": { + "precision": { + "type": "integer", + "minimum": 0, + "maximum": 5 + }, + "round": { + "enum": ["down", "up", "nearest"] + }, + "range": { + "type": "string" + }, + "notify": { + "description": "Notifications. See https://docs.codecov.io/docs/notifications for details.", + "type": "object", + "properties": { + "irc": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "branches": { + "type": "string" + }, + "threshold": { + "type": "string" + }, + "message": { + "type": "string" + }, + "flags": { + "type": "string" + }, + "base": { + "enum": ["parent", "pr", "auto"] + }, + "only_pulls": { + "type": "boolean" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "channel": { + "type": "string" + }, + "password": { + "type": "string" + }, + "nickserv_password": { + "type": "string" + }, + "notice": { + "type": "boolean" + } + } + }, + "slack": { + "description": "Slack. See https://docs.codecov.io/docs/notifications#section-slack for details.", + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "branches": { + "type": "string" + }, + "threshold": { + "type": "string" + }, + "message": { + "type": "string" + }, + "flags": { + "type": "string" + }, + "base": { + "enum": ["parent", "pr", "auto"] + }, + "only_pulls": { + "type": "boolean" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "attachments": { + "$ref": "#/definitions/layout" + } + } + }, + "gitter": { + "description": "Gitter. See https://docs.codecov.io/docs/notifications#section-gitter for details.", + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "branches": { + "type": "string" + }, + "threshold": { + "type": "string" + }, + "message": { + "type": "string" + }, + "flags": { + "type": "string" + }, + "base": { + "enum": ["parent", "pr", "auto"] + }, + "only_pulls": { + "type": "boolean" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "hipchat": { + "description": "Hipchat. See https://docs.codecov.io/docs/notifications#section-hipchat for details.", + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "branches": { + "type": "string" + }, + "threshold": { + "type": "string" + }, + "message": { + "type": "string" + }, + "flags": { + "type": "string" + }, + "base": { + "enum": ["parent", "pr", "auto"] + }, + "only_pulls": { + "type": "boolean" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "card": { + "type": "boolean" + }, + "notify": { + "type": "boolean" + } + } + }, + "webhook": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "branches": { + "type": "string" + }, + "threshold": { + "type": "string" + }, + "message": { + "type": "string" + }, + "flags": { + "type": "string" + }, + "base": { + "enum": ["parent", "pr", "auto"] + }, + "only_pulls": { + "type": "boolean" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "email": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "branches": { + "type": "string" + }, + "threshold": { + "type": "string" + }, + "message": { + "type": "string" + }, + "flags": { + "type": "string" + }, + "base": { + "enum": ["parent", "pr", "auto"] + }, + "only_pulls": { + "type": "boolean" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "layout": { + "$ref": "#/definitions/layout" + }, + "+to": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "status": { + "description": "Commit status. See https://docs.codecov.io/docs/commit-status for details.", + "type": ["boolean", "object"], + "additionalProperties": false, + "properties": { + "default_rules": { + "type": "object" + }, + "project": { + "properties": { + "default": { + "$ref": "#/definitions/default", + "type": ["object", "boolean"] + } + }, + "additionalProperties": { + "$ref": "#/definitions/default", + "type": ["object", "boolean"] + } + }, + "patch": { + "anyOf": [ + { + "$ref": "#/definitions/default", + "type": "object" + }, + { + "type": "string", + "enum": ["off"] + }, + { + "type": "boolean" + } + ] + }, + "changes": { + "$ref": "#/definitions/default", + "type": ["object", "boolean"] + } + } + } + } + }, + "ignore": { + "description": "Ignoring paths. see https://docs.codecov.io/docs/ignoring-paths for details.", + "type": "array", + "items": { + "type": "string" + } + }, + "fixes": { + "description": "Fixing paths. See https://docs.codecov.io/docs/fixing-paths for details.", + "type": "array", + "items": { + "type": "string" + } + }, + "flags": { + "description": "Flags. See https://docs.codecov.io/docs/flags for details.", + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/definitions/flag" + } + }, + { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/flag" + } + } + ] + }, + "comment": { + "description": "Pull request comments. See https://docs.codecov.io/docs/pull-request-comments for details.", + "oneOf": [ + { + "type": "object", + "properties": { + "layout": { + "$ref": "#/definitions/layout" + }, + "require_changes": { + "type": "boolean" + }, + "require_base": { + "type": "boolean" + }, + "require_head": { + "type": "boolean" + }, + "branches": { + "type": "array", + "items": { + "type": "string" + } + }, + "behavior": { + "enum": ["default", "once", "new", "spammy"] + }, + "flags": { + "type": "array", + "items": { + "$ref": "#/definitions/flag" + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + { + "const": false + } + ] + }, + "github_checks": { + "description": "GitHub Checks. See https://docs.codecov.com/docs/github-checks for details.", + "anyOf": [ + { + "type": "object", + "properties": { + "annotations": { + "type": "boolean" + } + } + }, + { "type": "boolean" }, + { "type": "string", "enum": ["off"] } + ] + } + }, + "title": "JSON schema for Codecov configuration files", + "type": "object" +} diff --git a/ci/schemas/conda-environment.json b/ci/schemas/conda-environment.json new file mode 100644 index 000000000000..458676942a44 --- /dev/null +++ b/ci/schemas/conda-environment.json @@ -0,0 +1,53 @@ +{ + "title": "conda environment file", + "description": "Support for conda's enviroment.yml files (e.g. `conda env export > environment.yml`)", + "id": "https://raw.githubusercontent.com/Microsoft/vscode-python/main/schemas/conda-environment.json", + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "channel": { + "type": "string" + }, + "package": { + "type": "string" + }, + "path": { + "type": "string" + } + }, + "properties": { + "name": { + "type": "string" + }, + "channels": { + "type": "array", + "items": { + "$ref": "#/definitions/channel" + } + }, + "dependencies": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/package" + }, + { + "type": "object", + "properties": { + "pip": { + "type": "array", + "items": { + "$ref": "#/definitions/package" + } + } + }, + "required": ["pip"] + } + ] + } + }, + "prefix": { + "$ref": "#/definitions/path" + } + } +} diff --git a/ci/schemas/github-funding.json b/ci/schemas/github-funding.json new file mode 100644 index 000000000000..d146d692c483 --- /dev/null +++ b/ci/schemas/github-funding.json @@ -0,0 +1,113 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/github-funding.json", + "$comment": "https://docs.github.com/en/github/administering-a-repository/displaying-a-sponsor-button-in-your-repository", + "additionalProperties": false, + "definitions": { + "github_username": { + "type": "string", + "maxLength": 39, + "pattern": "^[a-zA-Z0-9](-?[a-zA-Z0-9])*$", + "examples": ["SampleUserName"] + }, + "nullable_string": { + "type": ["string", "null"] + } + }, + "description": "You can add a sponsor button in your repository to increase the visibility of funding options for your open source project.", + "properties": { + "community_bridge": { + "$ref": "#/definitions/nullable_string", + "title": "CommunityBridge", + "description": "Project name on CommunityBridge.", + "minLength": 1 + }, + "github": { + "title": "GitHub Sponsors", + "description": "Username or usernames on GitHub.", + "oneOf": [ + { + "$ref": "#/definitions/github_username" + }, + { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/github_username" + } + } + ] + }, + "issuehunt": { + "$ref": "#/definitions/nullable_string", + "title": "IssueHunt", + "description": "Username on IssueHunt.", + "minLength": 1 + }, + "ko_fi": { + "$ref": "#/definitions/nullable_string", + "title": "Ko-fi", + "description": "Username on Ko-fi.", + "minLength": 1 + }, + "liberapay": { + "$ref": "#/definitions/nullable_string", + "title": "Liberapay", + "description": "Username on Liberapay.", + "minLength": 1 + }, + "open_collective": { + "$ref": "#/definitions/nullable_string", + "title": "Open Collective", + "description": "Username on Open Collective.", + "minLength": 1 + }, + "otechie": { + "$ref": "#/definitions/nullable_string", + "title": "Otechie", + "description": "Username on Otechie.", + "minLength": 1 + }, + "patreon": { + "$ref": "#/definitions/nullable_string", + "title": "Patreon", + "description": "Username on Pateron.", + "minLength": 1, + "maxLength": 100 + }, + "tidelift": { + "$ref": "#/definitions/nullable_string", + "title": "Tidelift", + "description": "Platform and package on Tidelift.", + "pattern": "^(npm|pypi|rubygems|maven|packagist|nuget)/.+$" + }, + "lfx_crowdfunding": { + "$ref": "#/definitions/nullable_string", + "title": "LFX Crowdfunding", + "description": "Project name on LFX Crowdfunding.", + "minLength": 1 + }, + "polar": { + "$ref": "#/definitions/github_username", + "title": "Polar", + "description": "Username on Polar.", + "minLength": 1 + }, + "custom": { + "title": "Custom URL", + "description": "Link or links where funding is accepted on external locations.", + "type": ["string", "array", "null"], + "format": "uri-reference", + "items": { + "title": "Link", + "description": "Link to an external location.", + "type": "string", + "format": "uri-reference" + }, + "uniqueItems": true + } + }, + "title": "GitHub Funding", + "type": "object" +} diff --git a/ci/schemas/github-issue-config.json b/ci/schemas/github-issue-config.json new file mode 100644 index 000000000000..b46556bb04a5 --- /dev/null +++ b/ci/schemas/github-issue-config.json @@ -0,0 +1,45 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/github-issue-config.json", + "$comment": "https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser", + "properties": { + "blank_issues_enabled": { + "description": "Specify whether allow blank issue creation\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser", + "type": "boolean" + }, + "contact_links": { + "title": "contact links", + "description": "Contact links\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser", + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": ["name", "url", "about"], + "properties": { + "name": { + "description": "A link title\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser", + "type": "string", + "minLength": 1, + "examples": ["Sample name"] + }, + "url": { + "description": "A link URL\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser", + "type": "string", + "pattern": "^https?://", + "examples": ["https://sample/url"] + }, + "about": { + "description": "A link description\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser", + "type": "string", + "minLength": 1, + "examples": ["Sample description"] + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false, + "title": "GitHub issue template chooser config file schema", + "type": "object" +} diff --git a/ci/schemas/github-issue-forms.json b/ci/schemas/github-issue-forms.json new file mode 100644 index 000000000000..c928818dfdd1 --- /dev/null +++ b/ci/schemas/github-issue-forms.json @@ -0,0 +1,1295 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/github-issue-forms.json", + "$comment": "https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms", + "additionalProperties": false, + "definitions": { + "type": { + "description": "A form item type\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#keys", + "type": "string", + "enum": ["checkboxes", "dropdown", "input", "markdown", "textarea"] + }, + "id": { + "type": "string", + "pattern": "^[a-zA-Z0-9_-]+$", + "examples": ["SampleId"] + }, + "validations": { + "title": "validation options", + "type": "object", + "properties": { + "required": { + "description": "Specify whether require a form item", + "type": "boolean", + "default": false + } + }, + "additionalProperties": false + }, + "assignee": { + "type": "string", + "maxLength": 39, + "pattern": "^[a-zA-Z0-9](-?[a-zA-Z0-9])*$", + "examples": ["SampleAssignee"] + }, + "label": { + "type": "string", + "minLength": 1, + "examples": ["Sample label"] + }, + "description": { + "type": "string", + "default": "", + "examples": ["Sample description"] + }, + "placeholder": { + "type": "string", + "default": "", + "examples": ["Sample placeholder"] + }, + "value": { + "type": "string", + "minLength": 1, + "examples": ["Sample value"] + }, + "form_item": { + "title": "form item", + "description": "A form item\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#about-githubs-form-schema", + "type": "object", + "required": ["type"], + "properties": { + "type": { + "$ref": "#/definitions/type" + } + }, + "allOf": [ + { + "if": { + "properties": { + "type": { + "const": "markdown" + } + } + }, + "then": { + "$comment": "For `additionalProperties` to work `type` must also be present here.", + "title": "markdown", + "description": "Markdown\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#markdown", + "type": "object", + "required": ["type", "attributes"], + "properties": { + "type": { + "$ref": "#/definitions/type" + }, + "attributes": { + "title": "markdown attributes", + "description": "Markdown attributes\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes", + "type": "object", + "required": ["value"], + "properties": { + "value": { + "description": "A markdown code\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes", + "type": "string", + "minLength": 1, + "examples": ["Sample code"] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + { + "if": { + "properties": { + "type": { + "const": "textarea" + } + } + }, + "then": { + "$comment": "For `additionalProperties` to work `type` must also be present here.", + "title": "textarea", + "description": "Textarea\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#textarea", + "type": "object", + "required": ["type", "attributes"], + "properties": { + "type": { + "$ref": "#/definitions/type" + }, + "id": { + "$ref": "#/definitions/id", + "description": "A textarea id\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#keys" + }, + "attributes": { + "title": "textarea attributes", + "description": "Textarea attributes\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-1", + "type": "object", + "required": ["label"], + "properties": { + "label": { + "$ref": "#/definitions/label", + "description": "A short textarea description\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-1" + }, + "description": { + "$ref": "#/definitions/description", + "description": "A long textarea description\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-1" + }, + "placeholder": { + "$ref": "#/definitions/placeholder", + "description": "A textarea placeholder\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-1" + }, + "value": { + "$ref": "#/definitions/value", + "description": "A textarea value\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-1" + }, + "render": { + "description": "A textarea syntax highlighting mode\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-1", + "type": "string", + "enum": [ + "1C Enterprise", + "4D", + "ABAP CDS", + "ABAP", + "ABNF", + "AFDKO", + "AGS Script", + "AIDL", + "AL", + "AMPL", + "ANTLR", + "API Blueprint", + "APL", + "ASL", + "ASN.1", + "ASP.NET", + "ATS", + "ActionScript", + "Ada", + "Alloy", + "Alpine Abuild", + "Altium Designer", + "AngelScript", + "Ant Build System", + "ApacheConf", + "Apex", + "Apollo Guidance Computer", + "AppleScript", + "Arc", + "AsciiDoc", + "AspectJ", + "Assembly", + "Astro", + "Asymptote", + "Augeas", + "AutoHotkey", + "AutoIt", + "AutoIt3", + "AutoItScript", + "Avro IDL", + "Awk", + "BASIC", + "Ballerina", + "Batchfile", + "Beef", + "Befunge", + "BibTeX", + "Bicep", + "Bison", + "BitBake", + "Blade", + "BlitzBasic", + "BlitzMax", + "Boo", + "Boogie", + "Brainfuck", + "Brightscript", + "Browserslist", + "C", + "C#", + "C++", + "C-ObjDump", + "C2hs Haskell", + "CIL", + "CLIPS", + "CMake", + "COBOL", + "CODEOWNERS", + "COLLADA", + "CSON", + "CSS", + "CSV", + "CUE", + "CWeb", + "Cabal Config", + "Cabal", + "Cap'n Proto", + "Carto", + "CartoCSS", + "Ceylon", + "Chapel", + "Charity", + "ChucK", + "Cirru", + "Clarion", + "Classic ASP", + "Clean", + "Click", + "Clojure", + "Closure Templates", + "Cloud Firestore Security Rules", + "CoNLL", + "CoNLL-U", + "CoNLL-X", + "ColdFusion CFC", + "ColdFusion", + "Common Lisp", + "Common Workflow Language", + "Component Pascal", + "Containerfile", + "Cool", + "Coq", + "Cpp-ObjDump", + "Crystal", + "Csound Document", + "Csound Score", + "Csound", + "Cuda", + "Cue Sheet", + "Cycript", + "Cython", + "D-ObjDump", + "DIGITAL Command Language", + "DM", + "DTrace", + "Dafny", + "Darcs Patch", + "Dart", + "DataWeave", + "Dhall", + "Diff", + "Dlang", + "Dockerfile", + "Dogescript", + "Dylan", + "E", + "E-mail", + "EBNF", + "ECL", + "ECLiPSe", + "EJS", + "EQ", + "Eagle", + "Earthly", + "Easybuild", + "Ecere Projects", + "EditorConfig", + "Eiffel", + "Elixir", + "Elm", + "Emacs Lisp", + "EmberScript", + "Erlang", + "F#", + "F*", + "FIGfont", + "FIGlet Font", + "FLUX", + "Factor", + "Fancy", + "Fantom", + "Faust", + "Fennel", + "Filebench WML", + "Filterscript", + "Fluent", + "Formatted", + "Forth", + "Fortran Free Form", + "Fortran", + "FreeBasic", + "Frege", + "Futhark", + "G-code", + "GAML", + "GAMS", + "GAP", + "GCC Machine Description", + "GDB", + "GDScript", + "GEDCOM", + "GLSL", + "GN", + "Game Maker Language", + "Gemfile.lock", + "Genie", + "Genshi", + "Gentoo Eclass", + "Gerber Image", + "Gettext Catalog", + "Gherkin", + "Git Config", + "Glyph Bitmap Distribution Format", + "Glyph", + "Gnuplot", + "Go Checksums", + "Go Module", + "Go", + "Golo", + "Gosu", + "Grace", + "Gradle", + "Grammatical Framework", + "Graph Modeling Language", + "GraphQL", + "Graphviz (DOT)", + "Groovy Server Pages", + "Groovy", + "HAProxy", + "HCL", + "HTML", + "HTML+ECR", + "HTML+EEX", + "HTML+ERB", + "HTML+PHP", + "HTML+Razor", + "HTTP", + "HXML", + "Hack", + "Haml", + "Handlebars", + "Harbour", + "HashiCorp Configuration Language", + "Haskell", + "Haxe", + "HiveQL", + "HolyC", + "Hy", + "IDL", + "IGOR Pro", + "IPython Notebook", + "Idris", + "Ignore List", + "ImageJ Macro", + "Inform 7", + "Io", + "Ioke", + "Isabelle ROOT", + "Isabelle", + "J", + "JAR Manifest", + "JFlex", + "JSON with Comments", + "JSON", + "JSON5", + "JSONLD", + "JSONiq", + "Jasmin", + "Java Properties", + "Java Server Pages", + "Java", + "JavaScript", + "JavaScript+ERB", + "Jest Snapshot", + "Jinja", + "Jison Lex", + "Jison", + "Jolie", + "Jsonnet", + "Julia", + "Jupyter Notebook", + "Kaitai Struct", + "KakouneScript", + "KiCad Layout", + "KiCad Legacy Layout", + "KiCad Schematic", + "Kit", + "Kotlin", + "Kusto", + "LFE", + "LLVM", + "LOLCODE", + "LSL", + "LTspice Symbol", + "LabVIEW", + "Lark", + "Lasso", + "Lean", + "Less", + "Lex", + "LilyPond", + "Limbo", + "Linker Script", + "Linux Kernel Module", + "Liquid", + "Literate Agda", + "Literate CoffeeScript", + "Literate Haskell", + "LiveScript", + "Logos", + "Logtalk", + "LookML", + "LoomScript", + "Lua", + "M", + "M4", + "M4Sugar", + "MATLAB", + "MAXScript", + "MLIR", + "MQL4", + "MQL5", + "MTML", + "MUF", + "Macaulay2", + "Makefile", + "Mako", + "Markdown", + "Marko", + "Mathematica", + "Max", + "Mercury", + "Meson", + "Metal", + "Microsoft Developer Studio Project", + "Microsoft Visual Studio Solution", + "MiniD", + "Mirah", + "Modelica", + "Modula-2", + "Modula-3", + "Module Management System", + "Monkey", + "Moocode", + "MoonScript", + "Motoko", + "Motorola 68K Assembly", + "Muse", + "Myghty", + "NASL", + "NCL", + "NEON", + "NPM Config", + "NSIS", + "NWScript", + "Nearley", + "Nemerle", + "NeoSnippet", + "NetLinx", + "NetLinx+ERB", + "NetLogo", + "NewLisp", + "Nextflow", + "Nginx", + "Ninja", + "Nit", + "Nix", + "NumPy", + "Nunjucks", + "ObjDump", + "Object Data Instance Notation", + "ObjectScript", + "Objective-C", + "Objective-C++", + "Objective-J", + "Odin", + "Omgrofl", + "Opa", + "Opal", + "Open Policy Agent", + "OpenCL", + "OpenEdge ABL", + "OpenQASM", + "OpenRC runscript", + "OpenSCAD", + "OpenStep Property List", + "OpenType Feature File", + "Org", + "Ox", + "Oxygene", + "Oz", + "P4", + "PEG.js", + "PHP", + "PLpgSQL", + "POV-Ray SDL", + "Pan", + "Papyrus", + "Parrot Assembly", + "Parrot Internal Representation", + "Parrot", + "Pascal", + "Pawn", + "Pep8", + "Perl", + "Pickle", + "PicoLisp", + "PigLatin", + "Pike", + "PlantUML", + "Pod 6", + "Pod", + "PogoScript", + "Pony", + "PostCSS", + "PostScript", + "PowerShell", + "Prisma", + "Processing", + "Proguard", + "Prolog", + "Promela", + "Propeller Spin", + "Protocol Buffer", + "Protocol Buffers", + "Public Key", + "Pug", + "Puppet", + "Pure Data", + "PureBasic", + "PureScript", + "Python", + "Q#", + "QMake", + "Qt Script", + "Quake", + "R", + "RAML", + "RDoc", + "REALbasic", + "REXX", + "RMarkdown", + "RPC", + "RPM Spec", + "Racket", + "Ragel", + "Raw token data", + "ReScript", + "Readline Config", + "Reason", + "Rebol", + "Record Jar", + "Red", + "Redirect Rules", + "Regular Expression", + "RenderScript", + "Rich Text Format", + "Ring", + "Riot", + "RobotFramework", + "Roff", + "Rouge", + "Rscript", + "Ruby", + "Rust", + "SAS", + "SCSS", + "SELinux Kernel Policy Language", + "SELinux Policy", + "SMT", + "SPARQL", + "SQF", + "SQL", + "SQLPL", + "SRecode Template", + "SSH Config", + "STON", + "SVG", + "SWIG", + "Sage", + "SaltStack", + "Sass", + "Scala", + "Scaml", + "Scheme", + "Scilab", + "Self", + "ShaderLab", + "Shell", + "ShellCheck Config", + "Sieve", + "Singularity", + "Slash", + "Slice", + "Slim", + "SmPL", + "Smalltalk", + "SnipMate", + "Solidity", + "Soong", + "SourcePawn", + "Spline Font Database", + "Squirrel", + "Stan", + "Standard ML", + "Starlark", + "StringTemplate", + "Stylus", + "SubRip Text", + "SugarSS", + "SuperCollider", + "Svelte", + "Swift", + "SystemVerilog", + "TI Program", + "TLA", + "TOML", + "TSQL", + "TSV", + "TSX", + "TXL", + "Tcl", + "Tcsh", + "TeX", + "Tea", + "Terra", + "Texinfo", + "Text", + "TextMate Properties", + "Textile", + "Thrift", + "Turing", + "Turtle", + "Twig", + "Type Language", + "TypeScript", + "UltiSnip", + "UltiSnips", + "Unified Parallel C", + "Unity3D Asset", + "Unix Assembly", + "Uno", + "UnrealScript", + "Ur", + "Ur/Web", + "UrWeb", + "V", + "VBA", + "VCL", + "VHDL", + "Vala", + "Valve Data Format", + "Verilog", + "Vim Help File", + "Vim Script", + "Vim Snippet", + "Visual Basic .NET", + "Vue", + "Wavefront Material", + "Wavefront Object", + "Web Ontology Language", + "WebAssembly", + "WebVTT", + "Wget Config", + "Wikitext", + "Windows Registry Entries", + "Wollok", + "World of Warcraft Addon Data", + "X BitMap", + "X Font Directory Index", + "X PixMap", + "X10", + "XC", + "XCompose", + "XML Property List", + "XML", + "XPages", + "XProc", + "XQuery", + "XS", + "XSLT", + "Xojo", + "Xonsh", + "Xtend", + "YAML", + "YANG", + "YARA", + "YASnippet", + "Yacc", + "ZAP", + "ZIL", + "Zeek", + "ZenScript", + "Zephir", + "Zig", + "Zimpl", + "abl", + "abuild", + "acfm", + "aconf", + "actionscript 3", + "actionscript3", + "ada2005", + "ada95", + "adobe composite font metrics", + "adobe multiple font metrics", + "advpl", + "ags", + "ahk", + "altium", + "amfm", + "amusewiki", + "apache", + "apkbuild", + "arexx", + "as3", + "asm", + "asp", + "aspx", + "aspx-vb", + "ats2", + "au3", + "autoconf", + "b3d", + "bash session", + "bash", + "bat", + "batch", + "bazel", + "blitz3d", + "blitzplus", + "bmax", + "bplus", + "bro", + "bsdmake", + "byond", + "bzl", + "c++-objdump", + "c2hs", + "cURL Config", + "cake", + "cakescript", + "cfc", + "cfm", + "cfml", + "chpl", + "clipper", + "coccinelle", + "coffee", + "coffee-script", + "coldfusion html", + "console", + "cperl", + "cpp", + "csharp", + "csound-csd", + "csound-orc", + "csound-sco", + "cucumber", + "curlrc", + "cwl", + "dcl", + "delphi", + "desktop", + "dircolors", + "django", + "dosbatch", + "dosini", + "dpatch", + "dtrace-script", + "eC", + "ecr", + "editor-config", + "edn", + "eeschema schematic", + "eex", + "elisp", + "emacs muse", + "emacs", + "email", + "eml", + "erb", + "fb", + "fish", + "flex", + "foxpro", + "fsharp", + "fstar", + "ftl", + "fundamental", + "gf", + "git-ignore", + "gitattributes", + "gitconfig", + "gitignore", + "gitmodules", + "go mod", + "go sum", + "go.mod", + "go.sum", + "golang", + "groff", + "gsp", + "hbs", + "heex", + "help", + "html+django", + "html+jinja", + "html+ruby", + "htmlbars", + "htmldjango", + "hylang", + "i7", + "ignore", + "igor", + "igorpro", + "ijm", + "inc", + "inform7", + "inputrc", + "irc logs", + "irc", + "java server page", + "jq", + "jruby", + "js", + "jsonc", + "jsp", + "kak", + "kakscript", + "keyvalues", + "ksy", + "lassoscript", + "latex", + "leex", + "lhaskell", + "lhs", + "lisp", + "litcoffee", + "live-script", + "ls", + "m2", + "m68k", + "mIRC Script", + "macruby", + "mail", + "make", + "man page", + "man", + "man-page", + "manpage", + "markojs", + "max/msp", + "maxmsp", + "mbox", + "mcfunction", + "mdoc", + "mediawiki", + "mf", + "mma", + "mumps", + "mupad", + "nanorc", + "nasm", + "ne-on", + "nesC", + "nette object notation", + "nginx configuration file", + "nixos", + "njk", + "node", + "npmrc", + "nroff", + "nush", + "nvim", + "obj-c", + "obj-c++", + "obj-j", + "objc", + "objc++", + "objectivec", + "objectivec++", + "objectivej", + "objectpascal", + "objj", + "octave", + "odin-lang", + "odinlang", + "oncrpc", + "ooc", + "openedge", + "openrc", + "osascript", + "pandoc", + "pasm", + "pcbnew", + "perl-6", + "perl6", + "pir", + "plain text", + "posh", + "postscr", + "pot", + "pov-ray", + "povray", + "progress", + "protobuf", + "pwsh", + "pycon", + "pyrex", + "python3", + "q", + "ql", + "qsharp", + "ragel-rb", + "ragel-ruby", + "rake", + "raw", + "razor", + "rb", + "rbx", + "reStructuredText", + "readline", + "red/system", + "redirects", + "regex", + "regexp", + "renpy", + "rhtml", + "robots txt", + "robots", + "robots.txt", + "rpcgen", + "rs", + "rs-274x", + "rss", + "rst", + "rusthon", + "salt", + "saltstate", + "sed", + "sepolicy", + "sh", + "shell-script", + "shellcheckrc", + "sml", + "snippet", + "sourcemod", + "soy", + "specfile", + "splus", + "squeak", + "terraform", + "tl", + "tm-properties", + "troff", + "ts", + "udiff", + "vb .net", + "vb.net", + "vb6", + "vbnet", + "vdf", + "vim", + "vimhelp", + "viml", + "visual basic 6", + "visual basic for applications", + "visual basic", + "vlang", + "wasm", + "wast", + "wdl", + "wgetrc", + "wiki", + "winbatch", + "wisp", + "wl", + "wolfram lang", + "wolfram language", + "wolfram", + "wsdl", + "xBase", + "xbm", + "xdr", + "xhtml", + "xml+genshi", + "xml+kid", + "xpm", + "xsd", + "xsl", + "xten", + "yas", + "yml", + "zsh" + ] + } + }, + "additionalProperties": false + }, + "validations": { + "$ref": "#/definitions/validations", + "title": "textarea validations", + "description": "Textarea validations\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#validations" + } + }, + "additionalProperties": false + } + }, + { + "if": { + "properties": { + "type": { + "const": "input" + } + } + }, + "then": { + "$comment": "For `additionalProperties` to work `type` must also be present here.", + "title": "input", + "description": "Input\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#input", + "type": "object", + "required": ["type", "attributes"], + "properties": { + "type": { + "$ref": "#/definitions/type" + }, + "id": { + "$ref": "#/definitions/id", + "description": "An input id\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#keys" + }, + "attributes": { + "title": "input attributes", + "description": "Input attributes\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-2", + "type": "object", + "required": ["label"], + "properties": { + "label": { + "$ref": "#/definitions/label", + "description": "A short input description\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-2" + }, + "description": { + "$ref": "#/definitions/description", + "description": "A long input description\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-2" + }, + "placeholder": { + "$ref": "#/definitions/placeholder", + "description": "An input placeholder\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-2" + }, + "value": { + "$ref": "#/definitions/value", + "description": "An input value\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-2" + } + }, + "additionalProperties": false + }, + "validations": { + "$ref": "#/definitions/validations", + "title": "input validations", + "description": "Input validations\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#validations-1" + } + }, + "additionalProperties": false + } + }, + { + "if": { + "properties": { + "type": { + "const": "dropdown" + } + } + }, + "then": { + "$comment": "For `additionalProperties` to work `type` must also be present here.", + "title": "dropdown", + "description": "dropdown\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#dropdown", + "type": "object", + "required": ["type", "attributes"], + "properties": { + "type": { + "$ref": "#/definitions/type" + }, + "id": { + "$ref": "#/definitions/id", + "description": "A dropdown id\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#keys" + }, + "attributes": { + "title": "dropdown attributes", + "description": "Dropdown attributes\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-3", + "type": "object", + "required": ["label", "options"], + "properties": { + "label": { + "$ref": "#/definitions/label", + "description": "A short dropdown description\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-3" + }, + "description": { + "$ref": "#/definitions/description", + "description": "A long dropdown description\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-3" + }, + "multiple": { + "description": "Specify whether allow a multiple choices\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-3", + "type": "boolean", + "default": false + }, + "options": { + "description": "Dropdown choices\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-3", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1, + "examples": ["Sample choice"] + } + }, + "default": { + "description": "Index of the default option\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-3", + "type": "integer", + "examples": [0] + } + }, + "additionalProperties": false + }, + "validations": { + "$ref": "#/definitions/validations", + "title": "dropdown validations", + "description": "Dropdown validations\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#validations-2" + } + }, + "additionalProperties": false + } + }, + { + "if": { + "properties": { + "type": { + "const": "checkboxes" + } + } + }, + "then": { + "$comment": "For `additionalProperties` to work `type` must also be present here.", + "title": "checkboxes", + "description": "Checkboxes\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#checkboxes", + "type": "object", + "required": ["type", "attributes"], + "properties": { + "type": { + "$ref": "#/definitions/type" + }, + "id": { + "$ref": "#/definitions/id", + "description": "Checkbox list id\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#keys" + }, + "attributes": { + "title": "checkbox list attributes", + "description": "Checkbox list attributes\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-4", + "type": "object", + "required": ["label", "options"], + "properties": { + "label": { + "$ref": "#/definitions/label", + "description": "A short checkbox list description\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-4" + }, + "description": { + "$ref": "#/definitions/description", + "description": "A long checkbox list description\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-4" + }, + "options": { + "description": "Checkbox list choices\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-4", + "type": "array", + "minItems": 1, + "items": { + "title": "checkbox list choice", + "description": "Checkbox list choice\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-4", + "type": "object", + "required": ["label"], + "properties": { + "label": { + "description": "A short checkbox list choice description\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-4", + "type": "string", + "minLength": 1, + "examples": ["Sample label"] + }, + "required": { + "description": "Specify whether a choice is required\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-4", + "type": "boolean", + "default": false + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + ] + } + }, + "properties": { + "name": { + "description": "An issue template name\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms#top-level-syntax", + "type": "string", + "minLength": 1, + "examples": ["Sample name"] + }, + "description": { + "description": "An issue template description\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms#top-level-syntax", + "type": "string", + "minLength": 1, + "examples": ["Sample description"] + }, + "body": { + "description": "An issue template body\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms#top-level-syntax", + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/form_item" + } + }, + "assignees": { + "description": "An issue template assignees\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms#top-level-syntax", + "oneOf": [ + { + "$ref": "#/definitions/assignee" + }, + { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/assignee" + } + } + ] + }, + "labels": { + "description": "An issue template labels\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms#top-level-syntax", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1, + "examples": [ + "Sample label", + "bug", + "documentation", + "duplicate", + "enhancement", + "good first issue", + "help wanted", + "invalid", + "question", + "wontfix" + ] + } + }, + "title": { + "description": "An issue template title\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms#top-level-syntax", + "type": "string", + "minLength": 1, + "examples": ["Sample title", "Bug: ", "Feature: "] + } + }, + "required": ["name", "description", "body"], + "title": "GitHub issue forms config file schema", + "type": "object" +} diff --git a/ci/schemas/pull-request-labeler-5.json b/ci/schemas/pull-request-labeler-5.json new file mode 100644 index 000000000000..22ad7955814f --- /dev/null +++ b/ci/schemas/pull-request-labeler-5.json @@ -0,0 +1,95 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/pull-request-labeler-5.json", + "$comment": "https://github.com/actions/labeler", + "$defs": { + "stringOrStringArray": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "match": { + "title": "Match", + "type": "object", + "properties": { + "changed-files": { + "type": "array", + "items": { + "type": "object", + "properties": { + "any-glob-to-any-file": { "$ref": "#/$defs/stringOrStringArray" }, + "any-glob-to-all-files": { + "$ref": "#/$defs/stringOrStringArray" + }, + "all-globs-to-any-file": { + "$ref": "#/$defs/stringOrStringArray" + }, + "all-globs-to-all-files": { + "$ref": "#/$defs/stringOrStringArray" + } + }, + "oneOf": [ + { "required": ["any-glob-to-any-file"] }, + { "required": ["any-glob-to-all-files"] }, + { "required": ["all-globs-to-any-file"] }, + { "required": ["all-globs-to-all-files"] } + ], + "additionalProperties": false + } + }, + "base-branch": { "$ref": "#/$defs/stringOrStringArray" }, + "head-branch": { "$ref": "#/$defs/stringOrStringArray" } + }, + "oneOf": [ + { "required": ["changed-files"] }, + { "required": ["base-branch"] }, + { "required": ["head-branch"] } + ], + "additionalProperties": false + } + }, + "additionalProperties": { + "title": "Label", + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "all": { + "title": "All", + "type": "array", + "items": { "$ref": "#/$defs/match" } + } + }, + "additionalProperties": false, + "required": ["all"] + }, + { + "type": "object", + "properties": { + "any": { + "title": "Any", + "type": "array", + "items": { "$ref": "#/$defs/match" } + } + }, + "additionalProperties": false, + "required": ["any"] + }, + { "$ref": "#/$defs/match" } + ] + } + }, + "description": "A GitHub Action for automatically labelling pull requests.", + "title": "Pull Request Labeler", + "type": "object" +} diff --git a/ci/schemas/vendor_schemas.py b/ci/schemas/vendor_schemas.py new file mode 100644 index 000000000000..a40e262e69f7 --- /dev/null +++ b/ci/schemas/vendor_schemas.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +""" +Download YAML Schemas for linting and validation. + +Since pre-commit CI doesn't have Internet access, we need to bundle these files +in the repo. +""" + +import os +import pathlib +import urllib.request + + +HERE = pathlib.Path(__file__).parent +SCHEMAS = [ + 'https://json.schemastore.org/appveyor.json', + 'https://json.schemastore.org/circleciconfig.json', + 'https://json.schemastore.org/github-funding.json', + 'https://json.schemastore.org/github-issue-config.json', + 'https://json.schemastore.org/github-issue-forms.json', + 'https://json.schemastore.org/codecov.json', + 'https://json.schemastore.org/pull-request-labeler-5.json', + 'https://github.com/microsoft/vscode-python/raw/' + 'main/schemas/conda-environment.json', +] + + +def print_progress(block_count, block_size, total_size): + size = block_count * block_size + if total_size != -1: + size = min(size, total_size) + width = 50 + percent = size / total_size * 100 + filled = int(percent // (100 // width)) + percent_str = '\N{Full Block}' * filled + '\N{Light Shade}' * (width - filled) + print(f'{percent_str} {size:6d} / {total_size:6d}', end='\r') + + +# First clean up existing files. +for json in HERE.glob('*.json'): + os.remove(json) + +for schema in SCHEMAS: + path = HERE / schema.rsplit('/', 1)[-1] + print(f'Downloading {schema} to {path}') + urllib.request.urlretrieve(schema, filename=path, reporthook=print_progress) + print() + # This seems weird, but it normalizes line endings to the current platform, + # so that Git doesn't complain about it. + path.write_text(path.read_text()) diff --git a/doc/Makefile b/doc/Makefile index 53b7c02b68fa..7eda39d87624 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -26,6 +26,7 @@ clean: rm -rf "$(SOURCEDIR)/savefig" rm -rf "$(SOURCEDIR)/sphinxext/__pycache__" rm -f $(SOURCEDIR)/_static/constrained_layout*.png + rm -f $(SOURCEDIR)/sg_execution_times.rst show: @python -c "import webbrowser; webbrowser.open_new_tab('file://$(shell pwd)/build/html/index.html')" diff --git a/doc/README.txt b/doc/README.txt index 0caf5e013c9b..c34dbd769712 100644 --- a/doc/README.txt +++ b/doc/README.txt @@ -9,35 +9,58 @@ See :file:`doc/devel/documenting_mpl.rst` for instructions to build the docs. Organization ------------ -This is the top level build directory for the Matplotlib -documentation. All of the documentation is written using sphinx, a -python documentation system built on top of ReST. This directory contains +This is the top level directory for the Matplotlib +documentation. All of the documentation is written using Sphinx, a +python documentation system based on reStructuredText. This directory contains the +following -* users - the user documentation, e.g., installation, plotting tutorials, -configuration tips, faq, explanations, etc. +Files +^^^^^ -* devel - documentation for Matplotlib developers +* index.rst - the top level include document (and landing page) for the Matplotlib docs -* api - placeholders to automatically generate the api documentation +* conf.py - the sphinx configuration -* tutorials, plot_types, and gallery - automatically -generated by sphinx-gallery from ``../tutorials``, ``../plot_types``, and -``../examples`` respectively (these are only present if docs have been -built locally). +* docutils.conf - htmnl output configuration -* thirdpartypackages - redirect to +* Makefile and make.bat - entry points for building the docs -* mpl_toolkits - documentation of individual toolkits that ship with - Matplotlib +* matplotlibrc - rcParam configuration for docs -* index.rst - the top level include document for Matplotlib docs +* missing-references.json - list of known missing/broken references -* conf.py - the sphinx configuration -* Makefile and make.bat - entry points for building the docs +Content folders +^^^^^^^^^^^^^^^ + +* api - templates for generating the api documentation -* _static - used by the sphinx build system +* devel - documentation for contributing to Matplotlib -* _templates - used by the sphinx build system +* project - about Matplotlib, e.g. mission, code of conduct, licenses, history, etc. + +* users - usage documentation, e.g., installation, tutorials, faq, explanations, etc. + +* thirdpartypackages - redirect to + +Build folders +^^^^^^^^^^^^^ + +* _static - supplementary files; e.g. images, CSS, etc. + +* _templates - Sphinx page templates * sphinxext - Sphinx extensions for the Matplotlib docs + +Symlinks +-------- + +During the documentation build, sphinx-gallery creates symlinks from the source folders +in `/galleries` to target_folders in '/doc'; therefore ensure that you are editing the +real files rather than the symbolic links. + +Source files -> symlink: +* galleries/tutorials -> doc/tutorials +* galleries/plot_types -> doc/plot_types +* galleries/examples -> doc/gallery +* galleries/users_explain -> doc/users/explain diff --git a/doc/_static/mpl.css b/doc/_static/mpl.css index 2b57f7608d26..aae167d1f6f8 100644 --- a/doc/_static/mpl.css +++ b/doc/_static/mpl.css @@ -3,35 +3,17 @@ --pst-color-link-hover: var(--pst-color-secondary); --sd-color-primary: var(--pst-color-primary); --sd-color-primary-text: var(--pst-color-text-base); + --sd-color-secondary: #ee9040; + --sd-color-success: #28a745; + --sd-color-dark: #323232; + --sd-color-danger: #dc3545; + --sd-color-light: #c9c9c9; } .simple li>p { margin: 0; } -/* Make announcement an error colour for unreleased documentation, and sticky. */ -#unreleased-message.bd-header-announcement { - border-bottom: solid var(--pst-color-danger-highlight); - color: var(--pst-color-danger-text); - font-weight: var(--pst-admonition-font-weight-heading); - position: sticky; - top: 0; - z-index: 1050; -} - -#unreleased-message.bd-header-announcement:after { - background-color: var(--pst-color-danger); - opacity: 1; -} - -#unreleased-message.bd-header-announcement a { - color: var(--pst-color-danger-text); -} - -#unreleased-message.bd-header-announcement + .bd-navbar { - top: 3rem; /* Minimum height of announcement header. */ -} - /* multi column TOC */ .contents ul { list-style-type: none; @@ -80,9 +62,13 @@ html[data-theme="dark"] .sphx-glr-thumbcontainer { background-color: rgb(63, 63, 63); } -/* workaround: the default padding decenters the image inside the frame */ -.sphx-glr-thumbcontainer .figure { - padding: 0; +/* Set a fixed height so that lazy loading does not change heights. Without a fixed + * height lazy loading of images interferes with anchor links: Clicking a link goes to + * a certain position, but then the loaded images add content and move the anchor to a + * different position. + */ +.sphx-glr-thumbcontainer img { + height: 112px; } /* hide download buttons in example headers diff --git a/doc/_static/switcher.json b/doc/_static/switcher.json index 75301fbb142a..3d712e4ff8e9 100644 --- a/doc/_static/switcher.json +++ b/doc/_static/switcher.json @@ -1,14 +1,20 @@ [ { - "name": "3.8 (stable)", + "name": "3.9 (stable)", "version": "stable", - "url": "https://matplotlib.org/stable/" + "url": "https://matplotlib.org/stable/", + "preferred": true }, { - "name": "3.9 (dev)", + "name": "3.10 (dev)", "version": "dev", "url": "https://matplotlib.org/devdocs/" }, + { + "name": "3.8", + "version": "3.8.4", + "url": "https://matplotlib.org/3.8.4/" + }, { "name": "3.7", "version": "3.7.5", diff --git a/doc/_static/zenodo_cache/10916799.svg b/doc/_static/zenodo_cache/10916799.svg new file mode 100644 index 000000000000..ca9c0a454251 --- /dev/null +++ b/doc/_static/zenodo_cache/10916799.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.10916799 + + + 10.5281/zenodo.10916799 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/11201097.svg b/doc/_static/zenodo_cache/11201097.svg new file mode 100644 index 000000000000..70f35a7a659f --- /dev/null +++ b/doc/_static/zenodo_cache/11201097.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.11201097 + + + 10.5281/zenodo.11201097 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/12652732.svg b/doc/_static/zenodo_cache/12652732.svg new file mode 100644 index 000000000000..cde5c5f37839 --- /dev/null +++ b/doc/_static/zenodo_cache/12652732.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.12652732 + + + 10.5281/zenodo.12652732 + + + \ No newline at end of file diff --git a/doc/_templates/autofunctions.rst b/doc/_templates/autofunctions.rst index 942731b46587..291b8eee2ede 100644 --- a/doc/_templates/autofunctions.rst +++ b/doc/_templates/autofunctions.rst @@ -14,7 +14,9 @@ Functions .. autosummary:: :template: autosummary.rst :toctree: + {% for item in functions %}{% if item not in ['plotting', 'colormaps'] %} {{ item }}{% endif %}{% endfor %} + {% endif %} {% endblock %} diff --git a/doc/_templates/automodule.rst b/doc/_templates/automodule.rst index 984c12e00d03..df3e8283f2f6 100644 --- a/doc/_templates/automodule.rst +++ b/doc/_templates/automodule.rst @@ -1,13 +1,5 @@ {{ fullname | escape | underline}} -{% if fullname in ['mpl_toolkits.axes_grid1.colorbar'] %} -.. To prevent problems with the autosummary for the colorbar doc - treat this separately (sphinx-doc/sphinx/issues/4874) -.. automodule:: {{ fullname }} - :members: - -{% else %} - .. automodule:: {{ fullname }} :no-members: :no-inherited-members: @@ -21,8 +13,10 @@ Classes .. autosummary:: :template: autosummary.rst :toctree: + {% for item in classes %}{% if item not in ['zip', 'map', 'reduce'] %} {{ item }}{% endif %}{% endfor %} + {% endif %} {% endblock %} @@ -38,6 +32,6 @@ Functions {% for item in functions %}{% if item not in ['zip', 'map', 'reduce'] %} {{ item }}{% endif %}{% endfor %} + {% endif %} {% endblock %} -{% endif %} diff --git a/doc/_templates/autosummary.rst b/doc/_templates/autosummary.rst index c5f90e87f016..824dbe5b9a4b 100644 --- a/doc/_templates/autosummary.rst +++ b/doc/_templates/autosummary.rst @@ -5,6 +5,7 @@ {% if objtype in ['class'] %} + .. auto{{ objtype }}:: {{ objname }} :show-inheritance: :special-members: __call__ @@ -16,11 +17,13 @@ {% if objtype in ['class', 'method', 'function'] %} {% if objname in ['AxesGrid', 'Scalable', 'HostAxes', 'FloatingAxes', - 'ParasiteAxesAuxTrans', 'ParasiteAxes'] %} +'ParasiteAxesAuxTrans', 'ParasiteAxes'] %} + .. Filter out the above aliases to other classes, as sphinx gallery creates no example file for those (sphinx-gallery/sphinx-gallery#365) {% else %} + .. minigallery:: {{module}}.{{objname}} :add-heading: diff --git a/doc/_templates/autosummary_class_only.rst b/doc/_templates/autosummary_class_only.rst index 6611f04f5c0d..d10f1b375fd3 100644 --- a/doc/_templates/autosummary_class_only.rst +++ b/doc/_templates/autosummary_class_only.rst @@ -5,6 +5,7 @@ {% if objtype in ['class'] %} + .. auto{{ objtype }}:: {{ objname }} :no-members: diff --git a/doc/_templates/empty_sidebar.html b/doc/_templates/empty_sidebar.html new file mode 100644 index 000000000000..2ebb29fad4dd --- /dev/null +++ b/doc/_templates/empty_sidebar.html @@ -0,0 +1 @@ + diff --git a/doc/api/animation_api.rst b/doc/api/animation_api.rst index df39b5942199..1233b482165d 100644 --- a/doc/api/animation_api.rst +++ b/doc/api/animation_api.rst @@ -18,6 +18,9 @@ Animation The easiest way to make a live animation in Matplotlib is to use one of the `Animation` classes. +.. seealso:: + - :ref:`animations` + .. inheritance-diagram:: matplotlib.animation.FuncAnimation matplotlib.animation.ArtistAnimation :parts: 1 diff --git a/doc/api/artist_api.rst b/doc/api/artist_api.rst index 3903bbd5924d..0ca3fb364c41 100644 --- a/doc/api/artist_api.rst +++ b/doc/api/artist_api.rst @@ -11,7 +11,7 @@ Inheritance Diagrams ==================== -.. inheritance-diagram:: matplotlib.axes._axes.Axes matplotlib.axes._base._AxesBase matplotlib.axis.Axis matplotlib.axis.Tick matplotlib.axis.XAxis matplotlib.axis.XTick matplotlib.axis.YAxis matplotlib.axis.YTick matplotlib.collections.AsteriskPolygonCollection matplotlib.collections.BrokenBarHCollection matplotlib.collections.CircleCollection matplotlib.collections.Collection matplotlib.collections.EllipseCollection matplotlib.collections.EventCollection matplotlib.collections.LineCollection matplotlib.collections.PatchCollection matplotlib.collections.PathCollection matplotlib.collections.PolyCollection matplotlib.collections.QuadMesh matplotlib.collections.RegularPolyCollection matplotlib.collections.StarPolygonCollection matplotlib.collections.TriMesh matplotlib.collections._CollectionWithSizes matplotlib.contour.ClabelText matplotlib.contour.ContourSet matplotlib.contour.QuadContourSet matplotlib.figure.FigureBase matplotlib.figure.Figure matplotlib.figure.SubFigure matplotlib.image.AxesImage matplotlib.image.BboxImage matplotlib.image.FigureImage matplotlib.image.NonUniformImage matplotlib.image.PcolorImage matplotlib.image._ImageBase matplotlib.legend.Legend matplotlib.lines.Line2D matplotlib.offsetbox.AnchoredOffsetbox matplotlib.offsetbox.AnchoredText matplotlib.offsetbox.AnnotationBbox matplotlib.offsetbox.AuxTransformBox matplotlib.offsetbox.DrawingArea matplotlib.offsetbox.HPacker matplotlib.offsetbox.OffsetBox matplotlib.offsetbox.OffsetImage matplotlib.offsetbox.PackerBase matplotlib.offsetbox.PaddedBox matplotlib.offsetbox.TextArea matplotlib.offsetbox.VPacker matplotlib.patches.Annulus matplotlib.patches.Arc matplotlib.patches.Arrow matplotlib.patches.Circle matplotlib.patches.CirclePolygon matplotlib.patches.ConnectionPatch matplotlib.patches.Ellipse matplotlib.patches.FancyArrow matplotlib.patches.FancyArrowPatch matplotlib.patches.FancyBboxPatch matplotlib.patches.Patch matplotlib.patches.PathPatch matplotlib.patches.Polygon matplotlib.patches.Rectangle matplotlib.patches.RegularPolygon matplotlib.patches.Shadow matplotlib.patches.StepPatch matplotlib.patches.Wedge matplotlib.projections.geo.AitoffAxes matplotlib.projections.geo.GeoAxes matplotlib.projections.geo.HammerAxes matplotlib.projections.geo.LambertAxes matplotlib.projections.geo.MollweideAxes matplotlib.projections.polar.PolarAxes matplotlib.projections.polar.RadialAxis matplotlib.projections.polar.RadialTick matplotlib.projections.polar.ThetaAxis matplotlib.projections.polar.ThetaTick matplotlib.quiver.Barbs matplotlib.quiver.Quiver matplotlib.quiver.QuiverKey matplotlib.spines.Spine matplotlib.table.Cell matplotlib.table.Table matplotlib.text.Annotation matplotlib.text.Text matplotlib.tri.TriContourSet +.. inheritance-diagram:: matplotlib.axes._axes.Axes matplotlib.axes._base._AxesBase matplotlib.axis.Axis matplotlib.axis.Tick matplotlib.axis.XAxis matplotlib.axis.XTick matplotlib.axis.YAxis matplotlib.axis.YTick matplotlib.collections.AsteriskPolygonCollection matplotlib.collections.CircleCollection matplotlib.collections.Collection matplotlib.collections.EllipseCollection matplotlib.collections.EventCollection matplotlib.collections.LineCollection matplotlib.collections.PatchCollection matplotlib.collections.PathCollection matplotlib.collections.PolyCollection matplotlib.collections.QuadMesh matplotlib.collections.RegularPolyCollection matplotlib.collections.StarPolygonCollection matplotlib.collections.TriMesh matplotlib.collections._CollectionWithSizes matplotlib.contour.ContourSet matplotlib.contour.QuadContourSet matplotlib.figure.FigureBase matplotlib.figure.Figure matplotlib.figure.SubFigure matplotlib.image.AxesImage matplotlib.image.BboxImage matplotlib.image.FigureImage matplotlib.image.NonUniformImage matplotlib.image.PcolorImage matplotlib.image._ImageBase matplotlib.legend.Legend matplotlib.lines.Line2D matplotlib.offsetbox.AnchoredOffsetbox matplotlib.offsetbox.AnchoredText matplotlib.offsetbox.AnnotationBbox matplotlib.offsetbox.AuxTransformBox matplotlib.offsetbox.DrawingArea matplotlib.offsetbox.HPacker matplotlib.offsetbox.OffsetBox matplotlib.offsetbox.OffsetImage matplotlib.offsetbox.PackerBase matplotlib.offsetbox.PaddedBox matplotlib.offsetbox.TextArea matplotlib.offsetbox.VPacker matplotlib.patches.Annulus matplotlib.patches.Arc matplotlib.patches.Arrow matplotlib.patches.Circle matplotlib.patches.CirclePolygon matplotlib.patches.ConnectionPatch matplotlib.patches.Ellipse matplotlib.patches.FancyArrow matplotlib.patches.FancyArrowPatch matplotlib.patches.FancyBboxPatch matplotlib.patches.Patch matplotlib.patches.PathPatch matplotlib.patches.Polygon matplotlib.patches.Rectangle matplotlib.patches.RegularPolygon matplotlib.patches.Shadow matplotlib.patches.StepPatch matplotlib.patches.Wedge matplotlib.projections.geo.AitoffAxes matplotlib.projections.geo.GeoAxes matplotlib.projections.geo.HammerAxes matplotlib.projections.geo.LambertAxes matplotlib.projections.geo.MollweideAxes matplotlib.projections.polar.PolarAxes matplotlib.projections.polar.RadialAxis matplotlib.projections.polar.RadialTick matplotlib.projections.polar.ThetaAxis matplotlib.projections.polar.ThetaTick matplotlib.quiver.Barbs matplotlib.quiver.Quiver matplotlib.quiver.QuiverKey matplotlib.spines.Spine matplotlib.table.Cell matplotlib.table.Table matplotlib.text.Annotation matplotlib.text.Text matplotlib.tri.TriContourSet :parts: 1 :private-bases: diff --git a/doc/api/axes_api.rst b/doc/api/axes_api.rst index 3457368fa51c..ac4e5bc4f536 100644 --- a/doc/api/axes_api.rst +++ b/doc/api/axes_api.rst @@ -335,6 +335,8 @@ Autoscaling and margins Axes.use_sticky_edges Axes.margins + Axes.get_xmargin + Axes.get_ymargin Axes.set_xmargin Axes.set_ymargin @@ -515,6 +517,9 @@ Interactive Axes.get_navigate_mode Axes.set_navigate_mode + Axes.get_forward_navigation_events + Axes.set_forward_navigation_events + Axes.start_pan Axes.drag_pan Axes.end_pan diff --git a/doc/api/axis_api.rst b/doc/api/axis_api.rst index a177f82a4a9a..17e892b99df8 100644 --- a/doc/api/axis_api.rst +++ b/doc/api/axis_api.rst @@ -112,6 +112,9 @@ Ticks, tick labels and Offset text Axis.axis_date + Axis.minorticks_off + Axis.minorticks_on + Data and view intervals ----------------------- diff --git a/doc/api/backend_registry_api.rst b/doc/api/backend_registry_api.rst new file mode 100644 index 000000000000..ca184c67d0a2 --- /dev/null +++ b/doc/api/backend_registry_api.rst @@ -0,0 +1,8 @@ +******************************** +``matplotlib.backends.registry`` +******************************** + +.. automodule:: matplotlib.backends.registry + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/figure_api.rst b/doc/api/figure_api.rst index 1beb0a701b26..2371e5a9a863 100644 --- a/doc/api/figure_api.rst +++ b/doc/api/figure_api.rst @@ -5,5 +5,314 @@ .. currentmodule:: matplotlib.figure .. automodule:: matplotlib.figure - :members: - :inherited-members: + :no-members: + :no-undoc-members: + +Figure +====== + +Figure class +------------ +.. autosummary:: + :toctree: _as_gen + :template: autosummary_class_only.rst + :nosignatures: + + Figure + + +Adding Axes and SubFigures +-------------------------- + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + Figure.add_axes + Figure.add_subplot + Figure.subplots + Figure.subplot_mosaic + Figure.add_gridspec + Figure.get_axes + Figure.axes + Figure.delaxes + Figure.subfigures + Figure.add_subfigure + +Saving +------ + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + Figure.savefig + + +Annotating +---------- + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + Figure.colorbar + Figure.legend + Figure.text + Figure.suptitle + Figure.get_suptitle + Figure.supxlabel + Figure.get_supxlabel + Figure.supylabel + Figure.get_supylabel + Figure.align_labels + Figure.align_xlabels + Figure.align_ylabels + Figure.align_titles + Figure.autofmt_xdate + + +Figure geometry +--------------- + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + Figure.set_size_inches + Figure.get_size_inches + Figure.set_figheight + Figure.get_figheight + Figure.set_figwidth + Figure.get_figwidth + Figure.dpi + Figure.set_dpi + Figure.set_dpi + +Subplot layout +-------------- + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + Figure.subplots_adjust + Figure.set_layout_engine + Figure.get_layout_engine + +Discouraged or deprecated +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + Figure.tight_layout + Figure.set_tight_layout + Figure.get_tight_layout + Figure.set_constrained_layout + Figure.get_constrained_layout + Figure.set_constrained_layout_pads + Figure.get_constrained_layout_pads + +Interactive +----------- + +.. seealso:: + + - :ref:`event-handling` + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + Figure.ginput + Figure.add_axobserver + Figure.waitforbuttonpress + Figure.pick + +Modifying appearance +-------------------- + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + Figure.set_frameon + Figure.get_frameon + Figure.set_linewidth + Figure.get_linewidth + Figure.set_facecolor + Figure.get_facecolor + Figure.set_edgecolor + Figure.get_edgecolor + +Adding and getting Artists +-------------------------- + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + Figure.add_artist + Figure.get_children + Figure.figimage + +Getting and modifying state +--------------------------- + +.. seealso:: + + - :ref:`interactive_figures` + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + Figure.clear + Figure.gca + Figure.sca + Figure.get_tightbbox + Figure.get_window_extent + Figure.show + Figure.set_canvas + Figure.draw + Figure.draw_without_rendering + Figure.draw_artist + +.. _figure-api-subfigure: + +SubFigure +========= + +Matplotlib has the concept of a `~.SubFigure`, which is a logical figure inside +a parent `~.Figure`. It has many of the same methods as the parent. See +:ref:`nested_axes_layouts`. + +.. plot:: + + fig = plt.figure(layout='constrained', figsize=(4, 2.5), facecolor='lightgoldenrodyellow') + + # Make two subfigures, left ones more narrow than right ones: + sfigs = fig.subfigures(1, 2, width_ratios=[0.8, 1]) + sfigs[0].set_facecolor('khaki') + sfigs[1].set_facecolor('lightsalmon') + + # Add subplots to left subfigure: + lax = sfigs[0].subplots(2, 1) + sfigs[0].suptitle('Left subfigure') + + # Add subplots to right subfigure: + rax = sfigs[1].subplots(1, 2) + sfigs[1].suptitle('Right subfigure') + + # suptitle for the main figure: + fig.suptitle('Figure') + +SubFigure class +--------------- + +.. autosummary:: + :toctree: _as_gen + :template: autosummary_class_only.rst + :nosignatures: + + SubFigure + +Adding Axes and SubFigures +-------------------------- +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + SubFigure.add_axes + SubFigure.add_subplot + SubFigure.subplots + SubFigure.subplot_mosaic + SubFigure.add_gridspec + SubFigure.delaxes + SubFigure.add_subfigure + SubFigure.subfigures + +Annotating +---------- + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + SubFigure.colorbar + SubFigure.legend + SubFigure.text + SubFigure.suptitle + SubFigure.get_suptitle + SubFigure.supxlabel + SubFigure.get_supxlabel + SubFigure.supylabel + SubFigure.get_supylabel + SubFigure.align_labels + SubFigure.align_xlabels + SubFigure.align_ylabels + SubFigure.align_titles + +Adding and getting Artists +-------------------------- + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + SubFigure.add_artist + SubFigure.get_children + +Modifying appearance +-------------------- + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + SubFigure.set_frameon + SubFigure.get_frameon + SubFigure.set_linewidth + SubFigure.get_linewidth + SubFigure.set_facecolor + SubFigure.get_facecolor + SubFigure.set_edgecolor + SubFigure.get_edgecolor + +Passthroughs +------------ + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + SubFigure.set_dpi + SubFigure.get_dpi + + +FigureBase parent class +======================= + +.. autoclass:: FigureBase + +Helper functions +================ + +.. autofunction:: figaspect diff --git a/doc/api/font_manager_api.rst b/doc/api/font_manager_api.rst index ba1d785ca939..1a1b06da1fa9 100644 --- a/doc/api/font_manager_api.rst +++ b/doc/api/font_manager_api.rst @@ -4,6 +4,7 @@ .. automodule:: matplotlib.font_manager :members: + :exclude-members: FontEntry :undoc-members: :show-inheritance: diff --git a/doc/api/gridspec_api.rst b/doc/api/gridspec_api.rst index 2b9f5a67784c..fe1137d94113 100644 --- a/doc/api/gridspec_api.rst +++ b/doc/api/gridspec_api.rst @@ -19,3 +19,4 @@ Classes SubplotSpec GridSpecBase GridSpecFromSubplotSpec + SubplotParams diff --git a/doc/api/index.rst b/doc/api/index.rst index e55a0ed3c5b2..70c3b5343e7a 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -126,6 +126,7 @@ Alphabetical list of modules: sphinxext_mathmpl_api.rst sphinxext_plot_directive_api.rst sphinxext_figmpl_directive_api.rst + sphinxext_roles.rst spines_api.rst style_api.rst table_api.rst diff --git a/doc/api/index_backend_api.rst b/doc/api/index_backend_api.rst index 6012f71c52a4..66009d86825d 100644 --- a/doc/api/index_backend_api.rst +++ b/doc/api/index_backend_api.rst @@ -17,6 +17,7 @@ backend_pdf_api.rst backend_pgf_api.rst backend_ps_api.rst + backend_registry_api.rst backend_qt_api.rst backend_svg_api.rst backend_tk_api.rst diff --git a/doc/api/prev_api_changes/api_changes_0.99.rst b/doc/api/prev_api_changes/api_changes_0.99.rst index 5d544eaec7f5..e03face0d075 100644 --- a/doc/api/prev_api_changes/api_changes_0.99.rst +++ b/doc/api/prev_api_changes/api_changes_0.99.rst @@ -7,7 +7,7 @@ Changes in 0.99 NumPy arrays. * User-generated colormaps can now be added to the set recognized - by :func:`matplotlib.cm.get_cmap`. Colormaps can be made the + by ``matplotlib.cm.get_cmap``. Colormaps can be made the default and applied to the current image using :func:`matplotlib.pyplot.set_cmap`. diff --git a/doc/api/prev_api_changes/api_changes_1.3.x.rst b/doc/api/prev_api_changes/api_changes_1.3.x.rst index 0aeb47815fcc..2601824ba7d1 100644 --- a/doc/api/prev_api_changes/api_changes_1.3.x.rst +++ b/doc/api/prev_api_changes/api_changes_1.3.x.rst @@ -7,7 +7,7 @@ API Changes in 1.3.x Changes in 1.3.1 ---------------- -It is rare that we make an API change in a bugfix release, however, +It is rare that we make an API change in a micro release, however, for 1.3.1 since 1.3.0 the following change was made: - ``text.Text.cached`` (used to cache font objects) has been made into a diff --git a/doc/api/prev_api_changes/api_changes_2.1.2.rst b/doc/api/prev_api_changes/api_changes_2.1.2.rst index 5eb6658e263e..92a72523443d 100644 --- a/doc/api/prev_api_changes/api_changes_2.1.2.rst +++ b/doc/api/prev_api_changes/api_changes_2.1.2.rst @@ -12,9 +12,9 @@ list of conditions was incomplete, didn't handle RGB tuples, didn't handle linewidths or linestyles etc. This logic did not exist in `.axes.Axes.legend`. It was included (erroneously) -in Matplotlib 2.1.1 when the legend argument parsing was unified -[#9324](https://github.com/matplotlib/matplotlib/pull/9324). This change -removes that check in `.axes.Axes.legend` again to restore the old behavior. +in Matplotlib 2.1.1 when the legend argument parsing was unified :ghpull:`9324`. +This change removes that check in `.axes.Axes.legend` again to restore the old +behavior. This logic has also been dropped from `.Figure.legend`, where it was previously undocumented. Repeated diff --git a/doc/api/prev_api_changes/api_changes_3.1.0.rst b/doc/api/prev_api_changes/api_changes_3.1.0.rst index 18f25d459200..5b06af781938 100644 --- a/doc/api/prev_api_changes/api_changes_3.1.0.rst +++ b/doc/api/prev_api_changes/api_changes_3.1.0.rst @@ -308,7 +308,7 @@ FreeType or libpng are not in the compiler or linker's default path, set the standard environment variables ``CFLAGS``/``LDFLAGS`` on Linux or OSX, or ``CL``/``LINK`` on Windows, to indicate the relevant paths. -See details in :doc:`/users/installing/index`. +See details in :doc:`/install/index`. Setting artist properties twice or more in the same call ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/api/prev_api_changes/api_changes_3.2.0/behavior.rst b/doc/api/prev_api_changes/api_changes_3.2.0/behavior.rst index 407494bdb53f..6c1960c4dfaf 100644 --- a/doc/api/prev_api_changes/api_changes_3.2.0/behavior.rst +++ b/doc/api/prev_api_changes/api_changes_3.2.0/behavior.rst @@ -294,10 +294,11 @@ Exception changes ~~~~~~~~~~~~~~~~~ Various APIs that raised a `ValueError` for incorrectly typed inputs now raise `TypeError` instead: `.backend_bases.GraphicsContextBase.set_clip_path`, -``blocking_input.BlockingInput.__call__``, `.cm.register_cmap`, `.dviread.DviFont`, -`.rcsetup.validate_hatch`, ``.rcsetup.validate_animation_writer_path``, `.spines.Spine`, -many classes in the :mod:`matplotlib.transforms` module and :mod:`matplotlib.tri` -package, and Axes methods that take a ``norm`` parameter. +``blocking_input.BlockingInput.__call__``, ``matplotlib.cm.register_cmap``, +`.dviread.DviFont`, `.rcsetup.validate_hatch`, +``.rcsetup.validate_animation_writer_path``, `.spines.Spine`, many classes in +the :mod:`matplotlib.transforms` module and :mod:`matplotlib.tri` package, and +Axes methods that take a ``norm`` parameter. If extra kwargs are passed to `.LogScale`, `TypeError` will now be raised instead of `ValueError`. diff --git a/doc/api/prev_api_changes/api_changes_3.3.0/deprecations.rst b/doc/api/prev_api_changes/api_changes_3.3.0/deprecations.rst index d9d79adcfbd5..256c33ed762f 100644 --- a/doc/api/prev_api_changes/api_changes_3.3.0/deprecations.rst +++ b/doc/api/prev_api_changes/api_changes_3.3.0/deprecations.rst @@ -55,7 +55,7 @@ Please pass capstyles ("miter", "round", "bevel") and joinstyles ("butt", Passing raw data to ``register_cmap()`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Passing raw data via parameters *data* and *lut* to `.register_cmap()` is +Passing raw data via parameters *data* and *lut* to ``matplotlib.cm.register_cmap()`` is deprecated. Instead, explicitly create a `.LinearSegmentedColormap` and pass it via the *cmap* parameter: ``register_cmap(cmap=LinearSegmentedColormap(name, data, lut))``. diff --git a/doc/api/prev_api_changes/api_changes_3.4.0/behaviour.rst b/doc/api/prev_api_changes/api_changes_3.4.0/behaviour.rst index df08097dba03..e35301c11986 100644 --- a/doc/api/prev_api_changes/api_changes_3.4.0/behaviour.rst +++ b/doc/api/prev_api_changes/api_changes_3.4.0/behaviour.rst @@ -203,7 +203,7 @@ time, not at draw time. Raise or warn on registering a colormap twice ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When using `matplotlib.cm.register_cmap` to register a user provided or +When using ``matplotlib.cm.register_cmap`` to register a user provided or third-party colormap it will now raise a `ValueError` if trying to over-write one of the built in colormaps and warn if trying to over write a user registered colormap. This may raise for user-registered colormaps in the diff --git a/doc/api/prev_api_changes/api_changes_3.4.2.rst b/doc/api/prev_api_changes/api_changes_3.4.2.rst index 2de543be37d6..34d760bf0941 100644 --- a/doc/api/prev_api_changes/api_changes_3.4.2.rst +++ b/doc/api/prev_api_changes/api_changes_3.4.2.rst @@ -7,7 +7,7 @@ Behaviour changes Rename first argument to ``subplot_mosaic`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Both `.FigureBase.subplot_mosaic`, and `.pyplot.subplot_mosaic` have had the +Both `.Figure.subplot_mosaic`, and `.pyplot.subplot_mosaic` have had the first position argument renamed from *layout* to *mosaic*. This is because we are considering to consolidate *constrained_layout* and *tight_layout* keyword arguments in the Figure creation functions of `.pyplot` into a single *layout* diff --git a/doc/api/prev_api_changes/api_changes_3.5.0/behaviour.rst b/doc/api/prev_api_changes/api_changes_3.5.0/behaviour.rst index 69e38270ca76..25f761ae39fa 100644 --- a/doc/api/prev_api_changes/api_changes_3.5.0/behaviour.rst +++ b/doc/api/prev_api_changes/api_changes_3.5.0/behaviour.rst @@ -4,7 +4,7 @@ Behaviour changes First argument to ``subplot_mosaic`` renamed ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Both `.FigureBase.subplot_mosaic`, and `.pyplot.subplot_mosaic` have had the +Both `.Figure.subplot_mosaic`, and `.pyplot.subplot_mosaic` have had the first positional argument renamed from *layout* to *mosaic*. As we have consolidated the *constrained_layout* and *tight_layout* keyword arguments in the Figure creation functions of `.pyplot` into a single *layout* keyword @@ -132,7 +132,7 @@ consistently exposes all the attributes and methods related to the line marker (:ghissue:`11358`). This makes it easy to change the marker features after instantiating a legend. -.. code:: +.. code-block:: python import matplotlib.pyplot as plt @@ -147,7 +147,7 @@ instantiating a legend. The former legend handler for Line2D objects has been renamed `.HandlerLine2DCompound`. To revert to the previous behaviour, one can use -.. code:: +.. code-block:: python import matplotlib.legend as mlegend from matplotlib.legend_handler import HandlerLine2DCompound diff --git a/doc/api/prev_api_changes/api_changes_3.5.0/deprecations.rst b/doc/api/prev_api_changes/api_changes_3.5.0/deprecations.rst index 7bb9009fbe77..d10da55a97f8 100644 --- a/doc/api/prev_api_changes/api_changes_3.5.0/deprecations.rst +++ b/doc/api/prev_api_changes/api_changes_3.5.0/deprecations.rst @@ -353,7 +353,7 @@ is thus deprecated as well. To test an installed copy, be sure to specify both ``matplotlib`` and ``mpl_toolkits`` with ``--pyargs``:: - python -m pytest --pyargs matplotlib.tests mpl_toolkits.tests + pytest --pyargs matplotlib.tests mpl_toolkits.tests See :ref:`testing` for more details. diff --git a/doc/api/prev_api_changes/api_changes_3.5.0/development.rst b/doc/api/prev_api_changes/api_changes_3.5.0/development.rst index 2db21237a699..b42e6eff3423 100644 --- a/doc/api/prev_api_changes/api_changes_3.5.0/development.rst +++ b/doc/api/prev_api_changes/api_changes_3.5.0/development.rst @@ -77,6 +77,6 @@ In order to avoid conflicting with the use of :file:`setup.cfg` by ``setup.cfg`` to ``mplsetup.cfg``. The :file:`setup.cfg.template` has been correspondingly been renamed to :file:`mplsetup.cfg.template`. -Note that the path to this configuration file can still be set via the -:envvar:`MPLSETUPCFG` environment variable, which allows one to keep using the -same file before and after this change. +Note that the path to this configuration file can still be set via the ``MPLSETUPCFG`` +environment variable, which allows one to keep using the same file before and after this +change. diff --git a/doc/api/prev_api_changes/api_changes_3.6.0/behaviour.rst b/doc/api/prev_api_changes/api_changes_3.6.0/behaviour.rst index a35584b04961..91802692ebb4 100644 --- a/doc/api/prev_api_changes/api_changes_3.6.0/behaviour.rst +++ b/doc/api/prev_api_changes/api_changes_3.6.0/behaviour.rst @@ -4,10 +4,10 @@ Behaviour changes ``plt.get_cmap`` and ``matplotlib.cm.get_cmap`` return a copy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Formerly, `~.pyplot.get_cmap` and `.cm.get_cmap` returned a global version of a -`.Colormap`. This was prone to errors as modification of the colormap would -propagate from one location to another without warning. Now, a new copy of the -colormap is returned. +Formerly, `~.pyplot.get_cmap` and ``matplotlib.cm.get_cmap`` returned a global version +of a `.Colormap`. This was prone to errors as modification of the colormap would +propagate from one location to another without warning. Now, a new copy of the colormap +is returned. Large ``imshow`` images are now downsampled ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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 b3b80f839166..2409eb2a5dd0 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 @@ -20,10 +20,10 @@ also be based on ``mpl_toolkits.axisartist``. This behavior is consistent with ``plt.get_cmap`` and ``matplotlib.cm.get_cmap`` return a copy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Formerly, `~.pyplot.get_cmap` and `.cm.get_cmap` returned a global version of a -`.Colormap`. This was prone to errors as modification of the colormap would -propagate from one location to another without warning. Now, a new copy of the -colormap is returned. +Formerly, `~.pyplot.get_cmap` and ``matplotlib.cm.get_cmap`` returned a global version +of a `.Colormap`. This was prone to errors as modification of the colormap would +propagate from one location to another without warning. Now, a new copy of the colormap +is returned. ``TrapezoidMapTriFinder`` uses different random number generator ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -51,7 +51,7 @@ Now, the halfrange remains fixed when vcenter is changed, and **vmin** and For example, this is what the values were when changing vcenter previously. -.. code-block:: +.. code-block:: python norm = CenteredNorm(vcenter=0, halfrange=1) # Move vcenter up by one @@ -61,7 +61,7 @@ For example, this is what the values were when changing vcenter previously. and now, with that same example -.. code-block:: +.. code-block:: python norm = CenteredNorm(vcenter=0, halfrange=1) norm.vcenter = 1 diff --git a/doc/api/prev_api_changes/api_changes_3.7.0/removals.rst b/doc/api/prev_api_changes/api_changes_3.7.0/removals.rst index 76ce9ff71819..03239be31057 100644 --- a/doc/api/prev_api_changes/api_changes_3.7.0/removals.rst +++ b/doc/api/prev_api_changes/api_changes_3.7.0/removals.rst @@ -185,7 +185,7 @@ is thus removed as well. To test an installed copy, be sure to specify both ``matplotlib`` and ``mpl_toolkits`` with ``--pyargs``:: - python -m pytest --pyargs matplotlib.tests mpl_toolkits.tests + pytest --pyargs matplotlib.tests mpl_toolkits.tests See :ref:`testing` for more details. diff --git a/doc/api/prev_api_changes/api_changes_3.8.0/behaviour.rst b/doc/api/prev_api_changes/api_changes_3.8.0/behaviour.rst index fd38708c9243..3476a05394df 100644 --- a/doc/api/prev_api_changes/api_changes_3.8.0/behaviour.rst +++ b/doc/api/prev_api_changes/api_changes_3.8.0/behaviour.rst @@ -123,7 +123,7 @@ Default antialiasing behavior changes for ``Text`` and ``Annotation`` ``matplotlib.pyplot.annotate()`` and ``matplotlib.pyplot.text()`` now support parameter *antialiased* when initializing. Examples: -.. code-block:: +.. code-block:: python mpl.text.Text(.5, .5, "foo\nbar", antialiased=True) plt.text(0.5, 0.5, '6 inches x 2 inches', antialiased=True) diff --git a/doc/api/prev_api_changes/api_changes_3.9.0.rst b/doc/api/prev_api_changes/api_changes_3.9.0.rst new file mode 100644 index 000000000000..8bd2628c90dc --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.9.0.rst @@ -0,0 +1,14 @@ +API Changes for 3.9.0 +===================== + +.. contents:: + :local: + :depth: 1 + +.. include:: /api/prev_api_changes/api_changes_3.9.0/behaviour.rst + +.. include:: /api/prev_api_changes/api_changes_3.9.0/deprecations.rst + +.. include:: /api/prev_api_changes/api_changes_3.9.0/removals.rst + +.. include:: /api/prev_api_changes/api_changes_3.9.0/development.rst diff --git a/doc/api/prev_api_changes/api_changes_3.9.0/behaviour.rst b/doc/api/prev_api_changes/api_changes_3.9.0/behaviour.rst new file mode 100644 index 000000000000..498dfb766922 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.9.0/behaviour.rst @@ -0,0 +1,120 @@ +Behaviour Changes +----------------- + +plot() shorthand format interprets "Cn" (n>9) as a color-cycle color +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Previously, ``plot(..., "-C11")`` would be interpreted as requesting a plot using +linestyle "-", color "C1" (color #1 of the color cycle), and marker "1" ("tri-down"). +It is now interpreted as requesting linestyle "-" and color "C11" (color #11 of the +color cycle). + +It is recommended to pass ambiguous markers (such as "1") explicitly using the *marker* +keyword argument. If the shorthand form is desired, such markers can also be +unambiguously set by putting them *before* the color string. + +Legend labels for ``plot`` +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Previously if a sequence was passed to the *label* parameter of `~.Axes.plot` when +plotting a single dataset, the sequence was automatically cast to string for the legend +label. Now, if the sequence has only one element, that element will be the legend label. +To keep the old behavior, cast the sequence to string before passing. + +Boxplots now ignore masked data points +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +`~matplotlib.axes.Axes.boxplot` and `~matplotlib.cbook.boxplot_stats` now ignore any +masked points in the input data. + +``axhspan`` and ``axvspan`` now return ``Rectangle``\s, not ``Polygon``\s +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This change allows using `~.Axes.axhspan` to draw an annulus on polar axes. + +This change also affects other elements built via `~.Axes.axhspan` and `~.Axes.axvspan`, +such as ``Slider.poly``. + +Improved handling of pan/zoom events of overlapping Axes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The forwarding of pan/zoom events is now determined by the visibility of the +background-patch (e.g. ``ax.patch.get_visible()``) and by the ``zorder`` of the axes. + +- Axes with a visible patch capture the event and do not pass it on to axes below. Only + the Axes with the highest ``zorder`` that contains the event is triggered (if there + are multiple Axes with the same ``zorder``, the last added Axes counts) +- Axes with an invisible patch are also invisible to events and they are passed on to + the axes below. + +To override the default behavior and explicitly set whether an Axes should forward +navigation events, use `.Axes.set_forward_navigation_events`. + +``loc='best'`` for ``legend`` now considers ``Text`` and ``PolyCollections`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The location selection ``legend`` now considers the existence of ``Text`` and +``PolyCollections`` in the ``badness`` calculation. + +Note: The ``best`` option can already be quite slow for plots with large amounts of +data. For ``PolyCollections``, it only considers the ``Path`` of ``PolyCollections`` and +not the enclosed area when checking for overlap to reduce additional latency. However, +it can still be quite slow when there are large amounts of ``PolyCollections`` in the +plot to check for. + +Exception when not passing a Bbox to BboxTransform*-classes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The exception when not passing a Bbox to BboxTransform*-classes that expect one, e.g., +`~matplotlib.transforms.BboxTransform` has changed from ``ValueError`` to ``TypeError``. + +*loc* parameter of ``Cell`` no longer accepts ``None`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The default value of the *loc* parameter has been changed from ``None`` to ``right``, +which already was the default location. The behavior of `.Cell` didn't change when +called without an explicit *loc* parameter. + +``ContourLabeler.add_label`` now respects *use_clabeltext* +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +... and sets `.Text.set_transform_rotates_text` accordingly. + +``Line2D`` +^^^^^^^^^^ + +When creating a Line2D or using `.Line2D.set_xdata` and `.Line2D.set_ydata`, +passing x/y data as non sequence is now an error. + +``ScalarMappable``\s auto-scale their norm when an array is set +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Collections previously deferred auto-scaling of the norm until draw time. This has been +changed to scale the norm whenever the first array is set to align with the docstring +and reduce unexpected behavior when accessing the norm before drawing. + +``SubplotParams`` moved from ``matplotlib.figure`` to ``matplotlib.gridspec`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +It is still importable from ``matplotlib.figure``, so does not require any changes to +existing code. + +``PowerNorm`` no longer clips values below vmin +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When ``clip=False`` is set (the default) on `~matplotlib.colors.PowerNorm`, values below +``vmin`` are now linearly normalised. Previously they were clipped to zero. This fixes +issues with the display of colorbars associated with a power norm. + +Image path semantics of toolmanager-based tools +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Previously, MEP22 ("toolmanager-based") Tools would try to load their icon +(``tool.image``) relative to the current working directory, or, as a fallback, from +Matplotlib's own image directory. Because both approaches are problematic for +third-party tools (the end-user may change the current working directory at any time, +and third-parties cannot add new icons in Matplotlib's image directory), this behavior +is deprecated; instead, ``tool.image`` is now interpreted relative to the directory +containing the source file where the ``Tool.image`` class attribute is defined. +(Defining ``tool.image`` as an absolute path also works and is compatible with both the +old and the new semantics.) diff --git a/doc/api/prev_api_changes/api_changes_3.9.0/deprecations.rst b/doc/api/prev_api_changes/api_changes_3.9.0/deprecations.rst new file mode 100644 index 000000000000..00469459d20a --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.9.0/deprecations.rst @@ -0,0 +1,93 @@ +Deprecations +------------ + +``plot_date`` +^^^^^^^^^^^^^ + +Use of `~.Axes.plot_date` has been discouraged since Matplotlib 3.5 and the function is +now formally deprecated. + +- ``datetime``-like data should directly be plotted using `~.Axes.plot`. +- If you need to plot plain numeric data as :ref:`date-format` or need to set a + timezone, call ``ax.xaxis.axis_date`` / ``ax.yaxis.axis_date`` before `~.Axes.plot`. + See `.Axis.axis_date`. + +Legend labels for ``plot`` +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Previously if a sequence was passed to the *label* parameter of `~.Axes.plot` when +plotting a single dataset, the sequence was automatically cast to string for the legend +label. This behavior is now deprecated and in future will error if the sequence length +is not one (consistent with multi-dataset behavior, where the number of elements must +match the number of datasets). To keep the old behavior, cast the sequence to string +before passing. + +``boxplot`` tick labels +^^^^^^^^^^^^^^^^^^^^^^^ + +The parameter *labels* has been renamed to *tick_labels* for clarity and consistency +with `~.Axes.bar`. + +Mixing positional and keyword arguments for ``legend`` handles and labels +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This previously only raised a warning, but is now formally deprecated. If passing +*handles* and *labels*, they must be passed either both positionally or both as keyword. + +Applying theta transforms in ``PolarTransform`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Applying theta transforms in `~matplotlib.projections.polar.PolarTransform` and +`~matplotlib.projections.polar.InvertedPolarTransform` is deprecated, and will be +removed in a future version of Matplotlib. This is currently the default behaviour when +these transforms are used externally, but only takes affect when: + +- An axis is associated with the transform. +- The axis has a non-zero theta offset or has theta values increasing in a clockwise + direction. + +To silence this warning and adopt future behaviour, set +``apply_theta_transforms=False``. If you need to retain the behaviour where theta values +are transformed, chain the ``PolarTransform`` with a `~matplotlib.transforms.Affine2D` +transform that performs the theta shift and/or sign shift. + +*interval* parameter of ``TimerBase.start`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Setting the timer *interval* while starting it is deprecated. The interval can be +specified instead in the timer constructor, or by setting the ``timer.interval`` +attribute. + +*nth_coord* parameter to axisartist helpers for fixed axis +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Helper APIs in `.axisartist` for generating a "fixed" axis on rectilinear axes +(`.FixedAxisArtistHelperRectilinear`) no longer take a *nth_coord* parameter, as that +parameter is entirely inferred from the (required) *loc* parameter and having +inconsistent *nth_coord* and *loc* is an error. + +For curvilinear axes, the *nth_coord* parameter remains supported (it affects the +*ticks*, not the axis position itself), but that parameter will become keyword-only, for +consistency with the rectilinear case. + +``rcsetup.interactive_bk``, ``rcsetup.non_interactive_bk`` and ``rcsetup.all_backends`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +... are deprecated and replaced by ``matplotlib.backends.backend_registry.list_builtin`` +with the following arguments + +- ``matplotlib.backends.BackendFilter.INTERACTIVE`` +- ``matplotlib.backends.BackendFilter.NON_INTERACTIVE`` +- ``None`` + +respectively. + +Miscellaneous deprecations +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- ``backend_ps.get_bbox_header`` is considered an internal helper +- ``BboxTransformToMaxOnly``; if you rely on this, please make a copy of the code +- ``ContourLabeler.add_label_clabeltext`` +- ``TransformNode.is_bbox``; instead check the object using ``isinstance(..., + BboxBase)`` +- ``GridHelperCurveLinear.get_tick_iterator`` diff --git a/doc/api/prev_api_changes/api_changes_3.9.0/development.rst b/doc/api/prev_api_changes/api_changes_3.9.0/development.rst new file mode 100644 index 000000000000..c16e8e98ecc4 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.9.0/development.rst @@ -0,0 +1,84 @@ +Development changes +------------------- + +Build system ported to Meson +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The build system of Matplotlib has been ported from setuptools to `meson-python +`_ and `Meson `_. +Consequently, there have been a few changes for development and packaging purposes. + +1. Installation by ``pip`` of packages with ``pyproject.toml`` use `build isolation + `_ + by default, which interferes with editable installation. Thus for developers using + editable installs, it is now necessary to pass the ``--no-build-isolation`` flag to + ``pip install``. This means that all build-time requirements must be available in the + environment for an editable install. +2. Build configuration has moved from a custom :file:`mplsetup.cfg` (also configurable + via ``MPLSETUP`` environment variable) to Meson options. These may be specified using + `meson-python's build config settings + `_ + for ``setup-args``. See :file:`meson_options.txt` for all options. For example, a + :file:`mplsetup.cfg` containing the following:: + + [rc_options] + backend=Agg + + [libs] + system_qhull = True + + may be replaced by passing the following arguments to ``pip``:: + + --config-settings=setup-args="-DrcParams-backend=Agg" + --config-settings=setup-args="-Dsystem-qhull=true" + + Note that you must use ``pip`` >= 23.1 in order to pass more than one setting. +3. Relatedly, Meson's `builtin options `_ + are now used instead of custom options, e.g., the LTO option is now ``b_lto``. +4. On Windows, Meson activates a Visual Studio environment automatically. However, it + will not do so if another compiler is available. See `Meson's documentation + `_ if you wish to + change the priority of chosen compilers. +5. Installation of test data was previously controlled by :file:`mplsetup.cfg`, but has + now been moved to Meson's install tags. To install test data, add the ``tests`` tag + to the requested install (be sure to include the existing tags as below):: + + --config-settings=install-args="--tags=data,python-runtime,runtime,tests" +6. Checking typing stubs with ``stubtest`` does not work easily with editable install. + For the time being, we suggest using a normal (non-editable) install if you wish to + run ``stubtest``. + +Increase to minimum supported versions of dependencies +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +For Matplotlib 3.9, the :ref:`minimum supported versions ` are being +bumped: + ++------------+-----------------+---------------+ +| Dependency | min in mpl3.8 | min in mpl3.9 | ++============+=================+===============+ +| NumPy | 1.21.0 | 1.23.0 | ++------------+-----------------+---------------+ +| setuptools | 42 | 64 | ++------------+-----------------+---------------+ + +This is consistent with our :ref:`min_deps_policy` and `SPEC 0 +`__. + +To comply with requirements of ``setuptools_scm``, the minimum version of ``setuptools`` +has been increased from 42 to 64. + +Extensions require C++17 +^^^^^^^^^^^^^^^^^^^^^^^^ + +Matplotlib now requires a compiler that supports C++17 in order to build its extensions. +According to `SciPy's analysis +`_, this +should be available on all supported platforms. + +Windows on ARM64 support +^^^^^^^^^^^^^^^^^^^^^^^^ + +Windows on ARM64 now bundles FreeType 2.6.1 instead of 2.11.1 when building from source. +This may cause small changes to text rendering, but should become consistent with all +other platforms. diff --git a/doc/api/prev_api_changes/api_changes_3.9.0/removals.rst b/doc/api/prev_api_changes/api_changes_3.9.0/removals.rst new file mode 100644 index 000000000000..b9aa03cfbf92 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.9.0/removals.rst @@ -0,0 +1,159 @@ +Removals +-------- + +Top-level cmap registration and access functions in ``mpl.cm`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +As part of the `multi-step refactoring of colormap registration +`_, the following functions have +been removed: + +- ``matplotlib.cm.get_cmap``; use ``matplotlib.colormaps[name]`` instead if you have a + `str`. + + Use `matplotlib.cm.ColormapRegistry.get_cmap` if you have a `str`, `None` or a + `matplotlib.colors.Colormap` object that you want to convert to a `.Colormap` object. +- ``matplotlib.cm.register_cmap``; use `matplotlib.colormaps.register + <.ColormapRegistry.register>` instead. +- ``matplotlib.cm.unregister_cmap``; use `matplotlib.colormaps.unregister + <.ColormapRegistry.unregister>` instead. +- ``matplotlib.pyplot.register_cmap``; use `matplotlib.colormaps.register + <.ColormapRegistry.register>` instead. + +The `matplotlib.pyplot.get_cmap` function will stay available for backward +compatibility. + +Contour labels +^^^^^^^^^^^^^^ + +``contour.ClabelText`` and ``ContourLabeler.set_label_props`` are removed. Use +``Text(..., transform_rotates_text=True)`` as a replacement for +``contour.ClabelText(...)`` and ``text.set(text=text, color=color, +fontproperties=labeler.labelFontProps, clip_box=labeler.axes.bbox)`` as a replacement +for the ``ContourLabeler.set_label_props(label, text, color)``. + +The ``labelFontProps``, ``labelFontSizeList``, and ``labelTextsList`` attributes of +`.ContourLabeler` have been removed. Use the ``labelTexts`` attribute and the font +properties of the corresponding text objects instead. + +``num2julian``, ``julian2num`` and ``JULIAN_OFFSET`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +... of the `.dates` module are removed without replacements. These were undocumented and +not exported. + +Julian dates in Matplotlib were calculated from a Julian date epoch: ``jdate = (date - +np.datetime64(EPOCH)) / np.timedelta64(1, 'D')``. Conversely, a Julian date was +converted to datetime as ``date = np.timedelta64(int(jdate * 24 * 3600), 's') + +np.datetime64(EPOCH)``. Matplotlib was using ``EPOCH='-4713-11-24T12:00'`` so that +2000-01-01 at 12:00 is 2_451_545.0 (see https://en.wikipedia.org/wiki/Julian_day). + +``offsetbox`` methods +^^^^^^^^^^^^^^^^^^^^^ + +``offsetbox.bbox_artist`` is removed. This was just a wrapper to call +`.patches.bbox_artist` if a flag is set in the file, so use that directly if you need +the behavior. + +``OffsetBox.get_extent_offsets`` and ``OffsetBox.get_extent`` are removed; these methods +are also removed on all subclasses of `.OffsetBox`. To get the offsetbox extents, +instead of ``get_extent``, use `.OffsetBox.get_bbox`, which directly returns a `.Bbox` +instance. To also get the child offsets, instead of ``get_extent_offsets``, separately +call `~.OffsetBox.get_offset` on each children after triggering a draw. + +``parse_fontconfig_pattern`` raises on unknown constant names +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Previously, in a fontconfig pattern like ``DejaVu Sans:foo``, the unknown ``foo`` +constant name would be silently ignored. This now raises an error. + +``tri`` submodules +^^^^^^^^^^^^^^^^^^ + +The ``matplotlib.tri.*`` submodules are removed. All functionality is available in +``matplotlib.tri`` directly and should be imported from there. + +Widget API +^^^^^^^^^^ + +- ``CheckButtons.rectangles`` and ``CheckButtons.lines`` are removed; `.CheckButtons` + now draws itself using `~.Axes.scatter`. +- ``RadioButtons.circles`` is removed; `.RadioButtons` now draws itself using + `~.Axes.scatter`. +- ``MultiCursor.needclear`` is removed with no replacement. +- The unused parameter *x* to ``TextBox.begin_typing`` was a required argument, and is + now removed. + +Most arguments to widgets have been made keyword-only +""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Passing all but the very few first arguments positionally in the constructors of Widgets +is now keyword-only. In general, all optional arguments are keyword-only. + +``Axes3D`` API +^^^^^^^^^^^^^^ + +- ``Axes3D.unit_cube``, ``Axes3D.tunit_cube``, and ``Axes3D.tunit_edges`` are removed + without replacement. +- ``axes3d.vvec``, ``axes3d.eye``, ``axes3d.sx``, and ``axes3d.sy`` are removed without + replacement. + +Inconsistent *nth_coord* and *loc* passed to ``_FixedAxisArtistHelperBase`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The value of the *nth_coord* parameter of ``_FixedAxisArtistHelperBase`` and its +subclasses is now inferred from the value of *loc*; passing inconsistent values (e.g., +requesting a "top y axis" or a "left x axis") has no more effect. + +Passing undefined *label_mode* to ``Grid`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +... is no longer allowed. This includes `mpl_toolkits.axes_grid1.axes_grid.Grid`, +`mpl_toolkits.axes_grid1.axes_grid.AxesGrid`, and +`mpl_toolkits.axes_grid1.axes_grid.ImageGrid` as well as the corresponding classes +imported from `mpl_toolkits.axisartist.axes_grid`. + +Pass ``label_mode='keep'`` instead to get the previous behavior of not modifying labels. + +``draw_gouraud_triangle`` +^^^^^^^^^^^^^^^^^^^^^^^^^ + +... is removed. Use `~.RendererBase.draw_gouraud_triangles` instead. + +A ``draw_gouraud_triangle`` call in a custom `~matplotlib.artist.Artist` can readily be +replaced as:: + + self.draw_gouraud_triangles(gc, points.reshape((1, 3, 2)), + colors.reshape((1, 3, 4)), trans) + +A `~.RendererBase.draw_gouraud_triangles` method can be implemented from an +existing ``draw_gouraud_triangle`` method as:: + + transform = transform.frozen() + for tri, col in zip(triangles_array, colors_array): + self.draw_gouraud_triangle(gc, tri, col, transform) + +Miscellaneous removals +^^^^^^^^^^^^^^^^^^^^^^ + +The following items have previously been replaced, and are now removed: + +- *ticklabels* parameter of ``matplotlib.axis.Axis.set_ticklabels`` has been renamed to + *labels*. +- ``Barbs.barbs_doc`` and ``Quiver.quiver_doc`` are removed. These are the doc-strings + and should not be accessible as a named class member, but as normal doc-strings would. +- ``collections.PolyCollection.span_where`` and ``collections.BrokenBarHCollection``; + use ``fill_between`` instead. +- ``Legend.legendHandles`` was undocumented and has been renamed to ``legend_handles``. + +The following items have been removed without replacements: + +- The attributes ``repeat`` of `.TimedAnimation` and subclasses and ``save_count`` of + `.FuncAnimation` are considered private and removed. +- ``matplotlib.backend.backend_agg.BufferRegion.to_string`` +- ``matplotlib.backend.backend_agg.BufferRegion.to_string_argb`` +- ``matplotlib.backends.backend_ps.PsBackendHelper`` +- ``matplotlib.backends.backend_webagg.ServerThread`` +- *raw* parameter of `.GridSpecBase.get_grid_positions` +- ``matplotlib.patches.ConnectionStyle._Base.SimpleEvent`` +- ``passthru_pt`` attribute of ``mpl_toolkits.axisartist.AxisArtistHelper`` diff --git a/doc/api/prev_api_changes/api_changes_3.9.1.rst b/doc/api/prev_api_changes/api_changes_3.9.1.rst new file mode 100644 index 000000000000..4a9a1fc6669c --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.9.1.rst @@ -0,0 +1,13 @@ +API Changes for 3.9.1 +===================== + +Development +----------- + +Documentation-specific custom Sphinx roles are now semi-public +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For third-party packages that derive types from Matplotlib, our use of custom roles may +prevent Sphinx from building their docs. These custom Sphinx roles are now public solely +for the purposes of use within projects that derive from Matplotlib types. See +:mod:`matplotlib.sphinxext.roles` for details. diff --git a/doc/api/prev_api_changes/api_changes_3.9.2.rst b/doc/api/prev_api_changes/api_changes_3.9.2.rst new file mode 100644 index 000000000000..4c2a69634502 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.9.2.rst @@ -0,0 +1,16 @@ +API Changes for 3.9.2 +===================== + +Development +----------- + +Windows wheel runtime bundling made static +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In 3.7.0, the MSVC runtime DLL was bundled in wheels to enable importing Matplotlib on +systems that do not have it installed. However, this could cause inconsistencies with +other wheels that did the same, and trigger random crashes depending on import order. See +`this issue `_ for further +details. + +Since 3.9.2, wheels now bundle the MSVC runtime DLL statically to avoid such issues. diff --git a/doc/api/sphinxext_roles.rst b/doc/api/sphinxext_roles.rst new file mode 100644 index 000000000000..99959ff05d14 --- /dev/null +++ b/doc/api/sphinxext_roles.rst @@ -0,0 +1,7 @@ +============================== +``matplotlib.sphinxext.roles`` +============================== + +.. automodule:: matplotlib.sphinxext.roles + :no-undoc-members: + :private-members: _rcparam_role, _mpltype_role diff --git a/doc/api/toolkits/mplot3d/axes3d.rst b/doc/api/toolkits/mplot3d/axes3d.rst index f6d8e2529896..877e47b7e93a 100644 --- a/doc/api/toolkits/mplot3d/axes3d.rst +++ b/doc/api/toolkits/mplot3d/axes3d.rst @@ -92,12 +92,22 @@ Axis limits and direction get_zaxis get_xlim + set_xlim get_ylim + set_ylim get_zlim set_zlim get_w_lims + invert_xaxis + xaxis_inverted + invert_yaxis + yaxis_inverted invert_zaxis zaxis_inverted + get_xbound + set_xbound + get_ybound + set_ybound get_zbound set_zbound @@ -137,6 +147,7 @@ Autoscaling and margins :template: autosummary.rst :nosignatures: + get_zmargin set_zmargin margins autoscale @@ -266,9 +277,6 @@ Aliases and deprecated methods set_zlim3d stem3D text3D - tunit_cube - tunit_edges - unit_cube Other diff --git a/doc/conf.py b/doc/conf.py index ab9821a1a086..8036edec9989 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -22,6 +22,7 @@ from urllib.parse import urlsplit, urlunsplit import warnings +from packaging.version import parse as parse_version import sphinx import yaml @@ -36,6 +37,14 @@ # are we running circle CI? CIRCLECI = 'CIRCLECI' in os.environ +# are we deploying this build to matplotlib.org/devdocs? +# This is a copy of the logic in .circleci/deploy-docs.sh +DEVDOCS = ( + CIRCLECI and + (os.environ.get("CIRCLE_PROJECT_USERNAME") == "matplotlib") and + (os.environ.get("CIRCLE_BRANCH") == "main") and + (not os.environ.get("CIRCLE_PULL_REQUEST", "").startswith( + "https://github.com/matplotlib/matplotlib/pull"))) def _parse_skip_subdirs_file(): @@ -47,8 +56,9 @@ def _parse_skip_subdirs_file(): but you can skip subdirectories of 'users'. Doing this can make partial builds very fast. """ - default_skip_subdirs = ['users/prev_whats_new/*', 'api/*', 'gallery/*', - 'tutorials/*', 'plot_types/*', 'devel/*'] + default_skip_subdirs = [ + 'users/prev_whats_new/*', 'users/explain/*', 'api/*', 'gallery/*', + 'tutorials/*', 'plot_types/*', 'devel/*'] try: with open(".mpl_skip_subdirs.yaml", 'r') as fin: print('Reading subdirectories to skip from', @@ -106,9 +116,9 @@ def _parse_skip_subdirs_file(): 'sphinx_gallery.gen_gallery', 'matplotlib.sphinxext.mathmpl', 'matplotlib.sphinxext.plot_directive', + 'matplotlib.sphinxext.roles', 'matplotlib.sphinxext.figmpl_directive', 'sphinxcontrib.inkscapeconverter', - 'sphinxext.custom_roles', 'sphinxext.github', 'sphinxext.math_symbol_table', 'sphinxext.missing_references', @@ -117,10 +127,14 @@ def _parse_skip_subdirs_file(): 'sphinxext.redirect_from', 'sphinx_copybutton', 'sphinx_design', + 'sphinx_tags', ] exclude_patterns = [ - 'api/prev_api_changes/api_changes_*/*', '**/*inc.rst'] + 'api/prev_api_changes/api_changes_*/*', + '**/*inc.rst', + 'users/explain/index.rst' # Page has no content, but required by sphinx gallery +] exclude_patterns += skip_subdirs @@ -144,18 +158,46 @@ def _check_dependencies(): raise ImportError( "The following dependencies are missing to build the " f"documentation: {', '.join(missing)}") + + # debug sphinx-pydata-theme and mpl-theme-version + if 'mpl_sphinx_theme' not in missing: + import pydata_sphinx_theme + import mpl_sphinx_theme + print(f"pydata sphinx theme: {pydata_sphinx_theme.__version__}") + print(f"mpl sphinx theme: {mpl_sphinx_theme.__version__}") + if shutil.which('dot') is None: raise OSError( "No binary named dot - graphviz must be installed to build the " "documentation") + if shutil.which('latex') is None: + raise OSError( + "No binary named latex - a LaTeX distribution must be installed to build " + "the documentation") _check_dependencies() # Import only after checking for dependencies. -# gallery_order.py from the sphinxext folder provides the classes that -# allow custom ordering of sections and subsections of the gallery -import sphinxext.gallery_order as gallery_order +import sphinx_gallery + +if parse_version(sphinx_gallery.__version__) >= parse_version('0.16.0'): + gallery_order_sectionorder = 'sphinxext.gallery_order.sectionorder' + gallery_order_subsectionorder = 'sphinxext.gallery_order.subsectionorder' + clear_basic_units = 'sphinxext.util.clear_basic_units' + matplotlib_reduced_latex_scraper = 'sphinxext.util.matplotlib_reduced_latex_scraper' +else: + # gallery_order.py from the sphinxext folder provides the classes that + # allow custom ordering of sections and subsections of the gallery + from sphinxext.gallery_order import ( + sectionorder as gallery_order_sectionorder, + subsectionorder as gallery_order_subsectionorder) + from sphinxext.util import clear_basic_units, matplotlib_reduced_latex_scraper + +if parse_version(sphinx_gallery.__version__) >= parse_version('0.17.0'): + sg_matplotlib_animations = (True, 'mp4') +else: + sg_matplotlib_animations = True # The following import is only necessary to monkey patch the signature later on from sphinx_gallery import gen_rst @@ -186,6 +228,7 @@ def _check_dependencies(): missing_references_write_json = False missing_references_warn_unused_ignores = False + intersphinx_mapping = { 'Pillow': ('https://pillow.readthedocs.io/en/stable/', None), 'cycler': ('https://matplotlib.org/cycler/', None), @@ -198,25 +241,10 @@ def _check_dependencies(): 'scipy': ('https://docs.scipy.org/doc/scipy/', None), 'tornado': ('https://www.tornadoweb.org/en/stable/', None), 'xarray': ('https://docs.xarray.dev/en/stable/', None), + 'meson-python': ('https://meson-python.readthedocs.io/en/stable/', None) } -# Sphinx gallery configuration - -def matplotlib_reduced_latex_scraper(block, block_vars, gallery_conf, - **kwargs): - """ - Reduce srcset when creating a PDF. - - Because sphinx-gallery runs *very* early, we cannot modify this even in the - earliest builder-inited signal. Thus we do it at scraping time. - """ - from sphinx_gallery.scrapers import matplotlib_scraper - - if gallery_conf['builder_name'] == 'latex': - gallery_conf['image_srcset'] = [] - return matplotlib_scraper(block, block_vars, gallery_conf, **kwargs) - gallery_dirs = [f'{ed}' for ed in ['gallery', 'tutorials', 'plot_types', 'users/explain'] if f'{ed}/*' not in skip_subdirs] @@ -227,7 +255,7 @@ def matplotlib_reduced_latex_scraper(block, block_vars, gallery_conf, example_dirs += [f'../galleries/{gd}'] sphinx_gallery_conf = { - 'backreferences_dir': Path('api') / Path('_as_gen'), + 'backreferences_dir': Path('api', '_as_gen'), # Compression is a significant effort that we skip for local and CI builds. 'compress_images': ('thumbnails', 'images') if is_release_build else (), 'doc_module': ('matplotlib', 'mpl_toolkits'), @@ -237,19 +265,16 @@ def matplotlib_reduced_latex_scraper(block, block_vars, gallery_conf, 'image_scrapers': (matplotlib_reduced_latex_scraper, ), 'image_srcset': ["2x"], 'junit': '../test-results/sphinx-gallery/junit.xml' if CIRCLECI else '', - 'matplotlib_animations': True, + 'matplotlib_animations': sg_matplotlib_animations, 'min_reported_time': 1, 'plot_gallery': 'True', # sphinx-gallery/913 - 'reference_url': {'matplotlib': None}, + 'reference_url': {'matplotlib': None, 'mpl_toolkits': None}, + 'prefer_full_module': {r'mpl_toolkits\.'}, 'remove_config_comments': True, - 'reset_modules': ( - 'matplotlib', - # clear basic_units module to re-register with unit registry on import - lambda gallery_conf, fname: sys.modules.pop('basic_units', None) - ), - 'subsection_order': gallery_order.sectionorder, + 'reset_modules': ('matplotlib', clear_basic_units), + 'subsection_order': gallery_order_sectionorder, 'thumbnail_size': (320, 224), - 'within_subsection_order': gallery_order.subsectionorder, + 'within_subsection_order': gallery_order_subsectionorder, 'capture_repr': (), 'copyfile_regex': r'.*\.rst', } @@ -273,6 +298,18 @@ def gallery_image_warning_filter(record): logger = logging.getLogger('sphinx') logger.addFilter(gallery_image_warning_filter) +# Sphinx tags configuration +tags_create_tags = True +tags_page_title = "All tags" +tags_create_badges = True +tags_badge_colors = { + "animation": "primary", + "component:*": "secondary", + "event-handling": "success", + "interactivity:*": "dark", + "plot-type:*": "danger", + "*": "light" # default value +} mathmpl_fontsize = 11.0 mathmpl_srcset = ['2x'] @@ -294,7 +331,7 @@ def gallery_image_warning_filter(record): :class: sphx-glr-download-link-note :ref:`Go to the end ` - to download the full example code{2} + to download the full example code.{2} .. rst-class:: sphx-glr-example-title @@ -455,22 +492,28 @@ def js_tag_with_cache_busting(js): "switcher": { # Add a unique query to the switcher.json url. This will be ignored by # the server, but will be used as part of the key for caching by browsers - # so when we do a new minor release the switcher will update "promptly" on + # so when we do a new meso release the switcher will update "promptly" on # the stable and devdocs. - "json_url": f"https://matplotlib.org/devdocs/_static/switcher.json?{SHA}", + "json_url": ( + "https://output.circle-artifacts.com/output/job/" + f"{os.environ['CIRCLE_WORKFLOW_JOB_ID']}/artifacts/" + f"{os.environ['CIRCLE_NODE_INDEX']}" + "/doc/build/html/_static/switcher.json" if CIRCLECI and not DEVDOCS else + f"https://matplotlib.org/devdocs/_static/switcher.json?{SHA}" + ), "version_match": ( - # The start version to show. This must be in switcher.json. - # We either go to 'stable' or to 'devdocs' - 'stable' if matplotlib.__version_info__.releaselevel == 'final' - else 'devdocs') + matplotlib.__version__ + if matplotlib.__version_info__.releaselevel == 'final' + else 'dev') }, "navbar_end": ["theme-switcher", "version-switcher", "mpl_icon_links"], - "secondary_sidebar_items": "page-toc.html", + "navbar_persistent": ["search-button"], "footer_start": ["copyright", "sphinx-version", "doc_version"], # We override the announcement template from pydata-sphinx-theme, where # this special value indicates the use of the unreleased banner. If we need # an actual announcement, then just place the text here as usual. "announcement": "unreleased" if not is_release_build else "", + "show_version_warning_banner": True, } include_analytics = is_release_build if include_analytics: @@ -505,13 +548,19 @@ def js_tag_with_cache_busting(js): html_sidebars = { "index": [ # 'sidebar_announcement.html', - "sidebar_versions.html", "cheatsheet_sidebar.html", "donate_sidebar.html", ], + # no sidebar for release notes, because that page is only a collection of links + # to sub-pages. The sidebar would repeat all the titles of the sub-pages and + # thus basically repeat all the content of the page. + "users/release_notes": ["empty_sidebar.html"], # '**': ['localtoc.html', 'pagesource.html'] } +# Don't include link to doc source files +html_show_sourcelink = False + # Copies only relevant code, not the '>>>' prompt copybutton_prompt_text = r'>>> |\.\.\. ' copybutton_prompt_is_regexp = True @@ -690,16 +739,14 @@ def js_tag_with_cache_busting(js): numpydoc_show_class_members = False # We want to prevent any size limit, as we'll add scroll bars with CSS. -inheritance_graph_attrs = dict(dpi=100, size='1000.0', splines='polyline') +inheritance_graph_attrs = dict(size='1000.0', splines='polyline') # Also remove minimum node dimensions, and increase line size a bit. inheritance_node_attrs = dict(height=0.02, margin=0.055, penwidth=1, width=0.01) inheritance_edge_attrs = dict(penwidth=1) graphviz_dot = shutil.which('dot') -# Still use PNG until SVG linking is fixed -# https://github.com/sphinx-doc/sphinx/issues/3176 -# graphviz_output_format = 'svg' +graphviz_output_format = 'svg' # ----------------------------------------------------------------------------- # Source code links @@ -709,7 +756,6 @@ def js_tag_with_cache_busting(js): if link_github: import inspect - from packaging.version import parse extensions.append('sphinx.ext.linkcode') @@ -765,7 +811,7 @@ def linkcode_resolve(domain, info): if not fn.startswith(('matplotlib/', 'mpl_toolkits/')): return None - version = parse(matplotlib.__version__) + version = parse_version(matplotlib.__version__) tag = 'main' if version.is_devrelease else f'v{version.public}' return ("https://github.com/matplotlib/matplotlib/blob" f"/{tag}/lib/{fn}{linespec}") diff --git a/doc/devel/MEP/MEP13.rst b/doc/devel/MEP/MEP13.rst index 58131a9a06fb..b8b80f281b6e 100644 --- a/doc/devel/MEP/MEP13.rst +++ b/doc/devel/MEP/MEP13.rst @@ -63,7 +63,7 @@ The following steps can be done simultaneously: 1, 2, and 3; 4 and 5; Only the following steps must be done in the same release: 4, 5, and 6. All other changes can be done in separate releases. 8 should -be done several major releases after everything else. +be done several macro releases after everything else. Backward compatibility ====================== diff --git a/doc/devel/MEP/MEP23.rst b/doc/devel/MEP/MEP23.rst index 11fd965c4816..d6b342877959 100644 --- a/doc/devel/MEP/MEP23.rst +++ b/doc/devel/MEP/MEP23.rst @@ -34,8 +34,8 @@ This is and may continue to be the desired method of operation for most use cases. Sometimes when there are too many figures open at the same time, it is -desirable to be able to group these under the same window -[see](https://github.com/matplotlib/matplotlib/issues/2194). +desirable to be able to group these under the same window. See :ghpull:`2194`. + The proposed solution modifies `.FigureManagerBase` to contain and manage more than one ``Canvas``. The settings parameter :rc:`backend.multifigure` control @@ -44,7 +44,7 @@ when the **MultiFigure** behaviour is desired. **Note** It is important to note, that the proposed solution, assumes that the -[MEP22](https://github.com/matplotlib/matplotlib/wiki/Mep22) is +`MEP22 `_. is already in place. This is simply because the actual implementation of the ``Toolbar`` makes it pretty hard to switch between canvases. diff --git a/doc/devel/MEP/MEP28.rst b/doc/devel/MEP/MEP28.rst index 0a215a9f19d0..7ae9f8e610d4 100644 --- a/doc/devel/MEP/MEP28.rst +++ b/doc/devel/MEP/MEP28.rst @@ -264,6 +264,7 @@ value of ``statfxn`` would be ``cbook.boxplot_stats``, but users could pass their own function. Then ``transform_in`` and ``transform_out`` would then be passed as elements of the ``statfxn_args`` parameter. +.. rstcheck: ignore-next-code-block .. code:: python def boxplot_stats(data, ..., transform_in=None, transform_out=None): diff --git a/doc/devel/README.txt b/doc/devel/README.txt deleted file mode 100644 index d7636cd4c37c..000000000000 --- a/doc/devel/README.txt +++ /dev/null @@ -1,9 +0,0 @@ -All documentation in the gitwash directory are automatically generated by running the gitwash_dumper.py -script in the project's root directory using the following parameters: - -python gitwash_dumper.py doc/devel Matplotlib --repo-name=matplotlib --github-user=matplotlib \ - --project-url=https://matplotlib.org \ - --project-ml-url=https://mail.python.org/mailman/listinfo/matplotlib-devel - -The script is hosted at https://raw.githubusercontent.com/matthew-brett/gitwash/master/gitwash_dumper.py. -For more information please visit https://github.com/matthew-brett/gitwash diff --git a/doc/devel/api_changes.rst b/doc/devel/api_changes.rst new file mode 100644 index 000000000000..b7d0a4b063ce --- /dev/null +++ b/doc/devel/api_changes.rst @@ -0,0 +1,229 @@ +.. _api_changes: + +API guidelines +============== + +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:`aesthetic `, +are backwards-incompatible API changes. + +.. toctree:: + :hidden: + + color_changes.rst + + +Add 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 +API. As discussed above, changing the existing API is cumbersome. Therefore, +take particular care when adding new API: + +- Mark helper functions and internal attributes as private by prefixing them + with an underscore. +- Carefully think about good names for your functions and variables. +- Try to adopt patterns and naming conventions from existing parts of the + Matplotlib API. +- Consider making as many arguments keyword-only as possible. See also + `API Evolution the Right Way -- Add Parameters Compatibly`__. + + __ https://emptysqua.re/blog/api-evolution-the-right-way/#adding-parameters + + +.. _deprecation-guidelines: + +Deprecate API +------------- + +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. Generally API deprecation happens in two stages: + +* **introduce:** warn users that the API *will* change +* **expire:** API *is* changed as described in the introduction period + +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 :ref:`meso release ` (e.g. 3.x) +- Deprecated API is generally removed (expired) two point-releases after introduction + of the deprecation. Longer deprecations can be imposed by core developers on + a case-by-case basis to give more time for the transition +- The old API must remain fully functional during the deprecation period +- If alternatives to the deprecated API exist, they should be available + during the deprecation period +- If in doubt, decisions about API changes are finally made by the + `API consistency lead `_ developer. + +.. _intro-deprecation: + +Introduce deprecation +^^^^^^^^^^^^^^^^^^^^^ + +#. 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: + + - Use ``_api.warn_deprecated()`` for general deprecation warnings + - Use the decorator ``@_api.deprecated`` to deprecate classes, functions, + methods, or properties + - Use ``@_api.deprecate_privatize_attribute`` to annotate deprecation of + attributes while keeping the internal private version. + - To warn on changes of the function signature, use the decorators + ``@_api.delete_parameter``, ``@_api.rename_parameter``, and + ``@_api.make_keyword_only`` + + All these helpers take a first parameter *since*, which should be set to + the next point release, e.g. "3.x". + + You can use standard rst cross references in *alternative*. + +#. Make appropriate changes to the type hints in the associated ``.pyi`` file. + The general guideline is to match runtime reported behavior. + + - Items marked with ``@_api.deprecated`` or ``@_api.deprecate_privatize_attribute`` + are generally kept during the expiry period, and thus no changes are needed on + introduction. + - Items decorated with ``@_api.rename_parameter`` or ``@_api.make_keyword_only`` + report the *new* (post deprecation) signature at runtime, and thus *should* be + updated on introduction. + - Items decorated with ``@_api.delete_parameter`` should include a default value hint + for the deleted parameter, even if it did not previously have one (e.g. + ``param: = ...``). + +.. _expire-deprecation: + +Expire deprecation +^^^^^^^^^^^^^^^^^^ + +#. 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. + +#. Make appropriate changes to the type hints in the associated ``.pyi`` file. + + - Items marked with ``@_api.deprecated`` or ``@_api.deprecate_privatize_attribute`` + are to be removed on expiry. + - Items decorated with ``@_api.rename_parameter`` or ``@_api.make_keyword_only`` + will have been updated at introduction, and require no change now. + - Items decorated with ``@_api.delete_parameter`` will need to be updated to the + final signature, in the same way as the ``.py`` file signature is updated. + - Any entries in :file:`ci/mypy-stubtest-allowlist.txt` which indicate a deprecation + version should be double checked. In most cases this is not needed, though some + items were never type hinted in the first place and were added to this file + instead. For removed items that were not in the stub file, only deleting from the + allowlist is required. + + +.. redirect-from:: /devel/coding_guide#new-features-and-api-changes + +.. _api_whats_new: + +Announce new and deprecated API +------------------------------- + +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: + ++-------------------+-----------------------------+----------------------------------------------+ +| | 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]` | ++-------------------+-----------------------------+----------------------------------------------+ + +When deprecating API, please add a notice as described in the +:ref:`deprecation guidelines ` and summarized here: + ++--------------------------------------------------+----------------------------------------------+ +| stage | announcement folder | ++===========+======================================+==============================================+ +| :ref:`introduce deprecation ` | :file:`doc/api/next_api_changes/deprecation` | ++-----------+--------------------------------------+----------------------------------------------+ +| :ref:`expire deprecation ` | :file:`doc/api/next_api_changes/[kind]` | ++-----------+--------------------------------------+----------------------------------------------+ + +Generally the introduction notices can be repurposed for the expiration notice as they +are expected to be describing the same API changes and removals. + +.. _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 micro release version is omitted and +the directive should not be added to entire modules. + +Release notes +^^^^^^^^^^^^^ + +For both change notes and what's new, please avoid using cross-references in section +titles as it causes links to be confusing in the table of contents. Instead, ensure that +a cross-reference is included in the descriptive text. + +.. _api-change-notes: + +API change notes +"""""""""""""""" + +.. include:: ../api/next_api_changes/README.rst + :start-line: 5 + :end-line: 31 + +.. _whats-new-notes: + +What's new notes +"""""""""""""""" + +.. include:: ../users/next_whats_new/README.rst + :start-line: 5 + :end-line: 24 diff --git a/doc/devel/coding_guide.rst b/doc/devel/coding_guide.rst index 22873020f103..77247ba9a3b2 100644 --- a/doc/devel/coding_guide.rst +++ b/doc/devel/coding_guide.rst @@ -1,369 +1,310 @@ -.. _pr-guidelines: +.. _coding_guidelines: -*********************** -Pull request guidelines -*********************** +***************** +Coding guidelines +***************** -`Pull requests (PRs) on GitHub -`__ -are the mechanism for contributing to Matplotlib's code and documentation. +We appreciate these guidelines being followed because it improves the readability, +consistency, and maintainability of the code base. -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. +.. admonition:: API guidelines + :class: seealso -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 ` + If adding new features, changing behavior or function signatures, or removing + public interfaces, please consult the :ref:`api_changes`. +.. _code-style: -Summary for pull request authors -================================ - -We recommend that you check that your contribution complies with the following -guidelines before submitting a pull request: - -.. rst-class:: checklist - -* Changes, both new features and bugfixes, should have good test coverage. See - :ref:`testing` for more details. - -* Update the :ref:`documentation ` if necessary. - -* 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 `. - -* 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` - -* 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`. - -When opening a pull request on Github, please ensure that: - -.. rst-class:: checklist - -* Changes were made on a :ref:`feature branch `. - -* :ref:`pre-commit ` checks for spelling, formatting, etc pass - -* The pull request targets the :ref:`main branch ` - -* 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 `__. - -* :ref:`pr-automated-tests` pass - -For guidance on creating and managing a pull request, please see our -:ref:`contributing ` and :ref:`pull request workflow ` -guides. - - -Summary for pull request reviewers -================================== - -.. redirect-from:: /devel/maintainer_workflow - -**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. +PEP8, as enforced by flake8 +=========================== -When reviewing, please ensure that the pull request satisfies the following -requirements before merging it: +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 :: -Content topics: + python -m pip install flake8 + flake8 /path/to/module.py -.. rst-class:: checklist +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__). -* Is the feature / bugfix reasonable? -* Does the PR conform with the :ref:`coding_guidelines`? -* Is the :ref:`documentation ` (docstrings, examples, - what's new, API changes) updated? -* Is the change purely stylistic? Generally, such changes are discouraged when - not part of other non-stylistic work because it obscures the git history of - functional changes to the code. Reflowing a method or docstring as part of a - larger refactor/rewrite is acceptable. +.. _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 -Organizational topics: +Package imports +=============== -.. rst-class:: checklist +Import the following modules using the standard scipy conventions:: -* Make sure all :ref:`automated tests ` pass. -* The PR should :ref:`target the main branch `. -* Tag with descriptive :ref:`labels `. -* Set the :ref:`milestone `. -* Keep an eye on the :ref:`number of commits `. -* Approve if all of the above topics are handled. -* :ref:`Merge ` if a sufficient number of approvals is reached. + 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 -.. _pr-guidelines-details: +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. -Detailed guidelines -=================== +Variable names +============== -.. _pr-documentation: +When feasible, please use our internal variable naming convention for objects +of a given class and objects of any child class: -Documentation -------------- ++------------------------------------+---------------+------------------------------------------+ +| base class | variable | multiples | ++====================================+===============+==========================================+ +| `~matplotlib.figure.FigureBase` | ``fig`` | | ++------------------------------------+---------------+------------------------------------------+ +| `~matplotlib.axes.Axes` | ``ax`` | | ++------------------------------------+---------------+------------------------------------------+ +| `~matplotlib.transforms.Transform` | ``trans`` | ``trans__`` | ++ + + + +| | | ``trans_`` when target is screen | ++------------------------------------+---------------+------------------------------------------+ -* Every new feature should be documented. If it's a new module, don't - forget to add a new rst file to the API docs. +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 +========== -* Each high-level plotting function should have a small example in - the ``Examples`` section of the docstring. This should be as simple as - possible to demonstrate the method. More complex examples should go into - a dedicated example file in the :file:`examples` directory, which will be - rendered to the examples gallery in the documentation. +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. -* Build the docs and make sure all formatting warnings are addressed. +Type hints are checked by the mypy :ref:`pre-commit hook `, +can often be verified by running ``tox -e stubtest``. -* See :ref:`documenting-matplotlib` for our documentation style guide. +New modules and files: installation +=================================== -.. _pr-labels: +* If you have added new files or directories, or reorganized existing ones, make sure the + new files are included in the :file:`meson.build` in the corresponding directories. +* New modules *may* be typed inline or using parallel stub file like existing modules. -Labels ------- +C/C++ extensions +================ + +* Extensions may be written in C or C++. -* If you have the rights to set labels, tag the PR with descriptive labels. - See the `list of labels `__. -* If the PR makes changes to the wheel building Action, add the - "Run cibuildwheel" label to enable testing wheels. +* Code style should conform to PEP7 (understanding that PEP7 doesn't + address C++, but most of its admonitions still apply). -.. _pr-milestones: - -Milestones ----------- - -Set the milestone according to these guidelines: - -* *New features and API changes* are milestoned for the next minor release - ``v3.N.0``. - -* *Bugfixes, tests for released code, and docstring changes* may be milestoned - for the next patch release ``v3.N.M``. - -* *Documentation changes* (only .rst files and examples) may be milestoned - ``v3.N-doc``. - -If multiple rules apply, choose the first matching from the above list. See -:ref:`backport-strategy` for detailed guidance on what should or should not be -backported. - -The milestone marks the release a PR should go into. It states intent, but can -be changed because of release planning or re-evaluation of the PR scope and -maturity. - -All Pull Requests should target the main branch. The milestone tag triggers -an :ref:`automatic backport ` for milestones which have -a corresponding branch. - -.. _pr-merging: - -Merging -------- - -* Documentation and examples may be merged by the first reviewer. Use - the threshold "is this better than it was?" as the review criteria. - -* For code changes (anything in ``src`` or ``lib``) at least two - core developers (those with commit rights) should review all pull - requests. If you are the first to review a PR and approve of the - changes use the GitHub `'approve review' - `__ - tool to mark it as such. If you are a subsequent reviewer please - approve the review and if you think no more review is needed, merge - the PR. - - Ensure that all API changes are documented in a file in one of the - subdirectories of :file:`doc/api/next_api_changes`, and significant new - features have an entry in :file:`doc/user/whats_new`. - - - If a PR already has a positive review, a core developer (e.g. the first - reviewer, but not necessarily) may champion that PR for merging. In order - to do so, they should ping all core devs both on GitHub and on the dev - mailing list, and label the PR with the "Merge with single review?" label. - Other core devs can then either review the PR and merge or reject it, or - simply request that it gets a second review before being merged. If no one - asks for such a second review within a week, the PR can then be merged on - the basis of that single review. - - A core dev should only champion one PR at a time and we should try to keep - the flow of championed PRs reasonable. - -* Do not self merge, except for 'small' patches to un-break the CI or - when another reviewer explicitly allows it (ex, "Approve modulo CI - passing, may self merge when green"). - -.. _pr-automated-tests: - -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 :ref:`communication-channels` - -.. _pr-squashing: - -Number of commits and squashing -------------------------------- - -* Squashing is case-by-case. The balance is between burden on the - contributor, keeping a relatively clean history, and keeping a - history usable for bisecting. The only time we are really strict - about it is to eliminate binary files (ex multiple test image - re-generations) and to remove upstream merges. - -* Do not let perfect be the enemy of the good, particularly for - documentation or example PRs. If you find yourself making many - small suggestions, either open a PR against the original branch, - push changes to the contributor branch, or merge the PR and then - open a new PR against upstream. - -* If you push to a contributor branch leave a comment explaining what - you did, ex "I took the liberty of pushing a small clean-up PR to - your branch, thanks for your work.". If you are going to make - substantial changes to the code or intent of the PR please check - with the contributor first. +* Python/C interface code should be kept separate from the core C/C++ + code. The interface code should be named :file:`FOO_wrap.cpp` or + :file:`FOO_wrapper.cpp`. + +* Header file documentation (aka docstrings) should be in Numpydoc + format. We don't plan on using automated tools for these + docstrings, and the Numpydoc format is well understood in the + scientific Python community. + +* C/C++ code in the :file:`extern/` directory is vendored, and should be kept + close to upstream whenever possible. It can be modified to fix bugs or + implement new features only if the required changes cannot be made elsewhere + in the codebase. In particular, avoid making style fixes to it. + +.. _keyword-argument-processing: + +Keyword argument processing +=========================== + +Matplotlib makes extensive use of ``**kwargs`` for pass-through customizations +from one function to another. A typical example is +`~matplotlib.axes.Axes.text`. The definition of `matplotlib.pyplot.text` is a +simple pass-through to `matplotlib.axes.Axes.text`:: + + # in pyplot.py + def text(x, y, s, fontdict=None, **kwargs): + return gca().text(x, y, s, fontdict=fontdict, **kwargs) + +`matplotlib.axes.Axes.text` (simplified for illustration) just +passes all ``args`` and ``kwargs`` on to ``matplotlib.text.Text.__init__``:: + + # in axes/_axes.py + def text(self, x, y, s, fontdict=None, **kwargs): + t = Text(x=x, y=y, text=s, **kwargs) + +and ``matplotlib.text.Text.__init__`` (again, simplified) +just passes them on to the `matplotlib.artist.Artist.update` method:: + + # in text.py + def __init__(self, x=0, y=0, text='', **kwargs): + super().__init__() + self.update(kwargs) + +``update`` does the work looking for methods named like +``set_property`` if ``property`` is a keyword argument. i.e., no one +looks at the keywords, they just get passed through the API to the +artist constructor which looks for suitably named methods and calls +them with the value. + +As a general rule, the use of ``**kwargs`` should be reserved for +pass-through keyword arguments, as in the example above. If all the +keyword args are to be used in the function, and not passed +on, use the key/value keyword args in the function definition rather +than the ``**kwargs`` idiom. + +In some cases, you may want to consume some keys in the local +function, and let others pass through. Instead of popping arguments to +use off ``**kwargs``, specify them as keyword-only arguments to the local +function. This makes it obvious at a glance which arguments will be +consumed in the function. For example, in +:meth:`~matplotlib.axes.Axes.plot`, ``scalex`` and ``scaley`` are +local arguments and the rest are passed on as +:meth:`~matplotlib.lines.Line2D` keyword arguments:: + # in axes/_axes.py + def plot(self, *args, scalex=True, scaley=True, **kwargs): + lines = [] + for line in self._get_lines(*args, **kwargs): + self.add_line(line) + lines.append(line) + +.. _using_logging: + +Using logging for debug messages +================================ -.. _branches_and_backports: +Matplotlib uses the standard Python `logging` library to write verbose +warnings, information, and debug messages. Please use it! In all those places +you write `print` calls to do your debugging, try using `logging.debug` +instead! -Branches and backports -====================== -Current branches ----------------- -The current active branches are +To include `logging` in your module, at the top of the module, you need to +``import logging``. Then calls in your code like:: -*main* - The current development version. Future minor releases (*v3.N.0*) will be - branched from this. + _log = logging.getLogger(__name__) # right after the imports -*v3.N.x* - Maintenance branch for Matplotlib 3.N. Future patch releases will be - branched from this. + # code + # more code + _log.info('Here is some information') + _log.debug('Here is some more detailed information') -*v3.N.M-doc* - Documentation for the current release. On a patch release, this will be - replaced by a properly named branch for the new release. +will log to a logger named ``matplotlib.yourmodulename``. +If an end-user of Matplotlib sets up `logging` to display at levels more +verbose than ``logging.WARNING`` in their code with the Matplotlib-provided +helper:: -.. _pr-branch-selection: + plt.set_loglevel("debug") -Branch selection for pull requests ----------------------------------- +or manually with :: -Generally, all pull requests should target the main branch. + import logging + logging.basicConfig(level=logging.DEBUG) + import matplotlib.pyplot as plt -Other branches are fed through :ref:`automatic ` or -:ref:`manual `. Directly -targeting other branches is only rarely necessary for special maintenance -work. +Then they will receive messages like -.. _backport-strategy: +.. code-block:: none -Backport strategy ------------------ + DEBUG:matplotlib.backends:backend MacOSX version unknown + DEBUG:matplotlib.yourmodulename:Here is some information + DEBUG:matplotlib.yourmodulename:Here is some more detailed information -Backports to the patch release branch (*v3.N.x*) are the changes that will be -included in the next patch (aka bug-fix) release. The goal of the patch -releases is to fix bugs without adding any new regressions or behavior changes. -We will always attempt to backport: +Avoid using pre-computed strings (``f-strings``, ``str.format``,etc.) for logging because +of security and performance issues, and because they interfere with style handlers. For +example, use ``_log.error('hello %s', 'world')`` rather than ``_log.error('hello +{}'.format('world'))`` or ``_log.error(f'hello {s}')``. -- critical bug fixes (segfault, failure to import, things that the - user cannot work around) -- fixes for regressions introduced in the last two minor releases +Which logging level to use? +--------------------------- -and may attempt to backport fixes for regressions introduced in older releases. +There are five levels at which you can emit messages. -In the case where the backport is not clean, for example if the bug fix is -built on top of other code changes we do not want to backport, balance the -effort and risk of re-implementing the bug fix vs the severity of the bug. -When in doubt, err on the side of not backporting. +- `logging.critical` and `logging.error` are really only there for errors that + will end the use of the library but not kill the interpreter. +- `logging.warning` and `._api.warn_external` are used to warn the user, + see below. +- `logging.info` is for information that the user may want to know if the + program behaves oddly. They are not displayed by default. For instance, if + an object isn't drawn because its position is ``NaN``, that can usually + be ignored, but a mystified user could call + ``logging.basicConfig(level=logging.INFO)`` and get an error message that + says why. +- `logging.debug` is the least likely to be displayed, and hence can be the + most verbose. "Expected" code paths (e.g., reporting normal intermediate + steps of layouting or rendering) should only log at this level. -When backporting a Pull Request fails or is declined, re-milestone the original -PR to the next minor release and leave a comment explaining why. +By default, `logging` displays all log messages at levels higher than +``logging.WARNING`` to `sys.stderr`. -The only changes backported to the documentation branch (*v3.N.M-doc*) -are changes to :file:`doc` or :file:`galleries`. Any changes to :file:`lib` -or :file:`src`, including docstring-only changes, must not be backported to -this branch. +The `logging tutorial`_ suggests that the difference between `logging.warning` +and `._api.warn_external` (which uses `warnings.warn`) is that +`._api.warn_external` should be used for things the user must change to stop +the warning (typically in the source), whereas `logging.warning` can be more +persistent. Moreover, note that `._api.warn_external` will by default only +emit a given warning *once* for each line of user code, whereas +`logging.warning` will display the message every time it is called. +By default, `warnings.warn` displays the line of code that has the ``warn`` +call. This usually isn't more informative than the warning message itself. +Therefore, Matplotlib uses `._api.warn_external` which uses `warnings.warn`, +but goes up the stack and displays the first line of code outside of +Matplotlib. For example, for the module:: -.. _automated-backports: + # in my_matplotlib_module.py + import warnings -Automated backports -------------------- + def set_range(bottom, top): + if bottom == top: + warnings.warn('Attempting to set identical bottom==top') -We use MeeseeksDev bot to automatically backport merges to the correct -maintenance branch base on the milestone. To work properly the -milestone must be set before merging. If you have commit rights, the -bot can also be manually triggered after a merge by leaving a message -``@meeseeksdev backport to BRANCH`` on the PR. If there are conflicts -MeeseeksDev will inform you that the backport needs to be done -manually. +running the script:: -The target branch is configured by putting ``on-merge: backport to -TARGETBRANCH`` in the milestone description on it's own line. + from matplotlib import my_matplotlib_module + my_matplotlib_module.set_range(0, 0) # set range -If the bot is not working as expected, please report issues to -`MeeseeksDev `__. +will display +.. code-block:: none -.. _manual-backports: + UserWarning: Attempting to set identical bottom==top + warnings.warn('Attempting to set identical bottom==top') -Manual backports ----------------- +Modifying the module to use `._api.warn_external`:: -When doing backports please copy the form used by MeeseeksDev, -``Backport PR #XXXX: TITLE OF PR``. If you need to manually resolve -conflicts make note of them and how you resolved them in the commit -message. + from matplotlib import _api -We do a backport from main to v2.2.x assuming: + def set_range(bottom, top): + if bottom == top: + _api.warn_external('Attempting to set identical bottom==top') -* ``matplotlib`` is a read-only remote branch of the matplotlib/matplotlib repo +and running the same script will display -The ``TARGET_SHA`` is the hash of the merge commit you would like to -backport. This can be read off of the GitHub PR page (in the UI with -the merge notification) or through the git CLI tools. +.. code-block:: none -Assuming that you already have a local branch ``v2.2.x`` (if not, then -``git checkout -b v2.2.x``), and that your remote pointing to -``https://github.com/matplotlib/matplotlib`` is called ``upstream``: + UserWarning: Attempting to set identical bottom==top + my_matplotlib_module.set_range(0, 0) # set range -.. code-block:: bash +.. _logging tutorial: https://docs.python.org/3/howto/logging.html#logging-basic-tutorial - git fetch upstream - git checkout v2.2.x # or include -b if you don't already have this. - git reset --hard upstream/v2.2.x - git cherry-pick -m 1 TARGET_SHA - # resolve conflicts and commit if required -Files with conflicts can be listed by ``git status``, -and will have to be fixed by hand (search on ``>>>>>``). Once -the conflict is resolved, you will have to re-add the file(s) to the branch -and then continue the cherry pick: +.. _licence-coding-guide: -.. code-block:: bash +.. include:: license.rst + :start-line: 2 - git add lib/matplotlib/conflicted_file.py - git add lib/matplotlib/conflicted_file2.py - git cherry-pick --continue +.. toctree:: + :hidden: -Use your discretion to push directly to upstream or to open a PR; be -sure to push or PR against the ``v2.2.x`` upstream branch, not ``main``! + license.rst diff --git a/doc/devel/contribute.rst b/doc/devel/contribute.rst index ee78beca0781..7b2b0e774ec7 100644 --- a/doc/devel/contribute.rst +++ b/doc/devel/contribute.rst @@ -2,9 +2,9 @@ .. _contributing: -========== +********** Contribute -========== +********** You've discovered a bug or something else you want to change in Matplotlib — excellent! @@ -30,65 +30,165 @@ existing issue and pull request discussions, and following the conversations during pull request reviews to get context. Or you can deep-dive into a subset of the code-base to understand what is going on. -There are a few typical new contributor profiles: - -* **You are a Matplotlib user, and you see a bug, a potential improvement, or - something that annoys you, and you can fix it.** - - You can search our issue tracker for an existing issue that describes your problem or - open a new issue to inform us of the problem you observed and discuss the best approach - to fix it. If your contributions would not be captured on GitHub (social media, - communication, educational content), you can also reach out to us on gitter_, - `Discourse `__ or attend any of our `community - meetings `__. - -* **You are not a regular Matplotlib user but a domain expert: you know about - visualization, 3D plotting, design, technical writing, statistics, or some - other field where Matplotlib could be improved.** - - Awesome -- you have a focus on a specific application and domain and can - start there. In this case, maintainers can help you figure out the best - implementation; open an issue or pull request with a starting point, and we'll - be happy to discuss technical approaches. - - If you prefer, you can use the `GitHub functionality for "draft" pull requests - `__ - and request early feedback on whatever you are working on, but you should be - aware that maintainers may not review your contribution unless it has the - "Ready to review" state on GitHub. - -* **You are new to Matplotlib, both as a user and contributor, and want to start - contributing but have yet to develop a particular interest.** - - Having some previous experience or relationship with the library can be very - helpful when making open-source contributions. It helps you understand why - things are the way they are and how they *should* be. Having first-hand - experience and context is valuable both for what you can bring to the - conversation (and given the breadth of Matplotlib's usage, there is a good - chance it is a unique context in any given conversation) and make it easier to - understand where other people are coming from. - - Understanding the entire codebase is a long-term project, and nobody expects - you to do this right away. If you are determined to get started with - Matplotlib and want to learn, going through the basic functionality, - choosing something to focus on (3d, testing, documentation, animations, etc.) - and gaining context on this area by reading the issues and pull requests - touching these subjects is a reasonable approach. +.. dropdown:: Do I really have something to contribute to Matplotlib? + :open: + :icon: person-fill -.. _get_connected: + 100% yes! There are so many ways to contribute to our community. Take a look + at the following sections to learn more. -Get connected -============= + There are a few typical new contributor profiles: + + * **You are a Matplotlib user, and you see a bug, a potential improvement, or + something that annoys you, and you can fix it.** + + You can search our issue tracker for an existing issue that describes your problem or + open a new issue to inform us of the problem you observed and discuss the best approach + to fix it. If your contributions would not be captured on GitHub (social media, + communication, educational content), you can also reach out to us on gitter_, + `Discourse `__ or attend any of our `community + meetings `__. + + * **You are not a regular Matplotlib user but a domain expert: you know about + visualization, 3D plotting, design, technical writing, statistics, or some + other field where Matplotlib could be improved.** + + Awesome -- you have a focus on a specific application and domain and can + start there. In this case, maintainers can help you figure out the best + implementation; open an issue or pull request with a starting point, and we'll + be happy to discuss technical approaches. + + If you prefer, you can use the `GitHub functionality for "draft" pull requests + `__ + and request early feedback on whatever you are working on, but you should be + aware that maintainers may not review your contribution unless it has the + "Ready to review" state on GitHub. + + * **You are new to Matplotlib, both as a user and contributor, and want to start + contributing but have yet to develop a particular interest.** + + Having some previous experience or relationship with the library can be very + helpful when making open-source contributions. It helps you understand why + things are the way they are and how they *should* be. Having first-hand + experience and context is valuable both for what you can bring to the + conversation (and given the breadth of Matplotlib's usage, there is a good + chance it is a unique context in any given conversation) and make it easier to + understand where other people are coming from. + + Understanding the entire codebase is a long-term project, and nobody expects + you to do this right away. If you are determined to get started with + Matplotlib and want to learn, going through the basic functionality, + choosing something to focus on (3d, testing, documentation, animations, etc.) + and gaining context on this area by reading the issues and pull requests + touching these subjects is a reasonable approach. + +.. _contribute_code: + +Code +---- +You want to implement a feature or fix a bug or help with maintenance - much +appreciated! Our library source code is found in: + +* Python library code: :file:`lib/` +* C-extension code: :file:`src/` +* Tests: :file:`lib/matplotlib/tests/` + +Because many people use and work on Matplotlib, we have guidelines for keeping +our code consistent and mitigating the impact of changes. + +* :ref:`coding_guidelines` +* :ref:`api_changes` +* :ref:`pr-guidelines` + +Code is contributed through pull requests, so we recommend that you start at +:ref:`how-to-pull-request` If you get stuck, please reach out on the +:ref:`contributor_incubator` + +.. _contribute_documentation: + +Documentation +------------- + +You as an end-user of Matplotlib can make a valuable contribution because you +more clearly see the potential for improvement than a core developer. For example, +you can: + +- Fix a typo +- Clarify a docstring +- Write or update an :ref:`example plot ` +- Write or update a comprehensive :ref:`tutorial ` + +Our code is documented inline in the source code files in :file:`matplotlib/lib`. +Our website structure mirrors our folder structure, meaning that a narrative +document's URL roughly corresponds to its location in our folder structure: + +.. grid:: 1 1 2 2 -Do I really have something to contribute to Matplotlib? -------------------------------------------------------- + .. grid-item:: using the library -100% yes. There are so many ways to contribute to our community. + * :file:`galleries/plot_types/` + * :file:`users/getting_started/` + * :file:`galleries/user_explain/` + * :file:`galleries/tutorials/` + * :file:`galleries/examples/` + * :file:`doc/api/` + .. grid-item:: information about the library + + * :file:`doc/install/` + * :file:`doc/project/` + * :file:`doc/devel/` + * :file:`doc/users/resources/index.rst` + * :file:`doc/users/faq.rst` + + +Other documentation is generated from the following external sources: + +* matplotlib.org homepage: https://github.com/matplotlib/mpl-brochure-site +* cheat sheets: https://github.com/matplotlib/cheatsheets +* third party packages: https://github.com/matplotlib/mpl-third-party + +Instructions and guidelines for contributing documentation are found in: + +* :doc:`document` +* :doc:`style_guide` +* :doc:`tag_guidelines` + +Documentation is contributed through pull requests, so we recommend that you start +at :ref:`how-to-pull-request`. If that feels intimidating, we encourage you to +`open an issue`_ describing what improvements you would make. If you get stuck, +please reach out on the :ref:`contributor_incubator` + +.. _`open an issue`: https://github.com/matplotlib/matplotlib/issues/new?assignees=&labels=Documentation&projects=&template=documentation.yml&title=%5BDoc%5D%3A+ + + +.. _other_ways_to_contribute: + +Community +--------- +Matplotlib's community is built by its members, if you would like to help out +see our :ref:`communications-guidelines`. + +It helps us if you spread the word: reference the project from your blog +and articles or link to it from your website! + +If Matplotlib contributes to a project that leads to a scientific publication, +please cite us following the :doc:`/project/citing` guidelines. + +If you have developed an extension to Matplotlib, please consider adding it to our +`third party package `_ list. + + +.. _get_connected: + +Get connected +============= When in doubt, we recommend going together! Get connected with our community of active contributors, many of whom felt just like you when they started out and are happy to welcome you and support you as you get to know how we work, and -where things are. Take a look at the next sections to learn more. +where things are. + +.. _contributor_incubator: Contributor incubator --------------------- @@ -104,6 +204,12 @@ documentation or a blog post, how to get involved in community work, or get a To join, please go to our public community_ channel, and ask to be added to ``#incubator``. One of our core developers will see your message and will add you. +.. _gitter: https://gitter.im/matplotlib/matplotlib +.. _community: https://gitter.im/matplotlib/community + + +.. _new_contributors: + New Contributors Meeting ------------------------ @@ -120,7 +226,22 @@ questions you might have, and to get to know a few of the people behind the GitHub handles 😉. You can reach out to us on gitter_ for any clarifications or suggestions. We ❤ feedback! -.. _new_contributors: +.. _managing_issues_prs: + +Work on an issue +================ + +In general, the Matplotlib project does not assign issues. Issues are +"assigned" or "claimed" by opening a PR; there is no other assignment +mechanism. If you have opened such a PR, please comment on the issue thread to +avoid duplication of work. Please check if there is an existing PR for the +issue you are addressing. If there is, try to work with the author by +submitting reviews of their code or commenting on the PR rather than opening +a new PR; duplicate PRs are subject to being closed. However, if the existing +PR is an outline, unlikely to work, or stalled, and the original author is +unresponsive, feel free to open a new PR referencing the old one. + +.. _good_first_issues: Good first issues ----------------- @@ -143,81 +264,11 @@ though not necessarily all at the same time: - It involves Python features such as decorators and context managers, which have subtleties due to our implementation decisions. -.. _managing_issues_prs: - -Work on an issue ----------------- - -In general, the Matplotlib project does not assign issues. Issues are -"assigned" or "claimed" by opening a PR; there is no other assignment -mechanism. If you have opened such a PR, please comment on the issue thread to -avoid duplication of work. Please check if there is an existing PR for the -issue you are addressing. If there is, try to work with the author by -submitting reviews of their code or commenting on the PR rather than opening -a new PR; duplicate PRs are subject to being closed. However, if the existing -PR is an outline, unlikely to work, or stalled, and the original author is -unresponsive, feel free to open a new PR referencing the old one. - -.. _submitting-a-bug-report: - -Submit a bug report -=================== - -If you find a bug in the code or documentation, do not hesitate to submit a -ticket to the -`Issue Tracker `_. You are -also welcome to post feature requests or pull requests. - -If you are reporting a bug, please do your best to include the following: - -#. A short, top-level summary of the bug. In most cases, this should be 1-2 - sentences. - -#. A short, self-contained code snippet to reproduce the bug, ideally allowing - a simple copy and paste to reproduce. Please do your best to reduce the code - snippet to the minimum required. - -#. The actual outcome of the code snippet. - -#. The expected outcome of the code snippet. - -#. The Matplotlib version, Python version and platform that you are using. You - can grab the version with the following commands:: - - >>> import matplotlib - >>> matplotlib.__version__ - '3.4.1' - >>> import platform - >>> platform.python_version() - '3.9.2' - -We have preloaded the issue creation page with a Markdown form that you can -use to organize this information. - -Thank you for your help in keeping bug reports complete, targeted and descriptive. - -.. _request-a-new-feature: - -Request a new feature -===================== -Please post feature requests to the -`Issue Tracker `_. +.. _how-to-pull-request: -The Matplotlib developers will give feedback on the feature proposal. Since -Matplotlib is an open source project with limited resources, we encourage -users to then also -:ref:`participate in the implementation `. - -.. _contributing-code: - -Contribute code -=============== - -.. _how-to-contribute: - -How to contribute ------------------ +Start a pull request +==================== The preferred way to contribute to Matplotlib is to fork the `main repository `__ on GitHub, @@ -228,7 +279,7 @@ in-browser development environment that comes with the appropriated setup to contribute to Matplotlib. Workflow overview -^^^^^^^^^^^^^^^^^ +----------------- A brief overview of the workflow is as follows. @@ -269,7 +320,7 @@ A brief overview of the workflow is as follows. #. Install the local version of Matplotlib with:: - python -m pip install -e . + python -m pip install --no-build-isolation --editable .[dev] See :ref:`installing_for_devs` for detailed instructions. @@ -292,579 +343,43 @@ A brief overview of the workflow is as follows. git push -u origin my-feature -Open a pull request on Matplotlib -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Finally, go to the web page of *your fork* of the Matplotlib repo, and click -**Compare & pull request** to send your changes to the maintainers for review. -The base repository is ``matplotlib/matplotlib`` and the base branch is -generally ``main``. For more guidance, see GitHub's `pull request tutorial -`_. - -For more detailed instructions on how to set up Matplotlib for development and -best practices for contribution, see :ref:`installing_for_devs`. - GitHub Codespaces workflows ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -* If you need to open a GUI window with Matplotlib output on Codespaces, our - configuration includes a `light-weight Fluxbox-based desktop - `_. - You can use it by connecting to this desktop via your web browser. To do this: - - #. Press ``F1`` or ``Ctrl/Cmd+Shift+P`` and select - ``Ports: Focus on Ports View`` in the VSCode session to bring it into - focus. Open the ports view in your tool, select the ``noVNC`` port, and - click the Globe icon. - #. In the browser that appears, click the Connect button and enter the desktop - password (``vscode`` by default). - - Check the `GitHub instructions - `_ - for more details on connecting to the desktop. - -* If you also built the documentation pages, you can view them using Codespaces. - Use the "Extensions" icon in the activity bar to install the "Live Server" - extension. Locate the ``doc/build/html`` folder in the Explorer, right click - the file you want to open and select "Open with Live Server." - -.. _contributing_documentation: - -Contribute documentation -======================== - -You as an end-user of Matplotlib can make a valuable contribution because you -more clearly see the potential for improvement than a core developer. For example, you can: - -- Fix a typo -- Clarify a docstring -- Write or update an :ref:`example plot ` -- Write or update a comprehensive :ref:`tutorial ` - -The documentation source files live in the same GitHub repository as the code. -Contributions are proposed and accepted through the pull request process. -For details see :ref:`how-to-contribute`. +If you need to open a GUI window with Matplotlib output on Codespaces, our +configuration includes a `light-weight Fluxbox-based desktop +`_. +You can use it by connecting to this desktop via your web browser. To do this: -If you have trouble getting started, you may instead open an `issue`_ -describing the intended improvement. +#. Press ``F1`` or ``Ctrl/Cmd+Shift+P`` and select + ``Ports: Focus on Ports View`` in the VSCode session to bring it into + focus. Open the ports view in your tool, select the ``noVNC`` port, and + click the Globe icon. +#. In the browser that appears, click the Connect button and enter the desktop + password (``vscode`` by default). -.. _issue: https://github.com/matplotlib/matplotlib/issues +Check the `GitHub instructions +`_ +for more details on connecting to the desktop. -.. seealso:: - * :ref:`documenting-matplotlib` - -.. _other_ways_to_contribute: - -Other ways to contribute -======================== - -It also helps us if you spread the word: reference the project from your blog -and articles or link to it from your website! If Matplotlib contributes to a -project that leads to a scientific publication, please follow the -:doc:`/users/project/citing` guidelines. - -.. _coding_guidelines: - -Coding guidelines -================= - -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. - -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: - -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. - -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 - a case-by-case basis to give more time for the transition -- The old API must remain fully functional during the deprecation period -- If alternatives to the deprecated API exist, they should be available - during the deprecation period -- If in doubt, decisions about API changes are finally made by the - API consistency lead developer - -.. _intro-deprecation: - -Introduce deprecation -""""""""""""""""""""" - -#. 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: - - - Use ``_api.warn_deprecated()`` for general deprecation warnings - - Use the decorator ``@_api.deprecated`` to deprecate classes, functions, - methods, or properties - - Use ``@_api.deprecate_privatize_attribute`` to annotate deprecation of - attributes while keeping the internal private version. - - To warn on changes of the function signature, use the decorators - ``@_api.delete_parameter``, ``@_api.rename_parameter``, and - ``@_api.make_keyword_only`` - - All these helpers take a first parameter *since*, which should be set to - the next point release, e.g. "3.x". - - You can use standard rst cross references in *alternative*. - -#. Make appropriate changes to the type hints in the associated ``.pyi`` file. - The general guideline is to match runtime reported behavior. - - - Items marked with ``@_api.deprecated`` or ``@_api.deprecate_privatize_attribute`` - are generally kept during the expiry period, and thus no changes are needed on - introduction. - - Items decorated with ``@_api.rename_parameter`` or ``@_api.make_keyword_only`` - report the *new* (post deprecation) signature at runtime, and thus *should* be - updated on introduction. - - Items decorated with ``@_api.delete_parameter`` should include a default value hint - for the deleted parameter, even if it did not previously have one (e.g. - ``param: = ...``). Even so, the decorator changes the default value to a - sentinel value which should not be included in the type stub. Thus, Mypy Stubtest - needs to be informed of the inconsistency by placing the method into - :file:`ci/mypy-stubtest-allowlist.txt` under a heading indicating the deprecation - version number. - -.. _expire-deprecation: - -Expire deprecation +View documentation """""""""""""""""" -#. 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. - -#. Make appropriate changes to the type hints in the associated ``.pyi`` file. - - - Items marked with ``@_api.deprecated`` or ``@_api.deprecate_privatize_attribute`` - are to be removed on expiry. - - Items decorated with ``@_api.rename_parameter`` or ``@_api.make_keyword_only`` - will have been updated at introduction, and require no change now. - - Items decorated with ``@_api.delete_parameter`` will need to be updated to the - final signature, in the same way as the ``.py`` file signature is updated. - The entry in :file:`ci/mypy-stubtest-allowlist.txt` should be removed. - - Any other entries in :file:`ci/mypy-stubtest-allowlist.txt` under a version's - deprecations should be double checked, as only ``delete_parameter`` should normally - 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 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 -API. As discussed above, changing the existing API is cumbersome. Therefore, -take particular care when adding new API: - -- Mark helper functions and internal attributes as private by prefixing them - with an underscore. -- Carefully think about good names for your functions and variables. -- Try to adopt patterns and naming conventions from existing parts of the - Matplotlib API. -- Consider making as many arguments keyword-only as possible. See also - `API Evolution the Right Way -- Add Parameters Compatibly`__. - - __ 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 ------------------------------------ - -* If you have added new files or directories, or reorganized existing - ones, make sure the new files are included in the match patterns in - in *package_data* in :file:`setupext.py`. -* New modules *may* be typed inline or using parallel stub file like existing modules. - -C/C++ extensions ----------------- - -* Extensions may be written in C or C++. - -* Code style should conform to PEP7 (understanding that PEP7 doesn't - address C++, but most of its admonitions still apply). - -* Python/C interface code should be kept separate from the core C/C++ - code. The interface code should be named :file:`FOO_wrap.cpp` or - :file:`FOO_wrapper.cpp`. - -* Header file documentation (aka docstrings) should be in Numpydoc - format. We don't plan on using automated tools for these - docstrings, and the Numpydoc format is well understood in the - scientific Python community. +If you also built the documentation pages, you can view them using Codespaces. +Use the "Extensions" icon in the activity bar to install the "Live Server" +extension. Locate the ``doc/build/html`` folder in the Explorer, right click +the file you want to open and select "Open with Live Server." -* C/C++ code in the :file:`extern/` directory is vendored, and should be kept - close to upstream whenever possible. It can be modified to fix bugs or - implement new features only if the required changes cannot be made elsewhere - in the codebase. In particular, avoid making style fixes to it. -.. _keyword-argument-processing: - -Keyword argument processing ---------------------------- - -Matplotlib makes extensive use of ``**kwargs`` for pass-through customizations -from one function to another. A typical example is -`~matplotlib.axes.Axes.text`. The definition of `matplotlib.pyplot.text` is a -simple pass-through to `matplotlib.axes.Axes.text`:: - - # in pyplot.py - def text(x, y, s, fontdict=None, **kwargs): - return gca().text(x, y, s, fontdict=fontdict, **kwargs) - -`matplotlib.axes.Axes.text` (simplified for illustration) just -passes all ``args`` and ``kwargs`` on to ``matplotlib.text.Text.__init__``:: - - # in axes/_axes.py - def text(self, x, y, s, fontdict=None, **kwargs): - t = Text(x=x, y=y, text=s, **kwargs) - -and ``matplotlib.text.Text.__init__`` (again, simplified) -just passes them on to the `matplotlib.artist.Artist.update` method:: - - # in text.py - def __init__(self, x=0, y=0, text='', **kwargs): - super().__init__() - self.update(kwargs) - -``update`` does the work looking for methods named like -``set_property`` if ``property`` is a keyword argument. i.e., no one -looks at the keywords, they just get passed through the API to the -artist constructor which looks for suitably named methods and calls -them with the value. - -As a general rule, the use of ``**kwargs`` should be reserved for -pass-through keyword arguments, as in the example above. If all the -keyword args are to be used in the function, and not passed -on, use the key/value keyword args in the function definition rather -than the ``**kwargs`` idiom. - -In some cases, you may want to consume some keys in the local -function, and let others pass through. Instead of popping arguments to -use off ``**kwargs``, specify them as keyword-only arguments to the local -function. This makes it obvious at a glance which arguments will be -consumed in the function. For example, in -:meth:`~matplotlib.axes.Axes.plot`, ``scalex`` and ``scaley`` are -local arguments and the rest are passed on as -:meth:`~matplotlib.lines.Line2D` keyword arguments:: - - # in axes/_axes.py - def plot(self, *args, scalex=True, scaley=True, **kwargs): - lines = [] - for line in self._get_lines(*args, **kwargs): - self.add_line(line) - lines.append(line) - -.. _using_logging: - -Using logging for debug messages --------------------------------- - -Matplotlib uses the standard Python `logging` library to write verbose -warnings, information, and debug messages. Please use it! In all those places -you write `print` calls to do your debugging, try using `logging.debug` -instead! - - -To include `logging` in your module, at the top of the module, you need to -``import logging``. Then calls in your code like:: - - _log = logging.getLogger(__name__) # right after the imports - - # code - # more code - _log.info('Here is some information') - _log.debug('Here is some more detailed information') - -will log to a logger named ``matplotlib.yourmodulename``. - -If an end-user of Matplotlib sets up `logging` to display at levels more -verbose than ``logging.WARNING`` in their code with the Matplotlib-provided -helper:: - - plt.set_loglevel("debug") - -or manually with :: - - import logging - logging.basicConfig(level=logging.DEBUG) - import matplotlib.pyplot as plt - -Then they will receive messages like - -.. code-block:: none - - DEBUG:matplotlib.backends:backend MacOSX version unknown - DEBUG:matplotlib.yourmodulename:Here is some information - DEBUG:matplotlib.yourmodulename:Here is some more detailed information - -Avoid using pre-computed strings (``f-strings``, ``str.format``,etc.) for logging because -of security and performance issues, and because they interfere with style handlers. For -example, use ``_log.error('hello %s', 'world')`` rather than ``_log.error('hello -{}'.format('world'))`` or ``_log.error(f'hello {s}')``. - -Which logging level to use? -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -There are five levels at which you can emit messages. - -- `logging.critical` and `logging.error` are really only there for errors that - will end the use of the library but not kill the interpreter. -- `logging.warning` and `._api.warn_external` are used to warn the user, - see below. -- `logging.info` is for information that the user may want to know if the - program behaves oddly. They are not displayed by default. For instance, if - an object isn't drawn because its position is ``NaN``, that can usually - be ignored, but a mystified user could call - ``logging.basicConfig(level=logging.INFO)`` and get an error message that - says why. -- `logging.debug` is the least likely to be displayed, and hence can be the - most verbose. "Expected" code paths (e.g., reporting normal intermediate - steps of layouting or rendering) should only log at this level. - -By default, `logging` displays all log messages at levels higher than -``logging.WARNING`` to `sys.stderr`. - -The `logging tutorial`_ suggests that the difference between `logging.warning` -and `._api.warn_external` (which uses `warnings.warn`) is that -`._api.warn_external` should be used for things the user must change to stop -the warning (typically in the source), whereas `logging.warning` can be more -persistent. Moreover, note that `._api.warn_external` will by default only -emit a given warning *once* for each line of user code, whereas -`logging.warning` will display the message every time it is called. - -By default, `warnings.warn` displays the line of code that has the ``warn`` -call. This usually isn't more informative than the warning message itself. -Therefore, Matplotlib uses `._api.warn_external` which uses `warnings.warn`, -but goes up the stack and displays the first line of code outside of -Matplotlib. For example, for the module:: - - # in my_matplotlib_module.py - import warnings - - def set_range(bottom, top): - if bottom == top: - warnings.warn('Attempting to set identical bottom==top') - -running the script:: - - from matplotlib import my_matplotlib_module - my_matplotlib_module.set_range(0, 0) # set range - -will display - -.. code-block:: none - - UserWarning: Attempting to set identical bottom==top - warnings.warn('Attempting to set identical bottom==top') - -Modifying the module to use `._api.warn_external`:: - - from matplotlib import _api - - def set_range(bottom, top): - if bottom == top: - _api.warn_external('Attempting to set identical bottom==top') - -and running the same script will display - -.. code-block:: none +Open a pull request on Matplotlib +--------------------------------- - UserWarning: Attempting to set identical bottom==top - my_matplotlib_module.set_range(0, 0) # set range +Finally, go to the web page of *your fork* of the Matplotlib repo, and click +**Compare & pull request** to send your changes to the maintainers for review. +The base repository is ``matplotlib/matplotlib`` and the base branch is +generally ``main``. For more guidance, see GitHub's `pull request tutorial +`_. -.. _logging tutorial: https://docs.python.org/3/howto/logging.html#logging-basic-tutorial -.. _gitter: https://gitter.im/matplotlib/matplotlib -.. _community: https://gitter.im/matplotlib/community +For more detailed instructions on how to set up Matplotlib for development and +best practices for contribution, see :ref:`installing_for_devs` and +:ref:`development-workflow`. diff --git a/doc/devel/development_setup.rst b/doc/devel/development_setup.rst index d06ebfc5918f..be99bed2fe5f 100644 --- a/doc/devel/development_setup.rst +++ b/doc/devel/development_setup.rst @@ -1,3 +1,5 @@ +.. highlight:: bash + .. redirect-from:: /devel/gitwash/configure_git .. redirect-from:: /devel/gitwash/dot2_dot3 .. redirect-from:: /devel/gitwash/following_latest @@ -163,7 +165,7 @@ must be installed separately. .. toctree:: :maxdepth: 2 - ../users/installing/dependencies + ../install/dependencies .. _development-install: @@ -171,40 +173,56 @@ must be installed separately. Install Matplotlib in editable mode =================================== -Install Matplotlib in editable mode from the :file:`matplotlib` directory -using the command :: +Install Matplotlib in editable mode from the :file:`matplotlib` directory using the +command :: - python -m pip install -ve . + python -m pip install --verbose --no-build-isolation --editable ".[dev]" +The 'editable/develop mode' builds everything and places links in your Python environment +so that Python will be able to import Matplotlib from your development source directory. +This allows you to import your modified version of Matplotlib without having to +re-install after changing a ``.py`` or compiled extension file. -The 'editable/develop mode', builds everything and places links in your Python -environment so that Python will be able to import Matplotlib from your -development source directory. This allows you to import your modified version -of Matplotlib without re-installing after every change. Note that this is only -true for ``*.py`` files. If you change the C-extension source (which might -also happen if you change branches) you will have to re-run -``python -m pip install -ve .`` +When working on a branch that does not have Meson enabled, meaning it does not +have :ghpull:`26621` in its history (log), you will have to reinstall from source +each time you change any compiled extension code. 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 `. For a list of environment variables -you can set before install, see :ref:`environment-variables`. +or :ref:`open an issue `. + + +Build options +------------- +If you are working heavily with files that need to be compiled, you may want to +inspect the compilation log. This can be enabled by setting the environment +variable :envvar:`MESONPY_EDITABLE_VERBOSE` or by setting the ``editable-verbose`` +config during installation :: + + python -m pip install --no-build-isolation --config-settings=editable-verbose=true --editable . + +For more information on installation and other configuration options, see the +Meson Python :external+meson-python:ref:`editable installs guide `. + +For a list of the other environment variables you can set before install, see :ref:`environment-variables`. + Verify the Installation ======================= -Run the following command to make sure you have correctly installed Matplotlib in editable mode. -The command should be run when the virtual environment is activated :: +Run the following command to make sure you have correctly installed Matplotlib in +editable mode. The command should be run when the virtual environment is activated:: python -c "import matplotlib; print(matplotlib.__file__)" This command should return : ``\lib\matplotlib\__init__.py`` -We encourage you to run tests and build docs to verify that the code installed correctly and that the docs build cleanly, -so that when you make code or document related changes you are aware of the existing issues beforehand. +We encourage you to run tests and build docs to verify that the code installed correctly +and that the docs build cleanly, so that when you make code or document related changes +you are aware of the existing issues beforehand. - * Run test cases to verify installation :ref:`testing` - * Verify documentation build :ref:`documenting-matplotlib` +* Run test cases to verify installation :ref:`testing` +* Verify documentation build :ref:`documenting-matplotlib` .. _pre-commit-hooks: diff --git a/doc/devel/development_workflow.rst b/doc/devel/development_workflow.rst index edb21bb862cd..23e2c17732e2 100644 --- a/doc/devel/development_workflow.rst +++ b/doc/devel/development_workflow.rst @@ -452,48 +452,86 @@ Automated tests Whenever a pull request is created or updated, various automated test tools will run on all supported platforms and versions of Python. -* Make sure the Linting, GitHub Actions, AppVeyor, CircleCI, and Azure - pipelines are passing before merging (All checks are listed at the bottom of - the GitHub page of your pull request). Here are some tips for finding the - cause of the test failure: - - - If *Linting* fails, you have a code style issue, which will be listed - as annotations on the pull request's diff. - - If *Mypy* or *Stubtest* fails, you have inconsistency in type hints, which - will be listed as annotations in the diff. - - If a GitHub Actions or AppVeyor run fails, search the log for ``FAILURES``. - The subsequent section will contain information on the failed tests. - - If CircleCI fails, likely you have some reStructuredText style issue in - the docs. Search the CircleCI log for ``WARNING``. - - If Azure pipelines fail with an image comparison error, you can find the - images as *artifacts* of the Azure job: - - - Click *Details* on the check on the GitHub PR page. - - Click *View more details on Azure Pipelines* to go to Azure. - - On the overview page *artifacts* are listed in the section *Related*. - - -* Codecov and CodeQL are currently for information only. Their failure is not - necessarily a blocker. - * tox_ is not used in the automated testing. It is supported for testing locally. .. _tox: https://tox.readthedocs.io/ -* If you know only a subset of CIs need to be run, this can be controlled on - individual commits by including the following substrings in commit messages: - - - ``[ci doc]``: restrict the CI to documentation checks. For when you only - changed documentation (this skip is automatic if the changes are only under - ``doc/`` or ``galleries/``). - - ``[skip circle]``: skip the documentation build check. For when you didn't - change documentation. - - Unit tests can be turned off for individual platforms with - - - ``[skip actions]``: GitHub Actions - - ``[skip appveyor]`` (must be in the first line of the commit): AppVeyor - - ``[skip azp]``: Azure Pipelines +* Codecov and CodeQL are currently for information only. Their failure is not + necessarily a blocker. - - ``[skip ci]``: skip all CIs. Use this only if you know your changes do not - need to be tested at all, which is very rare. +Make sure the Linting, GitHub Actions, AppVeyor, CircleCI, and Azure pipelines are +passing before merging. All checks are listed at the bottom of the GitHub page of your +pull request. + +.. list-table:: + :header-rows: 1 + :stub-columns: 1 + :widths: 20 20 60 + + * - Name + - Check + - Tips for finding cause of failure + * - Linting + - :ref:`code style ` + - Errors are displayed as annotations on the pull request diff. + * - | Mypy + | Stubtest + - :ref:`static type hints ` + - Errors are displayed as annotations on the pull request diff. + * - CircleCI + - :ref:`documentation build ` + - Search the CircleCI log for ``WARNING``. + * - | GitHub Actions + | AppVeyor + | Azure pipelines + - :ref:`tests ` + - | Search the log for ``FAILURES``. Subsequent section should contain information + on failed tests. + | + | On Azure, find the images as *artifacts* of the Azure job: + | 1. Click *Details* on the check on the GitHub PR page. + | 2. Click *View more details on Azure Pipelines* to go to Azure. + | 3. On the overview page *artifacts* are listed in the section *Related*. + +Skip CI checks +-------------- + +If you know only a subset of CI checks need to be run, you can skip unneeded CI checks +on individual commits by including the following strings in the commit message: + +.. list-table:: + :header-rows: 1 + :stub-columns: 1 + :widths: 25 20 55 + + * - String + - Effect + - Notes + * - ``[ci doc]`` + - Only run documentation checks. + - | For when you have only changed documentation. + | ``[ci doc]`` is applied automatically when the changes are only to files in + ``doc/**/`` or ``galleries/**/`` + * - ``[skip doc]`` + - Skip documentation checks. + - For when you didn't change documentation. + * - ``[skip appveyor]`` + - Skip AppVeyor run. + - Substring must be in first line of commit message. + * - ``[skip azp]`` + - Skip Azure Pipelines. + - + * - ``[skip actions]`` + - Skip GitHub Actions. + - + * - ``[skip ci]`` + - Skip all CI checks. + - Use only for changes where documentation checks and unit tests do not apply. + + +``[skip actions]`` and ``[skip ci]`` only skip Github Actions CI workflows that are +triggered on ``on: push`` and ``on: pull_request`` events. For more information, +see `Skipping workflow runs`_. + +.. _`Skipping workflow runs`: https://docs.github.com/en/actions/managing-workflow-runs/skipping-workflow-runs diff --git a/doc/devel/document.rst b/doc/devel/document.rst index 68f2d3a674c9..620c12c8db1c 100644 --- a/doc/devel/document.rst +++ b/doc/devel/document.rst @@ -64,6 +64,11 @@ used. To build the documentation in html format, cd into :file:`doc/` and run: make html +.. note:: + + Since the documentation is very large, the first build may take 10-20 minutes, + depending on your machine. Subsequent builds will be faster. + Other useful invocations include .. code-block:: sh @@ -142,7 +147,7 @@ It is useful to strive for consistency in the Matplotlib documentation. Here are some formatting and style conventions that are used. Section formatting -~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^ Use `sentence case `__ ``Upper lower`` for section titles, e.g., ``Possible hangups`` rather than @@ -163,8 +168,27 @@ for section markup characters, i.e.: This may not yet be applied consistently in existing docs. +Table formatting +^^^^^^^^^^^^^^^^ +Given the size of the table and length of each entry, use: + ++-------------+-------------------------------+--------------------+ +| | small table | large table | ++-------------+-------------------------------+--------------------+ +| short entry | `simple or grid table`_ | `grid table`_ | ++-------------+-------------------------------+--------------------+ +| long entry | `list table`_ | `csv table`_ | ++-------------+-------------------------------+--------------------+ + +For more information, see `rst tables `_. + +.. _`simple or grid table`: https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#tables +.. _`grid table`: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#grid-tables +.. _`list table`: https://docutils.sourceforge.io/docs/ref/rst/directives.html#list-table +.. _`csv table`: https://docutils.sourceforge.io/docs/ref/rst/directives.html#csv-table-1 + Function arguments -~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^ Function arguments and keywords within docstrings should be referred to using the ``*emphasis*`` role. This will keep Matplotlib's documentation consistent @@ -215,7 +239,7 @@ Examples: .. code-block:: rst - See the :doc:`/users/installing/index` + See the :doc:`/install/index` See the tutorial :ref:`quick_start` @@ -223,14 +247,14 @@ Examples: will render as: - See the :doc:`/users/installing/index` + See the :doc:`/install/index` See the tutorial :ref:`quick_start` See the example :doc:`/gallery/lines_bars_and_markers/simple_plot` Sections can also be given reference labels. For instance from the -:doc:`/users/installing/index` link: +:doc:`/install/index` link: .. code-block:: rst @@ -293,7 +317,10 @@ target is unambiguous you can simply leave them out: `.LineCollection` -and the link still works: `.LineCollection`. +and the link still works: `.LineCollection`. Note that you should typically include +the leading dot. It tells Sphinx to look for the given name in the whole project. +See also the explanation at `Sphinx: Cross-referencing Python objects +`_. If there are multiple code elements with the same name (e.g. ``plot()`` is a method in multiple classes), you'll have to extend the definition: @@ -445,7 +472,8 @@ and the Sphinx_ documentation. Some Matplotlib-specific formatting conventions to keep in mind: Quote positions -~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^ + The quotes for single line docstrings are on the same line (pydocstyle D200):: def get_linewidth(self): @@ -461,7 +489,8 @@ The quotes for multi-line docstrings are on separate lines (pydocstyle D213):: """ Function arguments -~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^ + Function arguments and keywords within docstrings should be referred to using the ``*emphasis*`` role. This will keep Matplotlib's documentation consistent with Python's documentation: @@ -478,7 +507,8 @@ Do not use the ```default role``` or the ````literal```` role: Quotes for strings -~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^ + Matplotlib does not have a convention whether to use single-quotes or double-quotes. There is a mixture of both in the current code. @@ -495,7 +525,8 @@ slightly improve the rendered docs, they are cumbersome to type and difficult to read in plain-text docs. Parameter type descriptions -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + The main goal for parameter type descriptions is to be readable and understandable by humans. If the possible types are too complex use a simplification for the type description and explain the type more @@ -534,7 +565,8 @@ Non-numeric homogeneous sequences are described as lists, e.g.:: list of `.Artist` Reference types -~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^ + Generally, the rules from referring-to-other-code_ apply. More specifically: Use full references ```~matplotlib.colors.Normalize``` with an @@ -550,7 +582,8 @@ Use abbreviated links ```.Normalize``` in the text. A `.Normalize` instance is used to scale luminance data to 0, 1. Default values -~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^ + As opposed to the numpydoc guide, parameters need not be marked as *optional* if they have a simple default: @@ -592,7 +625,8 @@ effect. ``See also`` sections -~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^ + Sphinx automatically links code elements in the definition blocks of ``See also`` sections. No need to use backticks there:: @@ -602,7 +636,8 @@ also`` sections. No need to use backticks there:: axhline : horizontal line across the Axes Wrap parameter lists -~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^ + Long parameter lists should be wrapped using a ``\`` for continuation and starting on the new line without any indent (no indent because pydoc will parse the docstring and strip the line continuation so that indent would @@ -627,7 +662,8 @@ Alternatively, you can describe the valid parameter values in a dedicated section of the docstring. rcParams -~~~~~~~~ +^^^^^^^^ + rcParams can be referenced with the custom ``:rc:`` role: :literal:`:rc:\`foo\`` yields ``rcParams["foo"] = 'default'``, which is a link to the :file:`matplotlibrc` file description. @@ -668,7 +704,7 @@ in that case, they can be documented as an ``.. ACCEPTS:`` block, e.g. for .. code-block:: python - def set_xlim(self, ...): + def set_xlim(self, left=None, right=None): """ Set the x-axis view limits. diff --git a/doc/devel/index.rst b/doc/devel/index.rst index 04f83f22227f..9744d757c342 100644 --- a/doc/devel/index.rst +++ b/doc/devel/index.rst @@ -13,14 +13,67 @@ Contribute 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 -and implementing new features, helping the community... +:octicon:`heart;1em;sd-text-info` Thank you for your interest in helping to improve +Matplotlib! :octicon:`heart;1em;sd-text-info` + +There are various ways to contribute: optimizing and refactoring code, detailing +unclear documentation and writing new examples, helping the community, reporting +and fixing bugs and requesting and implementing new features... + +.. _submitting-a-bug-report: +.. _request-a-new-feature: + +.. grid:: 1 1 2 2 + + .. grid-item-card:: + :class-header: sd-fs-5 + + :octicon:`bug;1em;sd-text-info` **Submit a bug report** + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + We have preloaded the issue creation page with a Markdown form that you can + use to provide relevant context. Thank you for your help in keeping bug reports + complete, targeted and descriptive. + + .. button-link:: https://github.com/matplotlib/matplotlib/issues/new/choose + :expand: + :color: primary + + Report a bug + + .. grid-item-card:: + :class-header: sd-fs-5 + + :octicon:`light-bulb;1em;sd-text-info` **Request a new feature** + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + We will give feedback on the feature proposal. Since + Matplotlib is an open source project with limited resources, we encourage + users to then also :ref:`participate in the implementation `. + + .. button-link:: https://github.com/matplotlib/matplotlib/issues/new/choose + :expand: + :color: primary + + Request a feature + + +We welcome you to get more involved with the Matplotlib project! If you are new +to contributing, we recommend that you first read our +:ref:`contributing guide`. If you are contributing code or +documentation, please follow our guides for setting up and managing a +:ref:`development environment and workflow`. +For code, documentation, or triage, please follow the corresponding +:ref:`contribution guidelines `. New contributors ================ +.. toctree:: + :hidden: + + contribute + .. grid:: 1 1 2 2 :class-row: sd-align-minor-center @@ -31,11 +84,9 @@ New contributors :octicon:`question;1em;sd-text-info` :ref:`Where should I ask questions? ` - :octicon:`issue-opened;1em;sd-text-info` :ref:`What are "good-first-issues"? ` + :octicon:`git-pull-request;1em;sd-text-info` :ref:`How do I work on an issue? ` - :octicon:`git-pull-request;1em;sd-text-info` :ref:`How do I claim an issue? ` - - :octicon:`codespaces;1em;sd-text-info` :ref:`How do I start a pull request? ` + :octicon:`codespaces;1em;sd-text-info` :ref:`How do I start a pull request? ` .. grid-item:: @@ -44,39 +95,27 @@ New contributors :class-row: sd-fs-5 .. grid-item-card:: - :link: request-a-new-feature + :link: contribute_code :link-type: ref :shadow: none - :octicon:`light-bulb;1em;sd-text-info` Request new feature + :octicon:`code;1em;sd-text-info` Contribute code .. grid-item-card:: - :link: submitting-a-bug-report + :link: contribute_documentation :link-type: ref :shadow: none - :octicon:`bug;1em;sd-text-info` Submit bug report + :octicon:`note;1em;sd-text-info` Write documentation .. grid-item-card:: - :link: contributing-code + :link: other_ways_to_contribute :link-type: ref :shadow: none - :octicon:`code;1em;sd-text-info` Contribute code + :octicon:`globe;1em;sd-text-info` Build community - .. grid-item-card:: - :link: documenting-matplotlib - :link-type: ref - :shadow: none - :octicon:`note;1em;sd-text-info` Write documentation - -If you are new to contributing, we recommend that you first read our -:ref:`contributing guide`. If you are contributing code or -documentation, please follow our guides for setting up and managing a -:ref:`development environment and workflow`. -For code, documentation, or triage, please follow the corresponding -:ref:`contribution guidelines `. .. _development_environment: @@ -130,12 +169,11 @@ Policies and guidelines **Code** ^^^ - | :ref:`coding_guidelines` - .. toctree:: :maxdepth: 1 coding_guide + api_changes testing .. grid-item-card:: @@ -149,16 +187,19 @@ Policies and guidelines document style_guide + tag_guidelines .. grid-item-card:: :shadow: none - **Triage** + **Triage And Review** ^^^ - | :ref:`bug_triaging` - | :ref:`triage_team` - | :ref:`triage_workflow` + .. toctree:: + :maxdepth: 1 + + triage + pr_guide .. grid-item-card:: :shadow: none @@ -173,11 +214,3 @@ Policies and guidelines communication_guide min_dep_policy MEP/index - -.. toctree:: - :hidden: - - contribute - triage - license - color_changes diff --git a/doc/devel/license.rst b/doc/devel/license.rst index 8474fa432ff4..7596f2f92348 100644 --- a/doc/devel/license.rst +++ b/doc/devel/license.rst @@ -1,7 +1,7 @@ .. _license-discussion: -Licenses -======== +Licenses for contributed code +============================= Matplotlib only uses BSD compatible code. If you bring in code from another project make sure it has a PSF, BSD, MIT or compatible license diff --git a/doc/devel/min_dep_policy.rst b/doc/devel/min_dep_policy.rst index 475985992bbc..a702b5930fd8 100644 --- a/doc/devel/min_dep_policy.rst +++ b/doc/devel/min_dep_policy.rst @@ -4,10 +4,11 @@ Dependency version policy ========================= -For the purpose of this document, 'minor version' is in the sense of -SemVer (major, minor, patch) and includes both major and minor -releases. For projects that use date-based versioning, every release -is a 'minor version'. +For the purpose of this document, 'minor version' is in the sense of SemVer +(major, minor, patch) or 'meso version' in the sense of `EffVer +`_ (macro, meso, micro). It includes both +major/macro and minor/meso releases. For projects that use date-based +versioning, every release is a 'minor version'. Matplotlib follows `NEP 29 `__. @@ -22,7 +23,7 @@ Matplotlib supports: - All minor versions of ``numpy`` released in the 24 months prior to the project, and at minimum the last three minor versions. -In ``setup.py``, the ``python_requires`` variable should be set to +In :file:`pyproject.toml`, the ``requires-python`` variable should be set to the minimum supported version of Python. All supported minor versions of Python should be in the test matrix and have binary artifacts built for the release. @@ -87,6 +88,7 @@ specification of the dependencies. ========== ======== ====== Matplotlib Python NumPy ========== ======== ====== +`3.9`_ 3.9 1.23.0 `3.8`_ 3.9 1.21.0 `3.7`_ 3.8 1.20.0 `3.6`_ 3.8 1.19.0 @@ -107,6 +109,7 @@ Matplotlib Python NumPy 1.0 2.4 1.1 ========== ======== ====== +.. _`3.9`: https://matplotlib.org/3.9.0/devel/dependencies.html .. _`3.8`: https://matplotlib.org/3.8.0/devel/dependencies.html .. _`3.7`: https://matplotlib.org/3.7.0/devel/dependencies.html .. _`3.6`: https://matplotlib.org/3.6.0/devel/dependencies.html diff --git a/doc/devel/pr_guide.rst b/doc/devel/pr_guide.rst new file mode 100644 index 000000000000..055998549e78 --- /dev/null +++ b/doc/devel/pr_guide.rst @@ -0,0 +1,369 @@ +.. _pr-guidelines: + +*********************** +Pull request guidelines +*********************** + +`Pull requests (PRs) on GitHub +`__ +are the mechanism for contributing to Matplotlib's code and documentation. + +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. + +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 ` + + +Summary for pull request authors +================================ + +We recommend that you check that your contribution complies with the following +guidelines before submitting a pull request: + +.. rst-class:: checklist + +* Changes, both new features and bugfixes, should have good test coverage. See + :ref:`testing` for more details. + +* Update the :ref:`documentation ` if necessary. + +* 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 `. + +* If you add a major new feature or change the API in a backward-incompatible + way, please document it as described in :ref:`api_changes`. + +* 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`. + +When opening a pull request on Github, please ensure that: + +.. rst-class:: checklist + +* Changes were made on a :ref:`feature branch `. + +* :ref:`pre-commit ` checks for spelling, formatting, etc pass + +* The pull request targets the :ref:`main branch ` + +* 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 `__. + +* :ref:`pr-automated-tests` pass + +For guidance on creating and managing a pull request, please see our +:ref:`contributing ` and :ref:`pull request workflow ` +guides. + + +Summary for pull request reviewers +================================== + +.. redirect-from:: /devel/maintainer_workflow + +**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. + +When reviewing, please ensure that the pull request satisfies the following +requirements before merging it: + +Content +------- + +.. rst-class:: checklist + +* Is the feature / bugfix reasonable? +* Does the PR conform with the :ref:`coding_guidelines`? +* Is the :ref:`documentation ` (docstrings, examples, + what's new, API changes) updated? +* Is the change purely stylistic? Generally, such changes are discouraged when + not part of other non-stylistic work because it obscures the git history of + functional changes to the code. Reflowing a method or docstring as part of a + larger refactor/rewrite is acceptable. + +Workflow +-------- +.. rst-class:: checklist + +* Make sure all :ref:`automated tests ` pass. +* The PR should :ref:`target the main branch `. +* Tag with descriptive :ref:`labels `. +* Set the :ref:`milestone `. +* Keep an eye on the :ref:`number of commits `. +* Approve if all of the above topics are handled. +* :ref:`Merge ` if a sufficient number of approvals is reached. + +.. _pr-guidelines-details: + +Detailed guidelines +=================== + +.. _pr-documentation: + +Documentation +------------- + +* Every new feature should be documented. If it's a new module, don't + forget to add a new rst file to the API docs. + +* Each high-level plotting function should have a small example in + the ``Examples`` section of the docstring. This should be as simple as + possible to demonstrate the method. More complex examples should go into + a dedicated example file in the :file:`examples` directory, which will be + rendered to the examples gallery in the documentation. + +* Build the docs and make sure all formatting warnings are addressed. + +* See :ref:`documenting-matplotlib` for our documentation style guide. + +.. _pr-labels: + +Labels +------ + +* If you have the rights to set labels, tag the PR with descriptive labels. + See the `list of labels `__. +* If the PR makes changes to the wheel building Action, add the + "Run cibuildwheel" label to enable testing wheels. + +.. _pr-milestones: + +Milestones +---------- + +Set the milestone according to these guidelines: + +* *New features and API changes* are milestoned for the next meso release + ``v3.N.0``. + +* *Bugfixes, tests for released code, and docstring changes* may be milestoned + for the next micro release ``v3.N.M``. + +* *Documentation changes* (only .rst files and examples) may be milestoned + ``v3.N-doc``. + +If multiple rules apply, choose the first matching from the above list. See +:ref:`backport-strategy` for detailed guidance on what should or should not be +backported. + +The milestone marks the release a PR should go into. It states intent, but can +be changed because of release planning or re-evaluation of the PR scope and +maturity. + +All Pull Requests should target the main branch. The milestone tag triggers +an :ref:`automatic backport ` for milestones which have +a corresponding branch. + +.. _pr-merging: + +Merging +------- + +* Documentation and examples may be merged by the first reviewer. Use + the threshold "is this better than it was?" as the review criteria. + +* For code changes (anything in ``src`` or ``lib``) at least two + core developers (those with commit rights) should review all pull + requests. If you are the first to review a PR and approve of the + changes use the GitHub `'approve review' + `__ + tool to mark it as such. If you are a subsequent reviewer please + approve the review and if you think no more review is needed, merge + the PR. + + Ensure that all API changes are documented in a file in one of the + subdirectories of :file:`doc/api/next_api_changes`, and significant new + features have an entry in :file:`doc/user/whats_new`. + + - If a PR already has a positive review, a core developer (e.g. the first + reviewer, but not necessarily) may champion that PR for merging. In order + to do so, they should ping all core devs both on GitHub and on the dev + mailing list, and label the PR with the "Merge with single review?" label. + Other core devs can then either review the PR and merge or reject it, or + simply request that it gets a second review before being merged. If no one + asks for such a second review within a week, the PR can then be merged on + the basis of that single review. + + A core dev should only champion one PR at a time and we should try to keep + the flow of championed PRs reasonable. + +* Do not self merge, except for 'small' patches to un-break the CI or + when another reviewer explicitly allows it (ex, "Approve modulo CI + passing, may self merge when green"). + +.. _pr-automated-tests: + +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 :ref:`communication-channels` + +.. _pr-squashing: + +Number of commits and squashing +------------------------------- + +* Squashing is case-by-case. The balance is between burden on the + contributor, keeping a relatively clean history, and keeping a + history usable for bisecting. The only time we are really strict + about it is to eliminate binary files (ex multiple test image + re-generations) and to remove upstream merges. + +* Do not let perfect be the enemy of the good, particularly for + documentation or example PRs. If you find yourself making many + small suggestions, either open a PR against the original branch, + push changes to the contributor branch, or merge the PR and then + open a new PR against upstream. + +* If you push to a contributor branch leave a comment explaining what + you did, ex "I took the liberty of pushing a small clean-up PR to + your branch, thanks for your work.". If you are going to make + substantial changes to the code or intent of the PR please check + with the contributor first. + + +.. _branches_and_backports: + +Branches and backports +====================== + +Current branches +---------------- +The current active branches are + +*main* + The current development version. Future meso (*v3.N.0*) or macro (*v4.0.0*) will be + branched from this. + +*v3.N.x* + Maintenance branch for Matplotlib 3.N. Future micro releases will be + tagged from this. + +*v3.N.M-doc* + Documentation for the current micro release. On a micro release, this will be + replaced by a properly named branch for the new release. + + +.. _pr-branch-selection: + +Branch selection for pull requests +---------------------------------- + +Generally, all pull requests should target the main branch. + +Other branches are fed through :ref:`automatic ` or +:ref:`manual `. Directly +targeting other branches is only rarely necessary for special maintenance +work. + +.. _backport-strategy: + +Backport strategy +----------------- + +Backports to the micro release branch (*v3.N.x*) are the changes that will be +included in the next patch (aka bug-fix) release. The goal of the patch +releases is to fix bugs without adding any new regressions or behavior changes. +We will always attempt to backport: + +- critical bug fixes (segfault, failure to import, things that the + user cannot work around) +- fixes for regressions introduced in the last two meso releases + +and may attempt to backport fixes for regressions introduced in older releases. + +In the case where the backport is not clean, for example if the bug fix is +built on top of other code changes we do not want to backport, balance the +effort and risk of re-implementing the bug fix vs the severity of the bug. +When in doubt, err on the side of not backporting. + +When backporting a Pull Request fails or is declined, re-milestone the original +PR to the next meso release and leave a comment explaining why. + +The only changes backported to the documentation branch (*v3.N.M-doc*) +are changes to :file:`doc` or :file:`galleries`. Any changes to :file:`lib` +or :file:`src`, including docstring-only changes, must not be backported to +this branch. + + +.. _automated-backports: + +Automated backports +------------------- + +We use MeeseeksDev bot to automatically backport merges to the correct +maintenance branch base on the milestone. To work properly the +milestone must be set before merging. If you have commit rights, the +bot can also be manually triggered after a merge by leaving a message +``@meeseeksdev backport to BRANCH`` on the PR. If there are conflicts +MeeseeksDev will inform you that the backport needs to be done +manually. + +The target branch is configured by putting ``on-merge: backport to +TARGETBRANCH`` in the milestone description on it's own line. + +If the bot is not working as expected, please report issues to +`MeeseeksDev `__. + + +.. _manual-backports: + +Manual backports +---------------- + +When doing backports please copy the form used by MeeseeksDev, +``Backport PR #XXXX: TITLE OF PR``. If you need to manually resolve +conflicts make note of them and how you resolved them in the commit +message. + +We do a backport from main to v2.2.x assuming: + +* ``matplotlib`` is a read-only remote branch of the matplotlib/matplotlib repo + +The ``TARGET_SHA`` is the hash of the merge commit you would like to +backport. This can be read off of the GitHub PR page (in the UI with +the merge notification) or through the git CLI tools. + +Assuming that you already have a local branch ``v2.2.x`` (if not, then +``git checkout -b v2.2.x``), and that your remote pointing to +``https://github.com/matplotlib/matplotlib`` is called ``upstream``: + +.. code-block:: bash + + git fetch upstream + git checkout v2.2.x # or include -b if you don't already have this. + git reset --hard upstream/v2.2.x + git cherry-pick -m 1 TARGET_SHA + # resolve conflicts and commit if required + +Files with conflicts can be listed by ``git status``, +and will have to be fixed by hand (search on ``>>>>>``). Once +the conflict is resolved, you will have to re-add the file(s) to the branch +and then continue the cherry pick: + +.. code-block:: bash + + git add lib/matplotlib/conflicted_file.py + git add lib/matplotlib/conflicted_file2.py + git cherry-pick --continue + +Use your discretion to push directly to upstream or to open a PR; be +sure to push or PR against the ``v2.2.x`` upstream branch, not ``main``! diff --git a/doc/devel/release_guide.rst b/doc/devel/release_guide.rst index 4adc4546e879..5034bde1eefc 100644 --- a/doc/devel/release_guide.rst +++ b/doc/devel/release_guide.rst @@ -12,10 +12,35 @@ Release guide A guide for developers who are doing a Matplotlib release. -.. note:: - This assumes that a read-only remote for the canonical repository is - ``remote`` and a read/write remote is ``DANGER`` +Versioning Scheme +================= + +Maplotlib follows the `Intended Effort Versioning (EffVer) `_ +versioning scheme: *macro.meso.micro*. + + +*macro* + A release that we expect a large effort from our users to upgrade to. The v1 to v2 transition + included a complete overhaul of the default styles and the v2 to v3 transition involved + dropping support for Python 2. + + Future macro versions would include changes of a comparable scale that can not be done + incrementally in meso releases. + +*meso* + A release that we expect some effort from our users to upgrade to. We target a + *Meso* release every 6 months. These release are primarily intended to release + new features to our users, however they also contain intentional feature deprecations and + removals per :ref:`our policy `. + +*micro* + A release that we expect users to require little to no effort to upgrade to. Per + our :ref:`backport-strategy` we only backport bug fixes to the maintenance branch. + We expect minimal impact on users other than possibly breaking work arounds to a + fixed bug or `bugs being used as features `_. + + These are released as-needed, but typically every 1-2 months between meso releases. .. _release_feature_freeze: @@ -23,9 +48,15 @@ Release guide Making the release branch ========================= -When a new minor release (vX.Y.0) is approaching, a new release branch must be made. +.. note:: + + This assumes that a read-only remote for the canonical repository is + ``remote`` and a read/write remote is ``DANGER`` + + +When a new meso release (vX.Y.0) is approaching, a new release branch must be made. When precisely this should happen is up to the release manager, but this point is where -most new features intended for the minor release are merged and you are entering a +most new features intended for the meso release are merged and you are entering a feature freeze (i.e. newly implemented features will be going into vX.Y+1). This does not necessarily mean that no further changes will be made prior to release, just that those changes will be made using the backport system. @@ -50,12 +81,12 @@ Micro versions should instead read:: on-merge: backport to v3.7.x Check all active milestones for consistency. Older milestones should also backport -to higher minor versions (e.g. ``v3.6.3`` and ``v3.6-doc`` should backport to both +to higher meso versions (e.g. ``v3.6.3`` and ``v3.6-doc`` should backport to both ``v3.6.x`` and ``v3.7.x`` once the ``v3.7.x`` branch exists and while PR backports are still targeting ``v3.6.x``) -Create the milestone for the next-next minor release (i.e. ``v3.9.0``, as ``v3.8.0`` -should already exist). While most active items should go in the next minor release, +Create the milestone for the next-next meso release (i.e. ``v3.9.0``, as ``v3.8.0`` +should already exist). While most active items should go in the next meso release, this milestone can help with longer term planning, especially around deprecation cycles. @@ -142,15 +173,15 @@ are going to tag on and delete the doc branch on GitHub. Update supported versions in Security Policy -------------------------------------------- -When making major or minor releases, update the supported versions in the Security +When making macro or meso releases, update the supported versions in the Security Policy in :file:`SECURITY.md`. -For minor version release update the table in :file:`SECURITY.md` to specify that the -two most recent minor releases in the current major version series are supported. +For meso version release update the table in :file:`SECURITY.md` to specify that the +two most recent meso releases in the current macro version series are supported. -For a major version release update the table in :file:`SECURITY.md` to specify that the -last minor version in the previous major version series is still supported. Dropping -support for the last version of a major version series will be handled on an ad-hoc +For a macro version release update the table in :file:`SECURITY.md` to specify that the +last meso version in the previous macro version series is still supported. Dropping +support for the last version of a macro version series will be handled on an ad-hoc basis. Update release notes @@ -159,7 +190,7 @@ Update release notes What's new ^^^^^^^^^^ -*Only needed for major and minor releases. Bugfix releases should not have new +*Only needed for macro and meso releases. Bugfix releases should not have new features.* Merge the contents of all the files in :file:`doc/users/next_whats_new/` into a single @@ -169,8 +200,8 @@ files. API changes ^^^^^^^^^^^ -*Primarily needed for major and minor releases. We may sometimes have API -changes in bugfix releases.* +*Primarily needed for macro and meso releases. We may sometimes have API +changes in micro releases.* Merge the contents of all the files in :file:`doc/api/next_api_changes/` into a single file :file:`doc/api/prev_api_changes/api_changes_{X}.{Y}.{Z}.rst` and delete the @@ -181,7 +212,7 @@ Release notes TOC Update :file:`doc/users/release_notes.rst`: -- For major and minor releases add a new section +- For macro and meso releases add a new section .. code:: rst @@ -193,7 +224,7 @@ Update :file:`doc/users/release_notes.rst`: prev_whats_new/whats_new_X.Y.0.rst ../api/prev_api_changes/api_changes_X.Y.0.rst prev_whats_new/github_stats_X.Y.0.rst -- For bugfix releases add the GitHub stats and (if present) the API changes to +- For micro releases add the GitHub stats and (if present) the API changes to the existing X.Y section .. code:: rst @@ -206,8 +237,8 @@ Update version switcher Update ``doc/_static/switcher.json``: -- If a bugfix release, :samp:`{X}.{Y}.{Z}`, no changes are needed. -- If a major release, :samp:`{X}.{Y}.0`, change the name of :samp:`name: {X}.{Y+1} +- If a micro release, :samp:`{X}.{Y}.{Z}`, no changes are needed. +- If a macro release, :samp:`{X}.{Y}.0`, change the name of :samp:`name: {X}.{Y+1} (dev)` and :samp:`name: {X}.{Y} (stable)` as well as adding a new version for the previous stable (:samp:`name: {X}.{Y-1}`). @@ -259,8 +290,8 @@ Finally, push the tag to GitHub:: Congratulations, the scariest part is done! This assumes the release branch has already been made. -Usually this is done at the time of feature freeze for a minor release (which often -coincides with the last patch release of the previous minor version) +Usually this is done at the time of feature freeze for a meso release (which often +coincides with the last micro release of the previous meso version) .. [#] The tarball that is provided by GitHub is produced using `git archive`_. We use setuptools_scm_ which uses a format string in @@ -298,7 +329,7 @@ with the ``v3.7-doc`` milestone to both the ``v3.7.x`` branch and the ``v3.7.0-d on-merge: backport to v3.7.0-doc Check all active milestones for consistency. Older doc milestones should also backport to -higher minor versions (e.g. ``v3.6-doc`` should backport to both ``v3.6.x`` and ``v3.7.x`` +higher meso versions (e.g. ``v3.6-doc`` should backport to both ``v3.6.x`` and ``v3.7.x`` if the ``v3.7.x`` branch exists) @@ -315,8 +346,8 @@ automatically produce one once the tag is pushed). Add the DOI post-fix and vers the dictionary in :file:`tools/cache_zenodo_svg.py` and run the script. This will download the new SVG to :file:`doc/_static/zenodo_cache/{postfix}.svg` and -edit :file:`doc/users/project/citing.rst`. Commit the new SVG, the change to -:file:`tools/cache_zenodo_svg.py`, and the changes to :file:`doc/users/project/citing.rst` +edit :file:`doc/project/citing.rst`. Commit the new SVG, the change to +:file:`tools/cache_zenodo_svg.py`, and the changes to :file:`doc/project/citing.rst` to the VER-doc branch and push to GitHub. :: git checkout v3.7.0-doc @@ -412,7 +443,6 @@ which will copy the built docs over. If this is a final release, link the rm stable ln -s 3.7.0 stable -You will need to manually edit :file:`versions.html` to show the released version. You will also need to edit :file:`sitemap.xml` to include the newly released version. Now commit and push everything to GitHub :: diff --git a/doc/devel/tag_glossary.rst b/doc/devel/tag_glossary.rst new file mode 100644 index 000000000000..9a53de6358c8 --- /dev/null +++ b/doc/devel/tag_glossary.rst @@ -0,0 +1,181 @@ +:orphan: + +Tag Glossary +============ + +I. API tags: what content from the API reference is in the example? +II. Structural tags: what format is the example? What context can we provide? +III. Domain tags: what discipline(s) might seek this example consistently? +IV. Internal tags: what information is helpful for maintainers or contributors? + + +API tags: what content from the API reference is in the example? +---------------------------------------------------------------- + ++-----------------------------------+---------------------------------------------+ +|``tag`` | use case - if not obvious | ++===================================+=============================================+ +|**Primary or relevant plot component** | ++-----------------------------------+---------------------------------------------+ +|``component: axes`` |remarkable or very clear use of component | ++-----------------------------------+---------------------------------------------+ +|``component: axis`` | | ++-----------------------------------+---------------------------------------------+ +|``component: marker`` | | ++-----------------------------------+---------------------------------------------+ +|``component: label`` | | ++-----------------------------------+---------------------------------------------+ +|``component: title`` | | ++-----------------------------------+---------------------------------------------+ +|``component: legend`` | | ++-----------------------------------+---------------------------------------------+ +|``component: subplot`` | | ++-----------------------------------+---------------------------------------------+ +|``component: figure`` | | ++-----------------------------------+---------------------------------------------+ +|``component: annotation`` | | ++-----------------------------------+---------------------------------------------+ +|``component: label`` | | ++-----------------------------------+---------------------------------------------+ +|``component: ticks`` | | ++-----------------------------------+---------------------------------------------+ +|``component: spines`` |frequently paired with ``component: axis`` | ++-----------------------------------+---------------------------------------------+ +|``component: error`` | | ++-----------------------------------+---------------------------------------------+ +|``component: animation`` | | ++-----------------------------------+---------------------------------------------+ +| | | ++-----------------------------------+---------------------------------------------+ +|**Styling** | ++-----------------------------------+---------------------------------------------+ +|Use these tags when plot contains a teachable example | ++-----------------------------------+---------------------------------------------+ +|``styling: color`` | | ++-----------------------------------+---------------------------------------------+ +|``styling: shape`` | | ++-----------------------------------+---------------------------------------------+ +|``styling: size`` | | ++-----------------------------------+---------------------------------------------+ +|``styling: position`` | | ++-----------------------------------+---------------------------------------------+ +|``styling: texture`` | | ++-----------------------------------+---------------------------------------------+ +|``styling: colormap`` | | ++-----------------------------------+---------------------------------------------+ +|``styling: linestyle`` | | ++-----------------------------------+---------------------------------------------+ +|``styling: small-multiples`` | | ++-----------------------------------+---------------------------------------------+ +|``styling: conditional`` |styling is determined programmatically by a | +| |condition being met | ++-----------------------------------+---------------------------------------------+ +| | | ++-----------------------------------+---------------------------------------------+ +|**Interactivity** | ++-----------------------------------+---------------------------------------------+ +|``interactivity: event handling`` | | ++-----------------------------------+---------------------------------------------+ +|``interactivity: click`` | | ++-----------------------------------+---------------------------------------------+ +|``interactivity: mouseover`` | | ++-----------------------------------+---------------------------------------------+ +|``interactivity: zoom`` | | ++-----------------------------------+---------------------------------------------+ +|``interactivity: pan`` | | ++-----------------------------------+---------------------------------------------+ +|``interactivity: brush`` | | ++-----------------------------------+---------------------------------------------+ +|``interactivity: drag`` | | ++-----------------------------------+---------------------------------------------+ +|``interactivity: scroll`` | | ++-----------------------------------+---------------------------------------------+ +| | | ++-----------------------------------+---------------------------------------------+ +|**Plot Type** | ++-----------------------------------+---------------------------------------------+ +|``plot-type: bar`` |example contains a bar plot | ++-----------------------------------+---------------------------------------------+ +|``plot-type: line`` |example contains a line plot | ++-----------------------------------+---------------------------------------------+ +|``plot-type: pie`` |example contains a pie plot | ++-----------------------------------+---------------------------------------------+ +|``plot-type: polar`` |example contains a polar plot | ++-----------------------------------+---------------------------------------------+ +|``plot-type: 3D`` |example contains a 3D plot | ++-----------------------------------+---------------------------------------------+ +|``plot-type: histogram`` |example contains a histogram | ++-----------------------------------+---------------------------------------------+ +|``plot-type: specialty`` | | ++-----------------------------------+---------------------------------------------+ +|``plot-type: scatter`` | | ++-----------------------------------+---------------------------------------------+ + + +Structural tags: what format is the example? What context can we provide? +------------------------------------------------------------------------- + ++----------------------------+-------------------------------------------------------------------+ +|``tag`` | use case | ++============================+===================================================================+ +|``level`` |level refers to how much context/background the user will need | ++----------------------------+-------------------------------------------------------------------+ +|``level: beginner`` |concepts are standalone, self-contained, require only one module | ++----------------------------+-------------------------------------------------------------------+ +|``level: intermediate`` |concepts may require knowledge of other modules, have dependencies | ++----------------------------+-------------------------------------------------------------------+ +|``level: advanced`` |concepts require multiple modules and have dependencies | ++----------------------------+-------------------------------------------------------------------+ +|``purpose`` |what's it here for? | ++----------------------------+-------------------------------------------------------------------+ +|``purpose: storytelling`` |storytelling exemplar -- build a visual argument | ++----------------------------+-------------------------------------------------------------------+ +|``purpose: reference`` |reference docs like "marker reference" or "list of named colors" | +| | - dense collection of short-format information | +| | - well-defined scope, accessible information | ++----------------------------+-------------------------------------------------------------------+ +|``purpose: fun`` |just for fun! | ++----------------------------+-------------------------------------------------------------------+ +|``purpose: showcase`` |good showcase example | ++----------------------------+-------------------------------------------------------------------+ + +Domain tags: what discipline(s) might seek this example consistently? +--------------------------------------------------------------------- + +It's futile to draw fences around "who owns what", and that's not the point of domain tags. Domain tags help groups of people to privately organize relevant information, and so are not displayed publicly. See below for a list of existing domain tags. If you don't see the one you're looking for and you think it should exist, consider proposing it. + ++-------------------------------+----------------------------------------+ +|``tag`` | use case | ++===============================+========================================+ +|``domain`` |for whom is the example relevant? | ++-------------------------------+----------------------------------------+ +|``domain: cartography`` | | ++-------------------------------+----------------------------------------+ +|``domain: geometry`` | | ++-------------------------------+----------------------------------------+ +|``domain: statistics`` | | ++-------------------------------+----------------------------------------+ +|``domain: oceanography`` | | ++-------------------------------+----------------------------------------+ +|``domain: signal-processing`` | | ++-------------------------------+----------------------------------------+ + +Internal tags: what information is helpful for maintainers or contributors? +--------------------------------------------------------------------------- + ++-------------------------------+-----------------------------------------------------------------------+ +|``tag`` | use case | ++===============================+=======================================================================+ +|``internal: high-bandwidth`` |allows users to filter out bandwidth-intensive examples like animations| ++-------------------------------+-----------------------------------------------------------------------+ +|``internal: untagged`` |allows docs contributors to easily find untagged examples | ++-------------------------------+-----------------------------------------------------------------------+ +|``internal: deprecated`` |examples containing deprecated functionality or concepts | ++-------------------------------+-----------------------------------------------------------------------+ +|``internal: needs-review`` |example needs to be reviewed for accuracy or pedagogical value | ++-------------------------------+-----------------------------------------------------------------------+ +|``internal: outstanding-todo`` |example has an unfinished to-do | ++-------------------------------+-----------------------------------------------------------------------+ +|``internal: too-much`` |the example should be refined, split into multiple examples, or | +| |reformatted into a tutorial or reference | ++-------------------------------+-----------------------------------------------------------------------+ diff --git a/doc/devel/tag_guidelines.rst b/doc/devel/tag_guidelines.rst new file mode 100644 index 000000000000..ca6b8cfde01d --- /dev/null +++ b/doc/devel/tag_guidelines.rst @@ -0,0 +1,75 @@ +Guidelines for assigning tags to gallery examples +================================================= + +Why do we need tags? +-------------------- + +Tags serve multiple purposes. + +Tags have a one-to-many organization (i.e. one example can have several tags), while the gallery structure requires that examples are placed in only one location. This means tags provide a secondary layer of organization and make the gallery of examples more flexible and more user-friendly. + +They allow for better discoverability, search, and browse functionality. They are helpful for users struggling to write a search query for what they're looking for. + +Hidden tags provide additional functionality for maintainers and contributors. + +What gets a tag? +---------------- + +Every gallery example should be tagged with: + +* 1+ content tags +* structural, domain, or internal tag(s) if helpful + +Tags can repeat existing forms of organization (e.g. an example is in the Animation folder and also gets an ``animation`` tag). + +Tags are helpful to denote particularly good "byproduct" examples. E.g. the explicit purpose of a gallery example might be to demonstrate a colormap, but it's also a good demonstration of a legend. Tag ``legend`` to indicate that, rather than changing the title or the scope of the example. + +**Tag Categories** - See :doc:`Tag Glossary ` for a complete list of tags. + +I. API tags: what content from the API reference is in the example? +II. Structural tags: what format is the example? What context can we provide? +III. Domain tags: what discipline(s) might seek this example consistently? +IV. Internal tags: what information is helpful for maintainers or contributors? + +Proposing new tags +------------------ + +1. Review existing tag list, looking out for similar entries (i.e. ``axes`` and ``axis``). +2. If a relevant tag or subcategory does not yet exist, propose it. Each tag is two parts: ``subcategory: tag``. Tags should be one or two words. +3. New tags should be be added when they are relevant to existing gallery entries too. Avoid tags that will link to only a single gallery entry. +4. Tags can recreate other forms of organization. + +Note: Tagging organization aims to work for 80-90% of cases. Some examples fall outside of the tagging structure. Niche or specific examples shouldn't be given standalone tags that won't apply to other examples. + +How to tag? +----------- +Put each tag as a directive at the bottom of the page. + +Related content +--------------- + +What is a gallery example? +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The gallery of examples contains visual demonstrations of matplotlib features. Gallery examples exist so that users can scan through visual examples. + +Unlike tutorials or user guides, gallery examples teach by demonstration, rather than by explanation or instruction. + +Gallery examples should avoid instruction or excessive explanation except for brief clarifying code comments. Instead, they can tag related concepts and/or link to relevant tutorials or user guides. + +Format +^^^^^^ + +All :ref:`examples-index` should aim to follow the following format: + +* Title: 1-6 words, descriptive of content +* Subtitle: 10-50 words, action-oriented description of the example subject +* Image: a clear demonstration of the subject, showing edge cases and different applications if possible +* Code + Text (optional): code, commented as appropriate + written text to add context if necessary + +Example: + +The ``bbox_intersect`` gallery example demonstrates the point of visual examples: + +* this example is "messy" in that it's hard to categorize, but the gallery is the right spot for it because it makes sense to find it by visual search +* https://matplotlib.org/devdocs/gallery/misc/bbox_intersect.html#sphx-glr-gallery-misc-bbox-intersect-py diff --git a/doc/devel/testing.rst b/doc/devel/testing.rst index 7a11c7c3eb18..a9f52f0e62b6 100644 --- a/doc/devel/testing.rst +++ b/doc/devel/testing.rst @@ -37,11 +37,11 @@ Running the tests In the root directory of your development repository run:: - python -m pytest + pytest -pytest can be configured via a lot of `command-line parameters`_. Some -particularly useful ones are: +``pytest`` can be configured via many :external+pytest:doc:`command-line parameters +`. Some particularly useful ones are: ============================= =========== ``-v`` or ``--verbose`` Be more verbose @@ -50,14 +50,49 @@ particularly useful ones are: ``--capture=no`` or ``-s`` Do not capture stdout ============================= =========== -To run a single test from the command line, you can provide a file path, -optionally followed by the function separated by two colons, e.g., (tests do -not need to be installed, but Matplotlib should be):: +To run a single test from the command line, you can provide a file path, optionally +followed by the function separated by two colons, e.g., (tests do not need to be +installed, but Matplotlib should be):: pytest lib/matplotlib/tests/test_simplification.py::test_clipping +If you want to use ``pytest`` as a module (via ``python -m pytest``), then you will need +to avoid clashes between ``pytest``'s import mode and Python's search path: -.. _command-line parameters: http://doc.pytest.org/en/latest/usage.html +- On more recent Python, you may :external+python:std:option:`disable "unsafe import + paths" <-P>` (i.e., stop adding the current directory to the import path) with the + ``-P`` argument:: + + python -P -m pytest + +- On older Python, you may enable :external+python:std:option:`isolated mode <-I>` + (which stops adding the current directory to the import path, but has other + repercussions):: + + python -I -m pytest + +- On any Python, set ``pytest``'s :external+pytest:doc:`import mode + ` to the older ``prepend`` mode (but note that this will break + ``pytest``'s assert rewriting):: + + python -m pytest --import-mode prepend + +Viewing image test output +^^^^^^^^^^^^^^^^^^^^^^^^^ + +The output of :ref:`image-based ` tests is stored in a +``result_images`` directory. These images can be compiled into one HTML page, containing +hundreds of images, using the ``visualize_tests`` tool:: + + python tools/visualize_tests.py + +Image test failures can also be analysed using the ``triage_tests`` tool:: + + python tools/triage_tests.py + +The triage tool allows you to accept or reject test failures and will copy the new image +to the folder where the baseline test images are stored. The triage tool requires that +:ref:`QT ` is installed. Writing a simple test @@ -94,7 +129,9 @@ For numpy's default random number generator use:: and then use ``rng`` when generating the random numbers. -The seed is John Hunter's birthday. +The seed is :ref:`John Hunter's ` birthday. + +.. _image-comparison: Writing an image comparison test -------------------------------- @@ -128,6 +165,10 @@ texts (labels, tick labels, etc) are not really part of what is tested, use ``remove_text=True`` as this will lead to smaller figures and reduce possible issues with font mismatch on different platforms. + +Compare two methods of creating an image +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Baseline images take a lot of space in the Matplotlib repository. An alternative approach for image comparison tests is to use the `~matplotlib.testing.decorators.check_figures_equal` decorator, which should be @@ -274,16 +315,17 @@ The correct target folder can be found using:: python -c "import matplotlib.tests; print(matplotlib.tests.__file__.rsplit('/', 1)[0])" -An analogous copying of :file:`lib/mpl_toolkits/tests/baseline_images` +An analogous copying of :file:`lib/mpl_toolkits/*/tests/baseline_images` is necessary for testing ``mpl_toolkits``. Run the tests ^^^^^^^^^^^^^ -To run the all the tests on your installed version of Matplotlib:: - python -m pytest --pyargs matplotlib.tests +To run all the tests on your installed version of Matplotlib:: + + pytest --pyargs matplotlib.tests The test discovery scope can be narrowed to single test modules or even single functions:: - python -m pytest --pyargs matplotlib.tests.test_simplification.py::test_clipping + pytest --pyargs matplotlib.tests.test_simplification.py::test_clipping diff --git a/doc/devel/triage.rst b/doc/devel/triage.rst index 94b6874af228..ca06fd515c79 100644 --- a/doc/devel/triage.rst +++ b/doc/devel/triage.rst @@ -1,8 +1,9 @@ .. _bug_triaging: +******************************* Bug triaging and issue curation -=============================== +******************************* The `issue tracker `_ is important to communication in the project because it serves as the @@ -11,24 +12,28 @@ identifying major projects to work on, and discussing priorities. For this reason, it is important to curate the issue list, adding labels to issues and closing issues that are resolved or unresolvable. +Writing well defined issues increases their chances of being successfully +resolved. Guidelines on writing a good issue can be found in :ref:`here +`. The recommendations in this page are adapted from +the `scikit learn `_ +and `Pandas `_ +contributing guides. + + +Improve issue reports +===================== + Triaging issues does not require any particular expertise in the internals of Matplotlib, is extremely valuable to the project, and we welcome anyone to participate in issue triage! However, people who are not part of the Matplotlib organization do not have `permissions to change milestones, add labels, or close issue `_. + If you do not have enough GitHub permissions do something (e.g. add a label, close an issue), please leave a comment with your recommendations! -Working on issues to improve them ---------------------------------- - -Improving issues increases their chances of being successfully resolved. -Guidelines on submitting good issues can be found :ref:`here -`. -A third party can give useful feedback or even add -comments on the issue. The following actions are typically useful: - documenting issues that are missing elements to reproduce the problem @@ -62,30 +67,11 @@ The following actions are typically useful: explores how to lead online discussions in the context of open source. -.. _triage_team: - -Triage team ------------ - - -If you would like to join the triage team: - -1. Correctly triage 2-3 issues. -2. Ask someone on in the Matplotlib organization (publicly or privately) to - recommend you to the triage team (look for "Member" on the top-right of - comments on GitHub). If you worked with someone on the issues triaged, they - would be a good person to ask. -3. Responsibly exercise your new power! - -Anyone with commit or triage rights may nominate a user to be invited to join -the triage team by emailing matplotlib-steering-council@numfocus.org . - +Maintainers and triage team members +----------------------------------- -Triaging operations for members of the core and triage teams ------------------------------------------------------------- - -In addition to the above, members of the core team and the triage team -can do the following important tasks: +In addition to the above, maintainers and the triage team can do the following +important tasks: - Update labels for issues and PRs: see the list of `available GitHub labels `_. @@ -113,7 +99,6 @@ can do the following important tasks: least a week) to add extra information - .. topic:: Closing issues: a tough call When uncertain on whether an issue should be closed or not, it is @@ -122,13 +107,19 @@ can do the following important tasks: question or has been considered as unclear for many years, then it should be closed. +Preparing PRs for review +======================== + +Reviewing code is also encouraged. Contributors and users are welcome to +participate to the review process following our :ref:`review guidelines +`. .. _triage_workflow: -A typical workflow for triaging issues --------------------------------------- +Triage workflow +=============== -The following workflow [1]_ is a good way to approach issue triaging: +The following workflow is a good way to approach issue triaging: #. Thank the reporter for opening an issue @@ -169,7 +160,7 @@ The following workflow [1]_ is a good way to approach issue triaging: While we strive for a bug-free library, regressions are the highest priority. If we have broken user-code that *used to* work, we should - fix that in the next patch release! + fix that in the next micro release! Try to determine when the regression happened by running the reproduction code against older versions of Matplotlib. This can @@ -208,22 +199,20 @@ The following workflow [1]_ is a good way to approach issue triaging: An additional useful step can be to tag the corresponding module e.g. the "GUI/Qt" label when relevant. +.. _triage_team: -.. [1] Adapted from the pandas project `maintainers guide - `_ and - `the scikit-learn project - `_ . - +Triage team +=========== -Working on PRs to help review ------------------------------- -Reviewing code is also encouraged. Contributors and users are welcome to -participate to the review process following our :ref:`review guidelines -`. +If you would like to join the triage team: -Acknowledgments ---------------- +1. Correctly triage 2-3 issues. +2. Ask someone on in the Matplotlib organization (publicly or privately) to + recommend you to the triage team (look for "Member" on the top-right of + comments on GitHub). If you worked with someone on the issues triaged, they + would be a good person to ask. +3. Responsibly exercise your new power! -This page is lightly adapted from `the scikit-learn project -`_ . +Anyone with commit or triage rights may nominate a user to be invited to join +the triage team by emailing matplotlib-steering-council@numfocus.org . diff --git a/doc/devel/troubleshooting.rst b/doc/devel/troubleshooting.rst index 77c1b242399b..74ce81b2da00 100644 --- a/doc/devel/troubleshooting.rst +++ b/doc/devel/troubleshooting.rst @@ -43,3 +43,23 @@ unlink this file. Multiple versions of Matplotlib can be linked to the same DLL, for example a development version installed in a development conda environment and a stable version running in a Jupyter notebook. To resolve this error, fully close all running instances of Matplotlib. + +Windows compilation errors +========================== +If the compiled extensions are not building on Windows due to errors in linking to +Windows' header files, for example ``../../src/_tkagg.cpp:133:10: error: 'WM_DPICHANGED' was not declared in this scope``, +you should check which compiler Meson is using: + +.. code-block:: bat + + Build type: native build + Project name: matplotlib + Project version: 3.9.0.dev0 + C compiler for the host machine: cc (gcc 7.2.0 "cc (Rev1, Built by MSYS2 project) 7.2.0") + C linker for the host machine: cc ld.bfd 2.29.1 + C++ compiler for the host machine: c++ (gcc 7.2.0 "c++ (Rev1, Built by MSYS2 project) 7.2.0") + C++ linker for the host machine: c++ ld.bfd 2.29.1 + +Our :ref:`dependencies ` documentation lists the minimum header +version if you intended to use ``MSYS2``. If you intended to use ``MSVC`` then +you may need to force Meson to :external+meson-python:ref:`use MSVC `. diff --git a/doc/index.rst b/doc/index.rst index 4f42273d3dcc..1a385d2330af 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -34,10 +34,10 @@ Install .. toctree:: :maxdepth: 2 - users/installing/index + install/index For more detailed instructions, see the -:doc:`installation guide `. +:doc:`installation guide `. Learn ===== @@ -172,4 +172,4 @@ About us .. toctree:: :maxdepth: 2 - users/project/index.rst + project/index.rst diff --git a/doc/users/installing/dependencies.rst b/doc/install/dependencies.rst similarity index 71% rename from doc/users/installing/dependencies.rst rename to doc/install/dependencies.rst index d5145ea85b3a..93c1990b9472 100644 --- a/doc/users/installing/dependencies.rst +++ b/doc/install/dependencies.rst @@ -1,17 +1,20 @@ -.. redirect-from: /devel/dependencies +.. redirect-from:: /devel/dependencies +.. redirect-from:: /users/installing/dependencies .. _dependencies: -============ +************ Dependencies -============ +************ + +.. _runtime_dependencies: Runtime dependencies ==================== -Mandatory dependencies ----------------------- +Required +-------- When installing through a package manager like ``pip`` or ``conda``, the mandatory dependencies are automatically installed. This list is mainly for @@ -23,7 +26,7 @@ reference. * `dateutil `_ (>= 2.7) * `fontTools `_ (>= 4.22.0) * `kiwisolver `_ (>= 1.3.1) -* `NumPy `_ (>= 1.21) +* `NumPy `_ (>= 1.23) * `packaging `_ (>= 20.0) * `Pillow `_ (>= 8.0) * `pyparsing `_ (>= 2.3.1) @@ -33,12 +36,14 @@ reference. .. _optional_dependencies: -Optional dependencies ---------------------- +Optional +-------- The following packages and tools are not required but extend the capabilities of Matplotlib. +.. _backend_dependencies: + Backends ^^^^^^^^ @@ -49,7 +54,8 @@ and the capabilities they provide. * Tk_ (>= 8.5, != 8.6.0 or 8.6.1): for the Tk-based backends. Tk is part of most standard Python installations, but it's not part of Python itself and thus may not be present in rare cases. -* PyQt6_ (>= 6.1), PySide6_, PyQt5_, or PySide2_: for the Qt-based backends. +* PyQt6_ (>= 6.1), PySide6_, PyQt5_ (>= 5.12), or PySide2_: for the Qt-based + backends. * PyGObject_ and pycairo_ (>= 1.14.0): for the GTK-based backends. If using pip (but not conda or system package manager) PyGObject must be built from source; see `pygobject documentation @@ -102,37 +108,34 @@ Matplotlib brings its own copies of the following libraries: Additionally, Matplotlib depends on: - FreeType_ (>= 2.3): a font rendering library -- QHull_ (>= 2020.2): a library for computing triangulations +- QHull_ (>= 8.0.2): a library for computing triangulations (note that this version is + also known as 2020.2) .. _FreeType: https://www.freetype.org/ .. _Qhull: http://www.qhull.org/ -By default, Matplotlib downloads and builds its own copies of FreeType (this is -necessary to run the test suite, because different versions of FreeType -rasterize characters differently) and of Qhull. As an exception, Matplotlib -defaults to the system version of FreeType on AIX. -Use system libraries -^^^^^^^^^^^^^^^^^^^^ +Download during install +^^^^^^^^^^^^^^^^^^^^^^^ -To force Matplotlib to use a copy of FreeType or Qhull already installed in -your system, create a :file:`mplsetup.cfg` file with the following contents: +By default, Matplotlib downloads and builds its own copies of Qhull and FreeType. +The vendored version of FreeType is necessary to run the test suite, because +different versions of FreeType rasterize characters differently. -.. code-block:: cfg - [libs] - system_freetype = true - system_qhull = true +Use system libraries +^^^^^^^^^^^^^^^^^^^^ -before running +To force Matplotlib to use a copy of FreeType or Qhull already installed in your system, +you must `pass configuration settings to Meson via meson-python +`_: .. code-block:: sh - python -m pip install . - - -You can also use the :envvar:`MPLSETUPCFG` to specify the path to a cfg file when -installing from pypi. + python -m pip install \ + --config-settings=setup-args="-Dsystem-freetype=true" \ + --config-settings=setup-args="-Dsystem-qhull=true" \ + . In this case, you need to install the FreeType and Qhull library and headers. @@ -166,7 +169,7 @@ tool for locating FreeType: If not using pkg-config (in particular on Windows), you may need to set the include path (to the library headers) and link path (to the libraries) explicitly, if they are not in standard locations. This can be done using -standard environment variables -- on Linux and OSX: +standard environment variables -- on Linux and macOS: .. code-block:: sh @@ -185,22 +188,15 @@ remember to clear your artifacts before re-building:: git clean -xfd +From source files +^^^^^^^^^^^^^^^^^ -Manual Download -^^^^^^^^^^^^^^^ - - -If the automatic download does not work (for example on air-gapped systems) it -is preferable to instead use system libraries. However you can manually -download and unpack the tarballs into:: - - build/freetype-2.6.1 # on all platforms but windows ARM64 - build/freetype-2.11.1 # on windows ARM64 - build/qhull-2020.2 - -at the top level of the checkout repository. The expected sha256 hashes of -the downloaded tarballs is in :file:`setupext.py` if you wish to verify -before unpacking. +If the automatic download does not work (for example, on air-gapped systems) it is +preferable to instead use system libraries. However you can manually download the +tarballs into :file:`subprojects/packagecache` at the top level of the checkout +repository. The expected SHA256 hashes of the downloaded tarballs are in +:file:`subprojects/*.wrap` if you wish to verify them, but they will also be checked by +the build system before unpacking. Minimum pip / manylinux support (linux) @@ -209,7 +205,7 @@ Minimum pip / manylinux support (linux) Matplotlib publishes `manylinux wheels `_ which have a minimum version of pip which will recognize the wheels -- Python 3.9+: ``manylinx2014`` / pip >= 19.3 +- Python 3.9+: ``manylinux2014`` / pip >= 19.3 In all cases the required version of pip is embedded in the CPython source. @@ -217,33 +213,45 @@ In all cases the required version of pip is embedded in the CPython source. .. _development-dependencies: -Dependencies for building Matplotlib -==================================== +Build dependencies +================== + .. _setup-dependencies: -Setup dependencies ------------------- +Python +------ -- `certifi `_ (>= 2020.06.20). Used while - downloading the freetype and QHull source during build. This is not a - runtime dependency. +By default, ``pip`` will build packages using build isolation, meaning that these +build dependencies are temporally installed by pip for the duration of the +Matplotlib build process. However, build isolation is disabled when :ref:`installing Matplotlib for development `; +therefore we recommend using one of our :ref:`virtual environment configurations ` to +create a development environment in which these packages are automatically installed. + +If you are developing Matplotlib and unable to use our environment configurations, +then you must manually install the following packages into your development environment: + +- `meson-python `_ (>= 0.13.1). +- `ninja `_ (>= 1.8.2). This may be available in your package + manager or bundled with Meson, but may be installed via ``pip`` if otherwise not + available. - `PyBind11 `_ (>= 2.6). Used to connect C/C++ code with Python. -- `setuptools `_ (>= 64). - `setuptools_scm `_ (>= 7). Used to update the reported ``mpl.__version__`` based on the current git commit. Also a runtime dependency for editable installs. -- `NumPy `_ (>= 1.21). Also a runtime dependency. +- `NumPy `_ (>= 1.22). Also a runtime dependency. .. _compile-dependencies: -C++ compiler ------------- +Compiled extensions +------------------- -Matplotlib requires a C++ compiler that supports C++11, and each platform has a +Matplotlib requires a C++ compiler that supports C++17, and each platform has a development environment that must be installed before a compiler can be installed. +You may also need to install headers for various libraries used in the compiled extension +source files. .. tab-set:: @@ -270,6 +278,8 @@ development environment that must be installed before a compiler can be installe Alternatively, you can install a Linux-like environment such as `CygWin `_ or `Windows Subsystem for Linux `_. + If using `MinGW-64 `_, we require **v6** of the + ```Mingw-w64-x86_64-headers``. We highly recommend that you install a compiler using your platform tool, i.e., @@ -286,35 +296,36 @@ Xcode, VS Code or Linux package manager. Choose **one** compiler from this list: - platforms - notes * - GCC - - **4.8.1** + - **7.2** - Linux, macOS, Windows - - `gcc 4.8.1 `_, + - `gcc 7.2 `_, `GCC: Binaries `_, - - For gcc <6.5 you will need to set ``$CFLAGS=-std=c++11`` to enable C++11 support. * - Clang (LLVM) - - **3.3** + - **5** - Linux, macOS - - `clang 3.3 `_, `LLVM `_ + - `clang 5 `_, `LLVM `_ * - MSVC++ - - **14.0** + - **16.0** - Windows - - `Visual Studio 2015 C++ `_ + - `Visual Studio 2019 C++ `_ .. _test-dependencies: -Dependencies for testing Matplotlib -=================================== +Test dependencies +================= + This section lists the additional software required for :ref:`running the tests `. -Required: +Required +-------- - pytest_ (>= 7.0.0) -Optional: +Optional +-------- In addition to all of the optional dependencies on the main library, for testing the following will be used if they are installed. @@ -332,8 +343,8 @@ testing the following will be used if they are installed. - pytest-xvfb_ to run tests without windows popping up (Linux) - pytz_ used to test pytz int - sphinx_ used to test our sphinx extensions -- WenQuanYi Zen Hei and `Noto Sans CJK `_ - fonts for testing font fallback and non-western fonts +- `WenQuanYi Zen Hei`_ and `Noto Sans CJK`_ fonts for testing font fallback and + non-Western fonts - xarray_ used to test compatibility with xarray If any of these dependencies are not discovered, then the tests that rely on @@ -361,16 +372,19 @@ them will be skipped by pytest. .. _pytest-xvfb: https://pypi.org/project/pytest-xvfb/ .. _pytest: http://doc.pytest.org/en/latest/ .. _sphinx: https://pypi.org/project/Sphinx/ +.. _WenQuanYi Zen Hei: http://wenq.org/en/ +.. _Noto Sans CJK: https://fonts.google.com/noto/use .. _xarray: https://pypi.org/project/xarray/ .. _doc-dependencies: -Dependencies for building Matplotlib's documentation -==================================================== +Documentation dependencies +========================== + +Python +------ -Python packages ---------------- The additional Python packages required to build the :ref:`documentation ` are listed in :file:`doc-requirements.txt` and can be installed using :: @@ -379,35 +393,41 @@ 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 --------------------------------- -Required: -* a minimal working LaTeX distribution, e.g., `TeX Live `_ or - `MikTeX `_ +External tools +-------------- + +The documentation requires LaTeX and Graphviz. These are not +Python packages and must be installed separately. + +Required +^^^^^^^^ + * `Graphviz `_ -* the following LaTeX packages (if your OS bundles TeX Live, the - "complete" version of the installer, e.g. "texlive-full" or "texlive-all", - will often automatically include these packages): +* a minimal working LaTeX distribution, e.g. `TeX Live `_ or + `MikTeX `_ + +The following LaTeX packages: - * `cm-super `_ * `dvipng `_ * `underscore `_ + * `cm-super `_ + * ``collection-fontsrecommended`` + +The complete version of many LaTex distribution installers, e.g. +"texlive-full" or "texlive-all", will often automatically include these packages. + + +Optional +^^^^^^^^ -Optional, but recommended: +The documentation can be built without Inkscape and optipng, but the build +process will raise various warnings. * `Inkscape `_ * `optipng `_ * the font `xkcd script `_ or `Comic Neue `_ * the font "Times New Roman" - -.. note:: - - The documentation will not build without LaTeX and Graphviz. These are not - Python packages and must be installed separately. The documentation can be - built without Inkscape and optipng, but the build process will raise various - warnings. If the build process warns that you are missing fonts, make sure - your LaTeX distribution bundles cm-super or install it separately. diff --git a/doc/users/installing/environment_variables_faq.rst b/doc/install/environment_variables_faq.rst similarity index 87% rename from doc/users/installing/environment_variables_faq.rst rename to doc/install/environment_variables_faq.rst index e7a721026743..38e0d0ef0c63 100644 --- a/doc/users/installing/environment_variables_faq.rst +++ b/doc/install/environment_variables_faq.rst @@ -3,6 +3,7 @@ .. redirect-from:: /faq/installing_faq .. redirect-from:: /users/faq/installing_faq +.. redirect-from:: /users/installing/environment_variables_faq ===================== Environment variables @@ -28,13 +29,6 @@ Environment variables used to find a base directory in which the :file:`matplotlib` subdirectory is created. -.. envvar:: MPLSETUPCFG - - This optional variable can be set to the full path of a :file:`mplsetup.cfg` - configuration file used to customize the Matplotlib build. By default, a - :file:`mplsetup.cfg` file in the root of the Matplotlib source tree will be - read. Supported build options are listed in :file:`mplsetup.cfg.template`. - .. envvar:: PATH The list of directories searched to find executable programs. @@ -49,7 +43,7 @@ Environment variables The Python Qt wrapper to prefer when using Qt-based backends. See :ref:`the entry in the usage guide ` for more information. -.. _setting-linux-osx-environment-variables: +.. _setting-linux-macos-environment-variables: Setting environment variables in Linux and macOS ================================================ diff --git a/doc/users/installing/index.rst b/doc/install/index.rst similarity index 88% rename from doc/users/installing/index.rst rename to doc/install/index.rst index ba380a22e41f..867e4600a77e 100644 --- a/doc/users/installing/index.rst +++ b/doc/install/index.rst @@ -1,4 +1,5 @@ .. redirect-from:: /users/installing +.. redirect-from:: /users/installing/index ************ Installation @@ -31,7 +32,7 @@ precompiled wheel for your OS and Python. For support of other GUI frameworks, LaTeX rendering, saving animations and a larger selection of file formats, you can - install :ref:`optional_dependencies`. + install :ref:`optional dependencies `. Third-party distributions @@ -120,22 +121,20 @@ Before trying to install Matplotlib, please install the :ref:`dependencies`. To build from a tarball, download the latest *tar.gz* release file from `the PyPI files page `_. -We provide a `mplsetup.cfg`_ file which you can use to customize the build -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/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. - Configure build and behavior defaults ===================================== -Aspects of the build and install process and some behaviorial defaults of the -library can be configured via: +We provide a `meson.options`_ file containing options with which you can use to +customize the build process. For example, which default backend to use, whether some of +the optional libraries that Matplotlib ships with are installed, and so on. These +options will be particularly useful to those packaging Matplotlib. + +.. _meson.options: https://github.com/matplotlib/matplotlib/blob/main/meson.options + +Aspects of some behaviorial defaults of the library can be configured via: .. toctree:: :maxdepth: 2 @@ -203,15 +202,15 @@ installation of the package. In order to fully remove an installed Matplotlib: 2. Delete any Matplotlib directories or eggs from your :ref:`installation directory `. -OSX Notes ---------- +macOS Notes +----------- -.. _which-python-for-osx: +.. _which-python-for-macos: -Which python for OSX? -^^^^^^^^^^^^^^^^^^^^^ +Which python for macOS? +^^^^^^^^^^^^^^^^^^^^^^^ -Apple ships OSX with its own Python, in ``/usr/bin/python``, and its own copy +Apple ships macOS with its own Python, in ``/usr/bin/python``, and its own copy of Matplotlib. Unfortunately, the way Apple currently installs its own copies of NumPy, Scipy and Matplotlib means that these packages are difficult to upgrade (see `system python packages`_). For that reason we strongly suggest @@ -228,18 +227,18 @@ See the Anaconda web page for installation support. .. _Anaconda: https://www.anaconda.com/ Other options for a fresh Python install are the standard installer from -`python.org `_, or installing -Python using a general OSX package management system such as `homebrew +`python.org `_, or installing +Python using a general macOS package management system such as `homebrew `_ or `macports `_. Power users on -OSX will likely want one of homebrew or macports on their system to install +macOS will likely want one of homebrew or macports on their system to install open source software packages, but it is perfectly possible to use these systems with another source for your Python binary, such as Anaconda or Python.org Python. -.. _install_osx_binaries: +.. _install_macos_binaries: -Installing OSX binary wheels -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Installing macOS 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 @@ -283,7 +282,7 @@ then check that the Python binary is the one you expected by running :: which python3 If you get a result like ``/usr/bin/python...``, then you are getting the -Python installed with OSX, which is probably not what you want. Try closing +Python installed with macOS, which is probably not what you want. Try closing and restarting Terminal.app before running the check again. If that doesn't fix the problem, depending on which Python you wanted to use, consider reinstalling Python.org Python, or check your homebrew or macports setup. Remember that diff --git a/doc/users/installing/troubleshooting_faq.inc.rst b/doc/install/troubleshooting_faq.inc.rst similarity index 94% rename from doc/users/installing/troubleshooting_faq.inc.rst rename to doc/install/troubleshooting_faq.inc.rst index 60bad7ccffc9..d130813a80c6 100644 --- a/doc/users/installing/troubleshooting_faq.inc.rst +++ b/doc/install/troubleshooting_faq.inc.rst @@ -1,5 +1,7 @@ .. _troubleshooting-install: +.. redirect-from:: /users/installing/troubleshooting_faq + Troubleshooting =============== @@ -67,6 +69,6 @@ directory by default:: If you would like to use a different configuration directory, you can do so by specifying the location in your :envvar:`MPLCONFIGDIR` environment variable -- see -:ref:`setting-linux-osx-environment-variables`. Note that +:ref:`setting-linux-macos-environment-variables`. Note that :envvar:`MPLCONFIGDIR` sets the location of both the configuration directory and the cache directory. diff --git a/doc/make.bat b/doc/make.bat index 220523d74b60..37c74eb5079a 100644 --- a/doc/make.bat +++ b/doc/make.bat @@ -40,8 +40,11 @@ if "%1" == "clean" ( rmdir /s /q "%SOURCEDIR%\gallery" rmdir /s /q "%SOURCEDIR%\plot_types" rmdir /s /q "%SOURCEDIR%\tutorials" + rmdir /s /q "%SOURCEDIR%\users\explain" rmdir /s /q "%SOURCEDIR%\savefig" rmdir /s /q "%SOURCEDIR%\sphinxext\__pycache__" + del /q "%SOURCEDIR%\_static\constrained_layout*.png" + del /q "%SOURCEDIR%\sg_execution_times.rst" ) goto end diff --git a/doc/users/project/citing.rst b/doc/project/citing.rst similarity index 56% rename from doc/users/project/citing.rst rename to doc/project/citing.rst index e162a427d8b4..e0b99995ad11 100644 --- a/doc/users/project/citing.rst +++ b/doc/project/citing.rst @@ -1,4 +1,7 @@ .. redirect-from:: /citing +.. redirect-from:: /users/project/citing + +.. _citing_matplotlib: Citing Matplotlib ================= @@ -8,12 +11,12 @@ please acknowledge this fact by citing `J. D. Hunter, "Matplotlib: A 2D Graphics Environment", Computing in Science & Engineering, vol. 9, no. 3, pp. 90-95, 2007 `_. -.. literalinclude:: ../../../CITATION.bib +.. literalinclude:: ../../CITATION.bib :language: bibtex .. container:: sphx-glr-download - :download:`Download BibTeX bibliography file: CITATION.bib <../../../CITATION.bib>` + :download:`Download BibTeX bibliography file: CITATION.bib <../../CITATION.bib>` DOIs ---- @@ -29,173 +32,182 @@ By version .. START OF AUTOGENERATED +v3.9.1 + .. image:: ../_static/zenodo_cache/12652732.svg + :target: https://doi.org/10.5281/zenodo.12652732 +v3.9.0 + .. image:: ../_static/zenodo_cache/11201097.svg + :target: https://doi.org/10.5281/zenodo.11201097 +v3.8.4 + .. image:: ../_static/zenodo_cache/10916799.svg + :target: https://doi.org/10.5281/zenodo.10916799 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 + .. image:: ../_static/zenodo_cache/10059757.svg :target: https://doi.org/10.5281/zenodo.10059757 v3.8.0 - .. image:: ../../_static/zenodo_cache/8347255.svg + .. image:: ../_static/zenodo_cache/8347255.svg :target: https://doi.org/10.5281/zenodo.8347255 v3.7.3 - .. image:: ../../_static/zenodo_cache/8336761.svg + .. image:: ../_static/zenodo_cache/8336761.svg :target: https://doi.org/10.5281/zenodo.8336761 v3.7.2 - .. image:: ../../_static/zenodo_cache/8118151.svg + .. image:: ../_static/zenodo_cache/8118151.svg :target: https://doi.org/10.5281/zenodo.8118151 v3.7.1 - .. image:: ../../_static/zenodo_cache/7697899.svg + .. image:: ../_static/zenodo_cache/7697899.svg :target: https://doi.org/10.5281/zenodo.7697899 v3.7.0 - .. image:: ../../_static/zenodo_cache/7637593.svg + .. image:: ../_static/zenodo_cache/7637593.svg :target: https://doi.org/10.5281/zenodo.7637593 v3.6.3 - .. image:: ../../_static/zenodo_cache/7527665.svg + .. image:: ../_static/zenodo_cache/7527665.svg :target: https://doi.org/10.5281/zenodo.7527665 v3.6.2 - .. image:: ../../_static/zenodo_cache/7275322.svg + .. image:: ../_static/zenodo_cache/7275322.svg :target: https://doi.org/10.5281/zenodo.7275322 v3.6.1 - .. image:: ../../_static/zenodo_cache/7162185.svg + .. image:: ../_static/zenodo_cache/7162185.svg :target: https://doi.org/10.5281/zenodo.7162185 v3.6.0 - .. image:: ../../_static/zenodo_cache/7084615.svg + .. image:: ../_static/zenodo_cache/7084615.svg :target: https://doi.org/10.5281/zenodo.7084615 v3.5.3 - .. image:: ../../_static/zenodo_cache/6982547.svg + .. image:: ../_static/zenodo_cache/6982547.svg :target: https://doi.org/10.5281/zenodo.6982547 v3.5.2 - .. image:: ../../_static/zenodo_cache/6513224.svg + .. image:: ../_static/zenodo_cache/6513224.svg :target: https://doi.org/10.5281/zenodo.6513224 v3.5.1 - .. image:: ../../_static/zenodo_cache/5773480.svg + .. image:: ../_static/zenodo_cache/5773480.svg :target: https://doi.org/10.5281/zenodo.5773480 v3.5.0 - .. image:: ../../_static/zenodo_cache/5706396.svg + .. image:: ../_static/zenodo_cache/5706396.svg :target: https://doi.org/10.5281/zenodo.5706396 v3.4.3 - .. image:: ../../_static/zenodo_cache/5194481.svg + .. image:: ../_static/zenodo_cache/5194481.svg :target: https://doi.org/10.5281/zenodo.5194481 v3.4.2 - .. image:: ../../_static/zenodo_cache/4743323.svg + .. image:: ../_static/zenodo_cache/4743323.svg :target: https://doi.org/10.5281/zenodo.4743323 v3.4.1 - .. image:: ../../_static/zenodo_cache/4649959.svg + .. image:: ../_static/zenodo_cache/4649959.svg :target: https://doi.org/10.5281/zenodo.4649959 v3.4.0 - .. image:: ../../_static/zenodo_cache/4638398.svg + .. image:: ../_static/zenodo_cache/4638398.svg :target: https://doi.org/10.5281/zenodo.4638398 v3.3.4 - .. image:: ../../_static/zenodo_cache/4475376.svg + .. image:: ../_static/zenodo_cache/4475376.svg :target: https://doi.org/10.5281/zenodo.4475376 v3.3.3 - .. image:: ../../_static/zenodo_cache/4268928.svg + .. image:: ../_static/zenodo_cache/4268928.svg :target: https://doi.org/10.5281/zenodo.4268928 v3.3.2 - .. image:: ../../_static/zenodo_cache/4030140.svg + .. image:: ../_static/zenodo_cache/4030140.svg :target: https://doi.org/10.5281/zenodo.4030140 v3.3.1 - .. image:: ../../_static/zenodo_cache/3984190.svg + .. image:: ../_static/zenodo_cache/3984190.svg :target: https://doi.org/10.5281/zenodo.3984190 v3.3.0 - .. image:: ../../_static/zenodo_cache/3948793.svg + .. image:: ../_static/zenodo_cache/3948793.svg :target: https://doi.org/10.5281/zenodo.3948793 v3.2.2 - .. image:: ../../_static/zenodo_cache/3898017.svg + .. image:: ../_static/zenodo_cache/3898017.svg :target: https://doi.org/10.5281/zenodo.3898017 v3.2.1 - .. image:: ../../_static/zenodo_cache/3714460.svg + .. image:: ../_static/zenodo_cache/3714460.svg :target: https://doi.org/10.5281/zenodo.3714460 v3.2.0 - .. image:: ../../_static/zenodo_cache/3695547.svg + .. image:: ../_static/zenodo_cache/3695547.svg :target: https://doi.org/10.5281/zenodo.3695547 v3.1.3 - .. image:: ../../_static/zenodo_cache/3633844.svg + .. image:: ../_static/zenodo_cache/3633844.svg :target: https://doi.org/10.5281/zenodo.3633844 v3.1.2 - .. image:: ../../_static/zenodo_cache/3563226.svg + .. image:: ../_static/zenodo_cache/3563226.svg :target: https://doi.org/10.5281/zenodo.3563226 v3.1.1 - .. image:: ../../_static/zenodo_cache/3264781.svg + .. image:: ../_static/zenodo_cache/3264781.svg :target: https://doi.org/10.5281/zenodo.3264781 v3.1.0 - .. image:: ../../_static/zenodo_cache/2893252.svg + .. image:: ../_static/zenodo_cache/2893252.svg :target: https://doi.org/10.5281/zenodo.2893252 v3.0.3 - .. image:: ../../_static/zenodo_cache/2577644.svg + .. image:: ../_static/zenodo_cache/2577644.svg :target: https://doi.org/10.5281/zenodo.2577644 v3.0.2 - .. image:: ../../_static/zenodo_cache/1482099.svg + .. image:: ../_static/zenodo_cache/1482099.svg :target: https://doi.org/10.5281/zenodo.1482099 v3.0.1 - .. image:: ../../_static/zenodo_cache/1482098.svg + .. image:: ../_static/zenodo_cache/1482098.svg :target: https://doi.org/10.5281/zenodo.1482098 v2.2.5 - .. image:: ../../_static/zenodo_cache/3633833.svg + .. image:: ../_static/zenodo_cache/3633833.svg :target: https://doi.org/10.5281/zenodo.3633833 v3.0.0 - .. image:: ../../_static/zenodo_cache/1420605.svg + .. image:: ../_static/zenodo_cache/1420605.svg :target: https://doi.org/10.5281/zenodo.1420605 v2.2.4 - .. image:: ../../_static/zenodo_cache/2669103.svg + .. image:: ../_static/zenodo_cache/2669103.svg :target: https://doi.org/10.5281/zenodo.2669103 v2.2.3 - .. image:: ../../_static/zenodo_cache/1343133.svg + .. image:: ../_static/zenodo_cache/1343133.svg :target: https://doi.org/10.5281/zenodo.1343133 v2.2.2 - .. image:: ../../_static/zenodo_cache/1202077.svg + .. image:: ../_static/zenodo_cache/1202077.svg :target: https://doi.org/10.5281/zenodo.1202077 v2.2.1 - .. image:: ../../_static/zenodo_cache/1202050.svg + .. image:: ../_static/zenodo_cache/1202050.svg :target: https://doi.org/10.5281/zenodo.1202050 v2.2.0 - .. image:: ../../_static/zenodo_cache/1189358.svg + .. image:: ../_static/zenodo_cache/1189358.svg :target: https://doi.org/10.5281/zenodo.1189358 v2.1.2 - .. image:: ../../_static/zenodo_cache/1154287.svg + .. image:: ../_static/zenodo_cache/1154287.svg :target: https://doi.org/10.5281/zenodo.1154287 v2.1.1 - .. image:: ../../_static/zenodo_cache/1098480.svg + .. image:: ../_static/zenodo_cache/1098480.svg :target: https://doi.org/10.5281/zenodo.1098480 v2.1.0 - .. image:: ../../_static/zenodo_cache/1004650.svg + .. image:: ../_static/zenodo_cache/1004650.svg :target: https://doi.org/10.5281/zenodo.1004650 v2.0.2 - .. image:: ../../_static/zenodo_cache/573577.svg + .. image:: ../_static/zenodo_cache/573577.svg :target: https://doi.org/10.5281/zenodo.573577 v2.0.1 - .. image:: ../../_static/zenodo_cache/570311.svg + .. image:: ../_static/zenodo_cache/570311.svg :target: https://doi.org/10.5281/zenodo.570311 v2.0.0 - .. image:: ../../_static/zenodo_cache/248351.svg + .. image:: ../_static/zenodo_cache/248351.svg :target: https://doi.org/10.5281/zenodo.248351 v1.5.3 - .. image:: ../../_static/zenodo_cache/61948.svg + .. image:: ../_static/zenodo_cache/61948.svg :target: https://doi.org/10.5281/zenodo.61948 v1.5.2 - .. image:: ../../_static/zenodo_cache/56926.svg + .. image:: ../_static/zenodo_cache/56926.svg :target: https://doi.org/10.5281/zenodo.56926 v1.5.1 - .. image:: ../../_static/zenodo_cache/44579.svg + .. image:: ../_static/zenodo_cache/44579.svg :target: https://doi.org/10.5281/zenodo.44579 v1.5.0 - .. image:: ../../_static/zenodo_cache/32914.svg + .. image:: ../_static/zenodo_cache/32914.svg :target: https://doi.org/10.5281/zenodo.32914 v1.4.3 - .. image:: ../../_static/zenodo_cache/15423.svg + .. image:: ../_static/zenodo_cache/15423.svg :target: https://doi.org/10.5281/zenodo.15423 v1.4.2 - .. image:: ../../_static/zenodo_cache/12400.svg + .. image:: ../_static/zenodo_cache/12400.svg :target: https://doi.org/10.5281/zenodo.12400 v1.4.1 - .. image:: ../../_static/zenodo_cache/12287.svg + .. image:: ../_static/zenodo_cache/12287.svg :target: https://doi.org/10.5281/zenodo.12287 v1.4.0 - .. image:: ../../_static/zenodo_cache/11451.svg + .. image:: ../_static/zenodo_cache/11451.svg :target: https://doi.org/10.5281/zenodo.11451 .. END OF AUTOGENERATED diff --git a/doc/users/project/code_of_conduct.rst b/doc/project/code_of_conduct.rst similarity index 99% rename from doc/users/project/code_of_conduct.rst rename to doc/project/code_of_conduct.rst index ac8b268a4e30..56fee2f25f6f 100644 --- a/doc/users/project/code_of_conduct.rst +++ b/doc/project/code_of_conduct.rst @@ -1,4 +1,5 @@ .. _code_of_conduct: +.. redirect-from:: /users/project/code_of_conduct ==================================== Contributor Covenant Code of Conduct diff --git a/doc/users/project/credits.rst b/doc/project/credits.rst similarity index 99% rename from doc/users/project/credits.rst rename to doc/project/credits.rst index c23d6ac11298..a57c35e8127d 100644 --- a/doc/users/project/credits.rst +++ b/doc/project/credits.rst @@ -1,6 +1,7 @@ .. Note: This file is auto-generated using generate_credits.py .. redirect-from:: /users/credits +.. redirect-from:: /users/project/credits .. _credits: @@ -13,7 +14,7 @@ Matplotlib was written by John D. Hunter, with contributions from an ever-increasing number of users and developers. The current lead developer is Thomas A. Caswell, who is assisted by many `active developers `_. -Please also see our instructions on :doc:`/users/project/citing`. +Please also see our instructions on :doc:`/project/citing`. The following is a list of contributors extracted from the git revision control history of the project: diff --git a/doc/users/project/history.rst b/doc/project/history.rst similarity index 99% rename from doc/users/project/history.rst rename to doc/project/history.rst index 18a94793736f..966b7a3caa38 100644 --- a/doc/users/project/history.rst +++ b/doc/project/history.rst @@ -1,4 +1,5 @@ .. redirect-from:: /users/history +.. redirect-from:: /users/project/history .. _project_history: diff --git a/doc/users/project/index.rst b/doc/project/index.rst similarity index 85% rename from doc/users/project/index.rst rename to doc/project/index.rst index ad55576190ab..c7e230339dc9 100644 --- a/doc/users/project/index.rst +++ b/doc/project/index.rst @@ -1,4 +1,5 @@ .. redirect-from:: /users/backmatter +.. redirect-from:: /users/project/index Project information =================== diff --git a/doc/users/project/license.rst b/doc/project/license.rst similarity index 81% rename from doc/users/project/license.rst rename to doc/project/license.rst index 3f9c1e30531b..1005b5467b5a 100644 --- a/doc/users/project/license.rst +++ b/doc/project/license.rst @@ -1,6 +1,7 @@ .. _license: .. redirect-from:: /users/license +.. redirect-from:: /users/project/license ******* License @@ -50,7 +51,7 @@ License agreement :open: :class-container: sdd - .. literalinclude:: ../../../LICENSE/LICENSE + .. literalinclude:: ../../LICENSE/LICENSE :language: none @@ -61,13 +62,13 @@ Bundled software .. dropdown:: JSX Tools Resize Observer :class-container: sdd - .. literalinclude:: ../../../LICENSE/LICENSE_JSXTOOLS_RESIZE_OBSERVER + .. literalinclude:: ../../LICENSE/LICENSE_JSXTOOLS_RESIZE_OBSERVER :language: none .. dropdown:: QT4 Editor :class-container: sdd - .. literalinclude:: ../../../LICENSE/LICENSE_QT4_EDITOR + .. literalinclude:: ../../LICENSE/LICENSE_QT4_EDITOR :language: none @@ -79,19 +80,19 @@ Colormaps and themes .. dropdown:: ColorBrewer :class-container: sdd - .. literalinclude:: ../../../LICENSE/LICENSE_COLORBREWER + .. literalinclude:: ../../LICENSE/LICENSE_COLORBREWER :language: none .. dropdown:: Solarized :class-container: sdd - .. literalinclude:: ../../../LICENSE/LICENSE_SOLARIZED + .. literalinclude:: ../../LICENSE/LICENSE_SOLARIZED :language: none .. dropdown:: Yorick :class-container: sdd - .. literalinclude:: ../../../LICENSE/LICENSE_YORICK + .. literalinclude:: ../../LICENSE/LICENSE_YORICK :language: none @@ -103,29 +104,29 @@ Fonts .. dropdown:: American Mathematical Society (AMS) fonts :class-container: sdd - .. literalinclude:: ../../../LICENSE/LICENSE_AMSFONTS + .. literalinclude:: ../../LICENSE/LICENSE_AMSFONTS :language: none .. dropdown:: BaKoMa :class-container: sdd - .. literalinclude:: ../../../LICENSE/LICENSE_BAKOMA + .. literalinclude:: ../../LICENSE/LICENSE_BAKOMA :language: none .. dropdown:: Carlogo :class-container: sdd - .. literalinclude:: ../../../LICENSE/LICENSE_CARLOGO + .. literalinclude:: ../../LICENSE/LICENSE_CARLOGO :language: none .. dropdown:: Courier 10 :class-container: sdd - .. literalinclude:: ../../../LICENSE/LICENSE_COURIERTEN + .. literalinclude:: ../../LICENSE/LICENSE_COURIERTEN :language: none .. dropdown:: STIX :class-container: sdd - .. literalinclude:: ../../../LICENSE/LICENSE_STIX + .. literalinclude:: ../../LICENSE/LICENSE_STIX :language: none diff --git a/doc/users/project/mission.rst b/doc/project/mission.rst similarity index 95% rename from doc/users/project/mission.rst rename to doc/project/mission.rst index 438d2fce8b1d..1b7a68afcc67 100644 --- a/doc/users/project/mission.rst +++ b/doc/project/mission.rst @@ -1,4 +1,5 @@ .. _mission-statement: +.. redirect-from:: /users/project/mission Mission Statement ================= diff --git a/doc/sphinxext/custom_roles.py b/doc/sphinxext/custom_roles.py deleted file mode 100644 index 3dcecc3df733..000000000000 --- a/doc/sphinxext/custom_roles.py +++ /dev/null @@ -1,75 +0,0 @@ -from urllib.parse import urlsplit, urlunsplit - -from docutils import nodes - -from matplotlib import rcParamsDefault - - -class QueryReference(nodes.Inline, nodes.TextElement): - """ - Wraps a reference or pending reference to add a query string. - - The query string is generated from the attributes added to this node. - - Also equivalent to a `~docutils.nodes.literal` node. - """ - - def to_query_string(self): - """Generate query string from node attributes.""" - return '&'.join(f'{name}={value}' for name, value in self.attlist()) - - -def visit_query_reference_node(self, node): - """ - Resolve *node* into query strings on its ``reference`` children. - - Then act as if this is a `~docutils.nodes.literal`. - """ - query = node.to_query_string() - for refnode in node.findall(nodes.reference): - uri = urlsplit(refnode['refuri'])._replace(query=query) - refnode['refuri'] = urlunsplit(uri) - - self.visit_literal(node) - - -def depart_query_reference_node(self, node): - """ - Act as if this is a `~docutils.nodes.literal`. - """ - self.depart_literal(node) - - -def rcparam_role(name, rawtext, text, lineno, inliner, options={}, content=[]): - # Generate a pending cross-reference so that Sphinx will ensure this link - # isn't broken at some point in the future. - title = f'rcParams["{text}"]' - target = 'matplotlibrc-sample' - ref_nodes, messages = inliner.interpreted(title, f'{title} <{target}>', - 'ref', lineno) - - qr = QueryReference(rawtext, highlight=text) - qr += ref_nodes - node_list = [qr] - - # The default backend would be printed as "agg", but that's not correct (as - # the default is actually determined by fallback). - if text in rcParamsDefault and text != "backend": - node_list.extend([ - nodes.Text(' (default: '), - nodes.literal('', repr(rcParamsDefault[text])), - nodes.Text(')'), - ]) - - return node_list, messages - - -def setup(app): - app.add_role("rc", rcparam_role) - app.add_node( - QueryReference, - html=(visit_query_reference_node, depart_query_reference_node), - latex=(visit_query_reference_node, depart_query_reference_node), - text=(visit_query_reference_node, depart_query_reference_node), - ) - return {"parallel_read_safe": True, "parallel_write_safe": True} diff --git a/doc/sphinxext/gallery_order.py b/doc/sphinxext/gallery_order.py index 70a018750537..378cb394d37b 100644 --- a/doc/sphinxext/gallery_order.py +++ b/doc/sphinxext/gallery_order.py @@ -105,7 +105,7 @@ def __call__(self, item): explicit_subsection_order = [item + ".py" for item in list_all] -class MplExplicitSubOrder: +class MplExplicitSubOrder(ExplicitOrder): """For use within the 'within_subsection_order' key.""" def __init__(self, src_dir): self.src_dir = src_dir # src_dir is unused here diff --git a/doc/sphinxext/redirect_from.py b/doc/sphinxext/redirect_from.py index 7a7e6aa9cdc8..37b56373a3bf 100644 --- a/doc/sphinxext/redirect_from.py +++ b/doc/sphinxext/redirect_from.py @@ -33,7 +33,7 @@ """ from pathlib import Path -from docutils.parsers.rst import Directive +from sphinx.util.docutils import SphinxDirective from sphinx.domains import Domain from sphinx.util import logging @@ -51,9 +51,9 @@ def setup(app): - RedirectFrom.app = app app.add_directive("redirect-from", RedirectFrom) app.add_domain(RedirectFromDomain) + app.connect("builder-inited", _clear_redirects) app.connect("build-finished", _generate_redirects) metadata = {'parallel_read_safe': True} @@ -73,8 +73,8 @@ def redirects(self): """The mapping of the redirects.""" return self.data.setdefault('redirects', {}) - def clear_doc(self, docnames): - self.redirects.clear() + def clear_doc(self, docname): + self.redirects.pop(docname, None) def merge_domaindata(self, docnames, otherdata): for src, dst in otherdata['redirects'].items(): @@ -86,15 +86,14 @@ def merge_domaindata(self, docnames, otherdata): f"{self.redirects[src]} and {otherdata['redirects'][src]}") -class RedirectFrom(Directive): +class RedirectFrom(SphinxDirective): required_arguments = 1 def run(self): redirected_doc, = self.arguments - env = self.app.env - domain = env.get_domain('redirect_from') - current_doc = env.path2doc(self.state.document.current_source) - redirected_reldoc, _ = env.relfn2path(redirected_doc, current_doc) + domain = self.env.get_domain('redirect_from') + current_doc = self.env.path2doc(self.state.document.current_source) + redirected_reldoc, _ = self.env.relfn2path(redirected_doc, current_doc) if redirected_reldoc in domain.redirects: raise ValueError( f"{redirected_reldoc} is already noted as redirecting to " @@ -119,3 +118,10 @@ def _generate_redirects(app, exception): logger.info('making refresh html file: %s redirect to %s', k, v) p.parent.mkdir(parents=True, exist_ok=True) p.write_text(html, encoding='utf-8') + + +def _clear_redirects(app): + domain = app.env.get_domain('redirect_from') + if domain.redirects: + logger.info('clearing cached redirects') + domain.redirects.clear() diff --git a/doc/sphinxext/util.py b/doc/sphinxext/util.py new file mode 100644 index 000000000000..14097ba9396a --- /dev/null +++ b/doc/sphinxext/util.py @@ -0,0 +1,21 @@ +import sys + + +def matplotlib_reduced_latex_scraper(block, block_vars, gallery_conf, + **kwargs): + """ + Reduce srcset when creating a PDF. + + Because sphinx-gallery runs *very* early, we cannot modify this even in the + earliest builder-inited signal. Thus we do it at scraping time. + """ + from sphinx_gallery.scrapers import matplotlib_scraper + + if gallery_conf['builder_name'] == 'latex': + gallery_conf['image_srcset'] = [] + return matplotlib_scraper(block, block_vars, gallery_conf, **kwargs) + + +# Clear basic_units module to re-register with unit registry on import. +def clear_basic_units(gallery_conf, fname): + return sys.modules.pop('basic_units', None) diff --git a/doc/users/faq.rst b/doc/users/faq.rst index 970016ed19e6..1ff21d739108 100644 --- a/doc/users/faq.rst +++ b/doc/users/faq.rst @@ -363,17 +363,23 @@ provide the following information in your e-mail to the `mailing list If you compiled Matplotlib yourself, please also provide: -* any changes you have made to ``setup.py`` or ``setupext.py``. +* your compiler version -- e.g., ``gcc --version``. * the output of:: - rm -rf build - python setup.py build + pip install --verbose The beginning of the build output contains lots of details about your platform that are useful for the Matplotlib developers to diagnose your problem. -* your compiler version -- e.g., ``gcc --version``. +If you compiled an older version of Matplotlib using the pre-Meson build system, instead +provide: + +* any changes you have made to ``setup.py``/``setupext.py``, +* the output of:: + + rm -rf build + python setup.py build Including this information in your first e-mail to the mailing list will save a lot of time. diff --git a/doc/users/getting_started/index.rst b/doc/users/getting_started/index.rst index 68d62232ddc9..ac896687979d 100644 --- a/doc/users/getting_started/index.rst +++ b/doc/users/getting_started/index.rst @@ -22,7 +22,7 @@ Installation quick-start conda install -c conda-forge matplotlib -Further details are available in the :doc:`Installation Guide `. +Further details are available in the :doc:`Installation Guide `. Draw a first plot diff --git a/doc/users/github_stats.rst b/doc/users/github_stats.rst index 8dab8484d6fd..00c3e5d656a1 100644 --- a/doc/users/github_stats.rst +++ b/doc/users/github_stats.rst @@ -1,81 +1,99 @@ .. _github-stats: -GitHub statistics for 3.8.4 (Apr 03, 2024) +GitHub statistics for 3.9.2 (Aug 12, 2024) ========================================== -GitHub statistics for 2023/09/15 (tag: v3.8.0) - 2024/04/03 +GitHub statistics for 2024/07/04 (tag: v3.9.1) - 2024/08/12 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 9 issues and merged 45 pull requests. +The full list can be seen `on GitHub `__ -The following 26 authors contributed 351 commits. +The following 20 authors contributed 67 commits. -* 0taj -* Alec Vercruysse -* Alexander Volkov -* Antony Lee -* Anvi Verma -* Chiraag Balu -* David Gilbertson -* David Stansby +* Adam J. Stewart +* Anthony Lee +* Caitlin Hathaway +* ClarkeAC * dependabot[bot] * Elliott Sales de Andrade -* Eric Firing +* Filippo Balzaretti * Greg Lucas -* Gurudatta Shanbhag * hannah -* James Salsman +* Ian Thomas * Jody Klymak * Kyle Sunden -* lkkmpn -* Lucia Korpas -* Matthew Morrison * Oscar Gustafsson +* Randolf Scholz +* Refael Ackermann * Ruth Comer -* Samuel Diebolt +* Scott Shambaugh +* Sean Smith * Thomas A Caswell * Tim Hoffmann -* wemi3 GitHub issues and pull requests: -Pull Requests (27): +Pull Requests (45): -* :ghpull:`28015`: Backport PR #27955 on branch v3.8.x (Add a draw during show for macos backend) -* :ghpull:`27993`: Unpin numpy 2, build against prerelease numpy in CIBW -* :ghpull:`27955`: Add a draw during show for macos backend -* :ghpull:`28001`: Backport PR #28000 on branch v3.8.x (Fix color sequence data for Set2 and Set3) -* :ghpull:`28000`: Fix color sequence data for Set2 and Set3 -* :ghpull:`27990`: Backport PR #27988 on branch v3.8.x (gtk: Ensure pending draws are done before GTK draw) -* :ghpull:`27988`: gtk: Ensure pending draws are done before GTK draw -* :ghpull:`27986`: Backport PR #27985 on branch v3.8.x (TST: Remove superfluous chdir from tests) -* :ghpull:`27985`: TST: Remove superfluous chdir from tests -* :ghpull:`27976`: Backport PR #27975 on branch v3.8.x (DOC: Fix typo in ``ax.transData.inversed()``) -* :ghpull:`27934`: Backport PR #27933 on branch v3.8.x (Update "Created with" url in hand.svg) -* :ghpull:`27933`: Update "Created with" url in hand.svg -* :ghpull:`27926`: Backport PR #27875 on branch v3.8.x (macosx: Clean up single-shot timers correctly) -* :ghpull:`27925`: Backport PR #27921 on branch v3.8.x (Avoid modifying user input to Axes.bar) -* :ghpull:`27875`: macosx: Clean up single-shot timers correctly -* :ghpull:`27921`: Avoid modifying user input to Axes.bar -* :ghpull:`27903`: Merge 3.8.3-doc into 3.8.x -* :ghpull:`27889`: Backport PR #27888 on branch v3.8.x (DOC: fix stray release note entry) -* :ghpull:`27888`: DOC: fix stray release note entry -* :ghpull:`27849`: Backport PR #27754 on branch v3.8.x (fix quiver3d incorrect arrow colors) -* :ghpull:`27859`: Backport PR #27858 on branch v3.8.x (pin pytest) -* :ghpull:`27858`: pin pytest -* :ghpull:`27754`: fix quiver3d incorrect arrow colors -* :ghpull:`27847`: Backport PR #27846 on branch v3.8.x (Make example in legend_elements doc more generalisable) -* :ghpull:`27846`: Make example in legend_elements doc more generalisable -* :ghpull:`27802`: Backport PR #27794 on branch v3.8.x (Remove old reference to 72 DPI in figure_size_units.py) -* :ghpull:`27794`: Remove old reference to 72 DPI in figure_size_units.py +* :ghpull:`28687`: BLD: Include MSVCP140 runtime statically +* :ghpull:`28679`: Run delvewheel with path to required msvcp140.dll +* :ghpull:`28695`: Backport PR #27797 on branch v3.9.x (DOC: Use video files for saving animations) +* :ghpull:`28688`: Backport PR #28293 and #28668: Enable 3.13 wheels and bump cibuildwheel +* :ghpull:`27797`: DOC: Use video files for saving animations +* :ghpull:`28692`: Backport PR #28632 on branch v3.9.x (DOC: Tell sphinx-gallery to link mpl_toolkits from our build) +* :ghpull:`28632`: DOC: Tell sphinx-gallery to link mpl_toolkits from our build +* :ghpull:`28668`: Bump the actions group with 2 updates +* :ghpull:`28686`: Backport PR #28682 on branch v3.9.x (Fix warnings from mingw compilers) +* :ghpull:`28682`: Fix warnings from mingw compilers +* :ghpull:`28676`: Backport PR #28577 on branch v3.9.x (Copy all internals from initial Tick to lazy ones) +* :ghpull:`28577`: Copy all internals from initial Tick to lazy ones +* :ghpull:`28674`: Backport PR #28650 on branch v3.9.x (remove out of date todos on animation.py) +* :ghpull:`28650`: remove out of date todos on animation.py +* :ghpull:`28656`: Backport PR #28649 on branch v3.9.x (FIX: improve formatting of image values in cases of singular norms) +* :ghpull:`28665`: Backport PR #28546 on branch v3.9.x (DOC: Clarify/simplify example of multiple images with one colorbar) +* :ghpull:`28649`: FIX: improve formatting of image values in cases of singular norms +* :ghpull:`28635`: BLD: windows wheels +* :ghpull:`28645`: Backport PR #28644 on branch v3.9.x (DOC: Fix matching for version switcher) +* :ghpull:`28640`: Backport PR #28634 on branch v3.9.x (Closed open div tag in color.ColorMap._repr_html_) +* :ghpull:`28634`: Closed open div tag in color.ColorMap._repr_html_ +* :ghpull:`28636`: Backport PR #28625 on branch v3.9.x (added typing_extensions.Self to _AxesBase.twinx) +* :ghpull:`28625`: added typing_extensions.Self to _AxesBase.twinx +* :ghpull:`28622`: Backport PR #28621 on branch v3.9.x (TYP: Fix a typo in animation.pyi) +* :ghpull:`28621`: TYP: Fix a typo in animation.pyi +* :ghpull:`28605`: Backport PR #28604 on branch v3.9.x (cycler signature update.) +* :ghpull:`28604`: cycler signature update. +* :ghpull:`28598`: Pin PyQt6 back on Ubuntu 20.04 +* :ghpull:`28596`: Backport PR #28518 on branch v3.9.x ([TYP] Fix overload of ``pyplot.subplots``) +* :ghpull:`28518`: [TYP] Fix overload of ``pyplot.subplots`` +* :ghpull:`28591`: Backport PR #28580 on branch v3.9.x (Bump actions/attest-build-provenance from 1.3.2 to 1.3.3 in the actions group) +* :ghpull:`28580`: Bump actions/attest-build-provenance from 1.3.2 to 1.3.3 in the actions group +* :ghpull:`28586`: Backport PR #28582 on branch v3.9.x (FIX: make sticky edge tolerance relative to data range) +* :ghpull:`28582`: FIX: make sticky edge tolerance relative to data range +* :ghpull:`28572`: Backport PR #28571 on branch v3.9.x (DOC: Add version directive to hatch parameter in stackplot) +* :ghpull:`28571`: DOC: Add version directive to hatch parameter in stackplot +* :ghpull:`28564`: Backport PR #28534 on branch v3.9.x ([BLD] Fix WSL build warning) +* :ghpull:`28563`: Backport PR #28526 on branch v3.9.x (Bump pypa/cibuildwheel from 2.19.1 to 2.19.2 in the actions group) +* :ghpull:`28534`: [BLD] Fix WSL build warning +* :ghpull:`28526`: Bump pypa/cibuildwheel from 2.19.1 to 2.19.2 in the actions group +* :ghpull:`28552`: Backport PR #28541 on branch v3.9.x (MNT: be more careful about disk I/O failures when writing font cache) +* :ghpull:`28541`: MNT: be more careful about disk I/O failures when writing font cache +* :ghpull:`28524`: Backport PR #28523 on branch v3.9.x (Fix value error when set widget size to zero while using FigureCanvasQT ) +* :ghpull:`28523`: Fix value error when set widget size to zero while using FigureCanvasQT +* :ghpull:`28519`: Backport PR #28517 on branch v3.9.x (DOC: better cross referencing for animations) -Issues (3): +Issues (9): -* :ghissue:`27953`: [Bug]: Pyplot can no longer set axes properties -* :ghissue:`11759`: The color of the 3D arrow head does not match that of the arrow body -* :ghissue:`27826`: [Bug]: Unexpected behavior of scatter.legend_elements +* :ghissue:`28551`: [Bug]: Possible issue with Matplotlib 3.9.1 wheel on Windows only +* :ghissue:`28250`: [Doc]: Sphinx gallery links mispointed for Axes3D methods +* :ghissue:`28574`: [Bug]: Nondeterministic behavior with subplot spacing and constrained layout +* :ghissue:`28626`: [Doc]: Remove old TODO's from animation.py +* :ghissue:`28648`: [Bug]: format_image_data on an image of only zeros produses a large number of zeros +* :ghissue:`28624`: [Bug]: Bad type hint in ``_AxesBase.twinx()`` +* :ghissue:`28567`: [Bug]: sticky edge related changes for datetime plots +* :ghissue:`28533`: [Doc]: Stackplot hatch functionality has version dependencies +* :ghissue:`28538`: [Bug]: Permission denied when importing matplotlib.pyplot Previous GitHub statistics diff --git a/doc/users/index.rst b/doc/users/index.rst index 64317fd61607..2991e7d2b324 100644 --- a/doc/users/index.rst +++ b/doc/users/index.rst @@ -101,6 +101,5 @@ Using Matplotlib .. toctree:: :hidden: - explain/index getting_started/index - installing/index + ../install/index diff --git a/doc/users/prev_whats_new/dflt_style_changes.rst b/doc/users/prev_whats_new/dflt_style_changes.rst index e86e34c50e46..a833064b573b 100644 --- a/doc/users/prev_whats_new/dflt_style_changes.rst +++ b/doc/users/prev_whats_new/dflt_style_changes.rst @@ -13,7 +13,7 @@ are designed to work well in the most common cases. A 'classic' style sheet is provided so reverting to the 1.x default values is a single line of python -.. code:: +.. code-block:: python import matplotlib.style import matplotlib as mpl @@ -102,14 +102,14 @@ denote the first 10 colors in :rc:`axes.prop_cycle`. See To restore the old color cycle use -.. code:: +.. code-block:: python from cycler import cycler mpl.rcParams['axes.prop_cycle'] = cycler(color='bgrcmyk') or set -.. code:: +.. code-block:: cfg axes.prop_cycle : cycler('color', 'bgrcmyk') @@ -156,13 +156,13 @@ map. For details on all of the colormaps available in matplotlib see The previous default can be restored using -.. code:: +.. code-block:: python mpl.rcParams['image.cmap'] = 'jet' or setting -.. code:: +.. code-block:: cfg image.cmap : 'jet' diff --git a/doc/users/prev_whats_new/github_stats_3.0.0.rst b/doc/users/prev_whats_new/github_stats_3.0.0.rst index ab90e5e79e4e..0e9c4b3b588d 100644 --- a/doc/users/prev_whats_new/github_stats_3.0.0.rst +++ b/doc/users/prev_whats_new/github_stats_3.0.0.rst @@ -128,7 +128,7 @@ The following 478 authors contributed 9809 commits. * Drew J. Sonne * Duncan Macleod * Dylan Evans -* E. G. Patrick Bos +* E\. G\. Patrick Bos * Egor Panfilov * Elijah Schutz * Elizabeth Seiver @@ -205,7 +205,7 @@ The following 478 authors contributed 9809 commits. * Isaac Slavitt * Ismo Toijala * J Alammar -* J. Goutin +* J\. Goutin * Jaap Versteegh * Jacob McDonald * jacob-on-github @@ -464,7 +464,7 @@ The following 478 authors contributed 9809 commits. * Tuan Dung Tran * u55 * ultra-andy -* V. R +* V\. R * vab9 * Valentin Schmidt * Vedant Nanda diff --git a/doc/users/prev_whats_new/github_stats_3.2.0.rst b/doc/users/prev_whats_new/github_stats_3.2.0.rst index 5fd75f7c57d0..3cb3fce5de52 100644 --- a/doc/users/prev_whats_new/github_stats_3.2.0.rst +++ b/doc/users/prev_whats_new/github_stats_3.2.0.rst @@ -119,7 +119,7 @@ The following 164 authors contributed 3455 commits. * Nicolas Courtemanche * Nikita Kniazev * njwhite -* O. Castany +* O\. Castany * Oliver Natt * Olivier * Om Sitapara @@ -142,7 +142,7 @@ The following 164 authors contributed 3455 commits. * Richard Ji-Cathriner * RoryIAngus * Ryan May -* S. Fukuda +* S\. Fukuda * Samesh * Samesh Lakhotia * sasoripathos @@ -164,7 +164,7 @@ The following 164 authors contributed 3455 commits. * Tim Hoffmann * Tom Flannaghan * Travis CI -* V. Armando Solé +* V\. Armando Solé * Vincent L.M. Mazoyer * Viraj Mohile * Wafa Soofi diff --git a/doc/users/prev_whats_new/github_stats_3.4.0.rst b/doc/users/prev_whats_new/github_stats_3.4.0.rst index fe49e673a660..b2568058b455 100644 --- a/doc/users/prev_whats_new/github_stats_3.4.0.rst +++ b/doc/users/prev_whats_new/github_stats_3.4.0.rst @@ -82,7 +82,7 @@ The following 177 authors contributed 3852 commits. * ImportanceOfBeingErnest * Isuru Fernando * ItsRLuo -* J. Scott Berg +* J\. Scott Berg * Jae-Joon Lee * Jakub Klus * Janakarajan Natarajan diff --git a/doc/users/prev_whats_new/github_stats_3.8.4.rst b/doc/users/prev_whats_new/github_stats_3.8.4.rst new file mode 100644 index 000000000000..324393b12f9d --- /dev/null +++ b/doc/users/prev_whats_new/github_stats_3.8.4.rst @@ -0,0 +1,78 @@ +.. _github-stats-3-8-4: + +GitHub statistics for 3.8.4 (Apr 03, 2024) +========================================== + +GitHub statistics for 2023/09/15 (tag: v3.8.0) - 2024/04/03 + +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 26 authors contributed 351 commits. + +* 0taj +* Alec Vercruysse +* Alexander Volkov +* Antony Lee +* Anvi Verma +* Chiraag Balu +* David Gilbertson +* David Stansby +* dependabot[bot] +* Elliott Sales de Andrade +* Eric Firing +* Greg Lucas +* Gurudatta Shanbhag +* hannah +* James Salsman +* Jody Klymak +* Kyle Sunden +* lkkmpn +* Lucia Korpas +* Matthew Morrison +* Oscar Gustafsson +* Ruth Comer +* Samuel Diebolt +* Thomas A Caswell +* Tim Hoffmann +* wemi3 + +GitHub issues and pull requests: + +Pull Requests (27): + +* :ghpull:`28015`: Backport PR #27955 on branch v3.8.x (Add a draw during show for macos backend) +* :ghpull:`27993`: Unpin numpy 2, build against prerelease numpy in CIBW +* :ghpull:`27955`: Add a draw during show for macos backend +* :ghpull:`28001`: Backport PR #28000 on branch v3.8.x (Fix color sequence data for Set2 and Set3) +* :ghpull:`28000`: Fix color sequence data for Set2 and Set3 +* :ghpull:`27990`: Backport PR #27988 on branch v3.8.x (gtk: Ensure pending draws are done before GTK draw) +* :ghpull:`27988`: gtk: Ensure pending draws are done before GTK draw +* :ghpull:`27986`: Backport PR #27985 on branch v3.8.x (TST: Remove superfluous chdir from tests) +* :ghpull:`27985`: TST: Remove superfluous chdir from tests +* :ghpull:`27976`: Backport PR #27975 on branch v3.8.x (DOC: Fix typo in ``ax.transData.inversed()``) +* :ghpull:`27934`: Backport PR #27933 on branch v3.8.x (Update "Created with" url in hand.svg) +* :ghpull:`27933`: Update "Created with" url in hand.svg +* :ghpull:`27926`: Backport PR #27875 on branch v3.8.x (macosx: Clean up single-shot timers correctly) +* :ghpull:`27925`: Backport PR #27921 on branch v3.8.x (Avoid modifying user input to Axes.bar) +* :ghpull:`27875`: macosx: Clean up single-shot timers correctly +* :ghpull:`27921`: Avoid modifying user input to Axes.bar +* :ghpull:`27903`: Merge 3.8.3-doc into 3.8.x +* :ghpull:`27889`: Backport PR #27888 on branch v3.8.x (DOC: fix stray release note entry) +* :ghpull:`27888`: DOC: fix stray release note entry +* :ghpull:`27849`: Backport PR #27754 on branch v3.8.x (fix quiver3d incorrect arrow colors) +* :ghpull:`27859`: Backport PR #27858 on branch v3.8.x (pin pytest) +* :ghpull:`27858`: pin pytest +* :ghpull:`27754`: fix quiver3d incorrect arrow colors +* :ghpull:`27847`: Backport PR #27846 on branch v3.8.x (Make example in legend_elements doc more generalisable) +* :ghpull:`27846`: Make example in legend_elements doc more generalisable +* :ghpull:`27802`: Backport PR #27794 on branch v3.8.x (Remove old reference to 72 DPI in figure_size_units.py) +* :ghpull:`27794`: Remove old reference to 72 DPI in figure_size_units.py + +Issues (3): + +* :ghissue:`27953`: [Bug]: Pyplot can no longer set axes properties +* :ghissue:`11759`: The color of the 3D arrow head does not match that of the arrow body +* :ghissue:`27826`: [Bug]: Unexpected behavior of scatter.legend_elements diff --git a/doc/users/prev_whats_new/github_stats_3.9.0.rst b/doc/users/prev_whats_new/github_stats_3.9.0.rst new file mode 100644 index 000000000000..b1d229ffbfa1 --- /dev/null +++ b/doc/users/prev_whats_new/github_stats_3.9.0.rst @@ -0,0 +1,744 @@ +.. _github-stats-3-9.0: + +GitHub statistics for 3.9.0 (May 15, 2024) +========================================== + +GitHub statistics for 2023/09/15 (tag: v3.8.0) - 2024/05/15 + +These lists are automatically generated, and may be incomplete or contain duplicates. + +We closed 97 issues and merged 450 pull requests. +The full list can be seen `on GitHub `__ + +The following 175 authors contributed 2584 commits. + +* 0taj +* Abdul Razak Taha +* Adam J. Stewart +* Adam Turner +* Aditi Gautam +* agautam478 +* Alan Lau +* Albert Y. Shih +* Alec Vercruysse +* Alexander Volkov +* Alice Descoeudres +* Allan Haldane +* Amirreza Aflakparast +* Ananya Devarakonda +* ananya314 +* Anja Beck +* Anjini2004 +* Ant Lockyer +* Antony Lee +* Anvi Verma +* Artyom Romanov +* Augusto Borges +* avramid9 +* Ben Root +* bersbersbers +* Binaya Sharma +* Cameron +* Chaoyi Hu +* chaoyihu +* Chiraag Balu +* Christoph Hasse +* ConstableCatnip +* CozyFrog +* Cyril Gadal +* Dale Dai +* Daniel Bergman +* Daniel Hitchcock +* danielcobej +* David Gilbertson +* David Stansby +* ddale1128@gmail.com +* dependabot[bot] +* Devilsaint +* dohyun +* Drew Kinneer +* DWesl +* Elisa Heckelmann +* ElisaHeck +* Elliott Sales de Andrade +* Eric Firing +* Eric Prestat +* esibinga +* Eva Sibinga +* Evgenii Radchenko +* Faisal Fawad +* Felipe Cybis Pereira +* Garrett Sward +* Gaurav-Kumar-Soni +* Gauri Chaudhari +* Gautam Sagar +* Greg Lucas +* Gurudatta Shanbhag +* hannah +* Haoying Zhang +* Hugues Hoppe +* i-jey +* iamfaham +* Ian Hunt-Isaak +* Ian Thomas +* ifEricReturnTrue +* Illviljan +* Issam +* Issam Arabi +* Jacob Stevens-Haas +* Jacob Tomlinson +* Jake +* Jake Stevens-Haas +* James Salsman +* Jaroza727 +* Jeremy Farrell +* Jirka +* Jody Klymak +* Jorge Moraleda +* Joshua Stevenson +* jovianw +* João Andrade +* jpgianfaldoni +* jsdodge +* jsjeelshah +* judfs +* Juhan Oskar Hennoste +* Junpei Ota +* Katherine Turk +* katotaisei +* KheshavKumar +* Koustav Ghosh +* Kritika Verma +* Kyle Sunden +* Linyi Li +* linyilily +* lkkmpn +* Lucia Korpas +* madisonwong210 +* Maggie Liu +* Marc Bresson +* Matthew Feickert +* Matthew Morrison +* Matthias Bussonnier +* Melissa Weber Mendonça +* melissawm +* mliu08 +* Mostafa Noah +* MostafaNouh0011 +* n-aswin +* Nabil +* nbarlowATI +* Nidaa Rabah +* Nivedita Chaudhari +* Oscar Gustafsson +* patel-zeel +* Pavel Liavonau +* Pedro +* Pedro Peçanha +* Peter Talley +* Pradeep Reddy Raamana +* Prajwal Agrawal +* Pranav Raghu +* prateetishah +* pre-commit-ci[bot] +* QuadroTec +* Rafael Tsuha +* Raghuram Sirigiri +* Raphael +* Raphael Quast +* Ratnabali Dutta +* rawwash +* rsp2210 +* Ruoyi +* Ruoyi Xie +* Rushikesh Pandya +* Ruth Comer +* samGreer +* Samuel Diebolt +* saranti +* Scott Shambaugh +* Sebastian Berg +* Seohyeon Lee +* Sheepfan0828 +* ShivamPathak99 +* Shriya Kalakata +* shriyakalakata +* Stefan +* Steffen Rehberg +* stevezhang1999 +* Sudhanshu Pandey +* Talha Irfan +* thehappycheese +* Thomas A Caswell +* Tiago Lubiana +* Tim Hoffmann +* tobias +* Tom Sarantis +* trananso +* turnipseason +* tusharkulkarni008 +* UFEddy +* Vashesh08 +* vicky6 +* vigneshvetrivel8 +* wemi3 +* yangyangdotcom +* YiLun Fan +* Zach Champion +* zachjweiner +* zoehcycy + +GitHub issues and pull requests: + +Pull Requests (450): + +* :ghpull:`28206`: Backport PR #28205 on branch v3.9.x (TST: Fix tests with older versions of ipython) +* :ghpull:`28207`: TST: Followup corrections to #28205 +* :ghpull:`28205`: TST: Fix tests with older versions of ipython +* :ghpull:`28203`: Backport PR #28164 on branch v3.9.x (CI: Ensure code coverage is always uploaded) +* :ghpull:`28204`: Backport PR #28195 on branch v3.9.x (TST: Prepare for pytest 9) +* :ghpull:`28191`: DOC: Use released mpl-sphinx-theme on v3.9.x +* :ghpull:`28195`: TST: Prepare for pytest 9 +* :ghpull:`28193`: Backport PR #28185 on branch v3.9.x (DOC: Bump mpl-sphinx-theme to 3.9) +* :ghpull:`28190`: Backport PR #28103 on branch v3.9.x ([DOC]: Fix compatibility with sphinx-gallery 0.16) +* :ghpull:`28164`: CI: Ensure code coverage is always uploaded +* :ghpull:`28194`: Backport PR #28188 on branch v3.9.x ([TST] Bump some tolerances for Macos ARM) +* :ghpull:`28188`: [TST] Bump some tolerances for Macos ARM +* :ghpull:`28185`: DOC: Bump mpl-sphinx-theme to 3.9 +* :ghpull:`28189`: Backport PR #28181 on branch v3.9.x (DOC: Prepare release notes for 3.9) +* :ghpull:`28103`: [DOC]: Fix compatibility with sphinx-gallery 0.16 +* :ghpull:`28181`: DOC: Prepare release notes for 3.9 +* :ghpull:`28184`: Backport PR #28182 on branch v3.9.x (Bump custom hatch deprecation expiration) +* :ghpull:`28182`: Bump custom hatch deprecation expiration +* :ghpull:`28178`: Backport PR #28171 on branch v3.9.x (Support removing absent tools from ToolContainerBase.) +* :ghpull:`28171`: Support removing absent tools from ToolContainerBase. +* :ghpull:`28174`: Backport PR #28169 on branch v3.9.x (Clarify public-ness of some ToolContainerBase APIs.) +* :ghpull:`28169`: Clarify public-ness of some ToolContainerBase APIs. +* :ghpull:`28160`: Backport PR #28039 on branch v3.9.x (Respect vertical_axis when rotating plot interactively) +* :ghpull:`28159`: Backport PR #28157 on branch v3.9.x (Remove call to non-existent method _default_contains in Artist) +* :ghpull:`28162`: Backport PR #27948 on branch v3.9.x (Move IPython backend mapping to Matplotlib and support entry points) +* :ghpull:`28163`: Backport PR #28144 on branch v3.9.x (DOC: Refactor code in the fishbone diagram example) +* :ghpull:`28144`: DOC: Refactor code in the fishbone diagram example +* :ghpull:`27948`: Move IPython backend mapping to Matplotlib and support entry points +* :ghpull:`28039`: Respect vertical_axis when rotating plot interactively +* :ghpull:`28157`: Remove call to non-existent method _default_contains in Artist +* :ghpull:`28141`: Backport PR #27960 on branch v3.9.x (Update AppVeyor config) +* :ghpull:`28138`: Backport PR #28068 on branch v3.9.x ([TYP] Add possible type hint to ``colors`` argument in ``LinearSegmentedColormap.from_list``) +* :ghpull:`28140`: Backport PR #28136 on branch v3.9.x (Appease pycodestyle.) +* :ghpull:`27960`: Update AppVeyor config +* :ghpull:`28068`: [TYP] Add possible type hint to ``colors`` argument in ``LinearSegmentedColormap.from_list`` +* :ghpull:`28136`: Appease pycodestyle. +* :ghpull:`28135`: Backport PR #28134 on branch v3.9.x (DOC: Minor improvements on quickstart) +* :ghpull:`28134`: DOC: Minor improvements on quickstart +* :ghpull:`28121`: Backport PR #28085 on branch v3.9.x (Clarify that the pgf backend is never actually used interactively.) +* :ghpull:`28120`: Backport PR #28102 on branch v3.9.x (Fix typo in color mapping documentation in quick_start.py) +* :ghpull:`28109`: Backport PR #28100 on branch v3.9.x (TST: wxcairo sometimes raises OSError on missing cairo libraries) +* :ghpull:`28100`: TST: wxcairo sometimes raises OSError on missing cairo libraries +* :ghpull:`28108`: Backport PR #28107 on branch v3.9.x ([DOC] Fix description in CapStyle example) +* :ghpull:`28107`: [DOC] Fix description in CapStyle example +* :ghpull:`28102`: Fix typo in color mapping documentation in quick_start.py +* :ghpull:`28095`: Backport PR #28094 on branch v3.9.x (DOC: exclude sphinx 7.3.*) +* :ghpull:`28081`: Backport PR #28078 on branch v3.9.x (Clarify that findfont & _find_fonts_by_props return paths.) +* :ghpull:`28080`: Backport PR #28077 on branch v3.9.x (Parent tk StringVar to the canvas widget, not to the toolbar.) +* :ghpull:`28092`: Backport PR #28032 on branch v3.9.x (FIX: ensure images are C order before passing to pillow) +* :ghpull:`28032`: FIX: ensure images are C order before passing to pillow +* :ghpull:`28088`: Backport PR #28087 on branch v3.9.x (Document Qt5 minimal version.) +* :ghpull:`28085`: Clarify that the pgf backend is never actually used interactively. +* :ghpull:`28078`: Clarify that findfont & _find_fonts_by_props return paths. +* :ghpull:`28077`: Parent tk StringVar to the canvas widget, not to the toolbar. +* :ghpull:`28062`: Backport PR #28056 on branch v3.9.x (Strip trailing spaces from log-formatter cursor output.) +* :ghpull:`28063`: Backport PR #28055 on branch v3.9.x (DOC: Improve inverted axis example) +* :ghpull:`28056`: Strip trailing spaces from log-formatter cursor output. +* :ghpull:`28049`: Backport PR #28036 on branch v3.9.x (BLD: Fetch version from setuptools_scm at build time) +* :ghpull:`28036`: BLD: Fetch version from setuptools_scm at build time +* :ghpull:`28038`: Backport PR #28023 on branch v3.9.x (ci: Update merge conflict labeler) +* :ghpull:`28023`: ci: Update merge conflict labeler +* :ghpull:`28035`: Backport PR #28026 on branch v3.9.x ([DOC] reshuffle of contributing) +* :ghpull:`28026`: [DOC] reshuffle of contributing +* :ghpull:`28024`: DOC: Rewrite "Work on an issue" section +* :ghpull:`28011`: DOC: Move bug reports and feature requests to top of contributing index +* :ghpull:`27747`: Move doc/users/installing/ to doc/install/ +* :ghpull:`27952`: ENH: Align titles +* :ghpull:`28017`: Merge up v3.8.4 +* :ghpull:`28014`: Improve timeline example. +* :ghpull:`28019`: DOC: correct path to mpl_toolkits reference images +* :ghpull:`26981`: Fixes Issue #26377 - Auto-escape % Symbol in Latex in pie labels +* :ghpull:`28007`: wx: Fix file extension for toolmanager-style toolbar +* :ghpull:`25556`: Display cursor coordinates for all axes twinned with the current one. +* :ghpull:`23597`: Always use PyQT/PySide6 for GitHub CI +* :ghpull:`28013`: Avoid plt.xticks/plt.yticks in gallery examples. +* :ghpull:`28006`: Fix deprecation warnings in ft2font extension +* :ghpull:`27723`: ci: Enable testing on M1 macOS +* :ghpull:`26375`: Add ``widths``, ``heights`` and ``angles`` setter to ``EllipseCollection`` +* :ghpull:`27999`: Remove documentation that some backends don't support hatching. +* :ghpull:`26710`: Add support for High DPI displays to wxAgg backend +* :ghpull:`27148`: Correctly treat pan/zoom events of overlapping axes. +* :ghpull:`27981`: DOC: Fix label type specification in parameter descriptions +* :ghpull:`27979`: Clarify error message for bad-dimensionality in pcolorfast(). +* :ghpull:`27962`: DOC: Document axes_grid1.Grid attributes +* :ghpull:`27968`: MNT: Remove remaining 3.7 deprecations +* :ghpull:`27965`: DOC: Rewrite the example illustrating bxp() +* :ghpull:`26453`: add documentation for reloading font cache +* :ghpull:`26131`: Tst/restore old tests +* :ghpull:`27730`: Add an rcparam for image.interpolation_stage. +* :ghpull:`27956`: Use PyOS_setsig in macos backend +* :ghpull:`27829`: Simplify color/marker disambiguation logic in _process_plot_format. +* :ghpull:`27840`: Add legend support for boxplots +* :ghpull:`27943`: Support Cn, n>9 in plot() shorthand format. +* :ghpull:`27950`: ci: Fix condition for publishing wheels +* :ghpull:`27909`: Add a note to pyplot docstrings referencing the corresponding object methods +* :ghpull:`27929`: DOC: Add summary lines to plot types +* :ghpull:`27915`: [BUG] Fix redirect-from Sphinx extension +* :ghpull:`27945`: DOC: Explain leading dot in object references +* :ghpull:`27947`: Update docs for ``FancyArrowPatch`` & ``Annotation`` to make it clear that ShrinkA/B parameters are in points and not fractional. +* :ghpull:`27944`: Bump the actions group with 2 updates +* :ghpull:`27932`: Fix pickling of make_axes_area_auto_adjustable'd axes. +* :ghpull:`26500`: closes #26477 ENH: Add interpolation_stage in qt figureoptions +* :ghpull:`27927`: Update docs +* :ghpull:`27916`: Revert renaming labels to tick_labels in boxplot_stats() +* :ghpull:`27931`: Highlight development_setup code snippets as bash, not python. +* :ghpull:`27856`: Support hatching in cairo backends. +* :ghpull:`27922`: Fix cbook style +* :ghpull:`27668`: MNT: prevent merging using labels + branch protection rules +* :ghpull:`27857`: Documentation edit for matshow function +* :ghpull:`27928`: DOC: Fix syntax for ToolBase.image docstring +* :ghpull:`27873`: Simplify the LineCollection example +* :ghpull:`27492`: Fix semantics of MEP22 image names. +* :ghpull:`27918`: Fix new flake8 errors from old merge +* :ghpull:`27874`: Modernize macosx backend a bit +* :ghpull:`25887`: Update ``_unpack_to_numpy`` function to convert JAX and PyTorch arrays to NumPy +* :ghpull:`27685`: Work around pyparsing diagnostic warnings +* :ghpull:`26594`: Added optional props argument to Lasso Widget __init__ to customize Lasso line +* :ghpull:`22761`: Add minor ticks on and off in Axis +* :ghpull:`22407`: Add ``set_XY`` and ``set_data`` to ``Quiver`` +* :ghpull:`27901`: Rename boxplot's tick label parameter +* :ghpull:`27883`: Fix build on older macOS deployment targets +* :ghpull:`27900`: Remove empty user guide tutorials page +* :ghpull:`27885`: Clean up headers in extensions +* :ghpull:`27910`: DOC: Fix dead link in README +* :ghpull:`26567`: Use SVG inheritance diagrams now that linking has been fixed +* :ghpull:`27899`: Merge up 3.8.x into main +* :ghpull:`27905`: Improved error message for malformed colors +* :ghpull:`27906`: Override open_group, close_group methods in PathEffectRenderer +* :ghpull:`27904`: FIX: Restore D213 in flake8 +* :ghpull:`27895`: Remove versions from sidebar in docs +* :ghpull:`27894`: Mark triangulation classes as final +* :ghpull:`27557`: Use :mpltype:``color`` for color types +* :ghpull:`27845`: Make sure custom alpha param does not change 'none' colors in a list of colors +* :ghpull:`27719`: Add BackendRegistry singleton class +* :ghpull:`27890`: DOC: State approximate documentation build time +* :ghpull:`27887`: BLD: Add a fallback URL for FreeType +* :ghpull:`25224`: Allow passing a transformation to secondary_xaxis/_yaxis +* :ghpull:`27886`: Fix devdocs version switcher +* :ghpull:`27884`: FIX: don't copy twice on RGB input +* :ghpull:`27087`: Convert path extension to pybind11 +* :ghpull:`27867`: DOC: Update some animation related topics +* :ghpull:`27848`: FIX: handle nans in RGBA input with ScalarMappables +* :ghpull:`27821`: BLD,Cygwin: Include Python.h first in various C++ files +* :ghpull:`27457`: TST: adding tests of current clear behavior on ticks +* :ghpull:`27872`: doc: add description of ``**kwargs`` usage to collections +* :ghpull:`27868`: Use pybind11 string formatter for exception messages +* :ghpull:`27862`: Add dtype/copy args to internal testing class +* :ghpull:`27658`: Bump pydata-sphinx-theme +* :ghpull:`27303`: FIX: also exclude np.nan in RGB(A) in color mapping +* :ghpull:`27860`: Bump the actions group with 2 updates +* :ghpull:`27869`: Correctly set temporary pdf/pgf backends +* :ghpull:`27850`: Deprecate ``plot_date`` +* :ghpull:`27815`: Add side option to violinplot +* :ghpull:`27836`: DOC: use ... for continuation prompt in docstrings +* :ghpull:`27819`: MNT: remove draw method args and kwargs +* :ghpull:`27813`: DOC: Update violinplot() docs +* :ghpull:`27698`: Add linting and validation of all YAML files +* :ghpull:`27811`: Fix Annulus width check +* :ghpull:`27667`: Change return type of ``ion`` and ``ioff`` to fix unbound variable errors with Pyright +* :ghpull:`27807`: Expand CI pytest reporting config to ignore xfails +* :ghpull:`27806`: Remove self._renderer from AnnotationBbox and ConnectionPatch +* :ghpull:`27799`: Clarify that set_ticks() affects major/minor ticks independently +* :ghpull:`27787`: Improve documentation on boxplot and violinplot +* :ghpull:`27800`: Deactivate sidebar for release notes +* :ghpull:`27798`: Fix sphinx-gallery CSS +* :ghpull:`27462`: DOC: clarify the default value of *radius* in Patch.contains_point +* :ghpull:`27565`: MNT: arghandling subplotspec +* :ghpull:`27796`: Make mypy a bit stricter +* :ghpull:`27767`: Update handling of sequence labels for plot +* :ghpull:`27795`: Add EffVer badge +* :ghpull:`27780`: Partly revert #27711 +* :ghpull:`27768`: MNT: deprecate draw method args and kwargs +* :ghpull:`27783`: Update README.md to fix citation link +* :ghpull:`27726`: TST: always set a (long) timeout for subprocess and always use our wrapper +* :ghpull:`27781`: Simplify example: Box plots with custom fill colors +* :ghpull:`27750`: Bump the actions group with 2 updates +* :ghpull:`27771`: Add marker-only and line+marker visuals to the plot() plot types +* :ghpull:`27764`: Increase size of legend in Legend guide example +* :ghpull:`26800`: Bump minimum NumPy version to 1.23 +* :ghpull:`27752`: Update some Meson internals +* :ghpull:`27702`: GOV: adopt EffVer +* :ghpull:`26965`: Removal of deprecated API cm +* :ghpull:`27758`: [Doc] Remove special casing for removed method +* :ghpull:`25815`: [TST] Make jpl units instantiated with datetimes consistent with mpl converters +* :ghpull:`27729`: DOC: Improve colormap normalization example +* :ghpull:`27732`: TST: Remove memory leak test +* :ghpull:`27733`: ci: Simplify CodeQL setup +* :ghpull:`27692`: Add method to update position of arrow patch +* :ghpull:`27736`: Fix incorrect API reference in docs +* :ghpull:`27731`: DOC: Create explicit rename legend entry section in guide +* :ghpull:`27560`: Moved /users/project to /doc/project +* :ghpull:`27728`: Simplify Figure._suplabels. +* :ghpull:`27715`: Bump the actions group with 3 updates +* :ghpull:`27711`: Fix boxplot legend entries part 2 +* :ghpull:`27696`: DOC: clean up automated tests section of workflow docs +* :ghpull:`27686`: Improve Locator docstrings +* :ghpull:`27704`: ci: Remove prerelease conditions from Azure Pipelines +* :ghpull:`27568`: Fix boxplot legend entries +* :ghpull:`27694`: MNT: fix labeller +* :ghpull:`26953`: MNT: test that table doesn't try to convert unitized data +* :ghpull:`27690`: Remove "Past versions" section from release notes +* :ghpull:`26926`: Closes #22011: Changes to SubFigures so it behaves like a regular artist +* :ghpull:`27469`: Fixed legend with legend location "best" when legend overlaps shaded area and text +* :ghpull:`27684`: Bump the actions group with 1 update +* :ghpull:`27665`: Axes.inset_axes - warning message removed +* :ghpull:`27688`: CI: skip code coverage upload on scheduled tests +* :ghpull:`27689`: ci: Don't include API/what's new notes in general doc labels +* :ghpull:`27640`: Add ``get_cursor_data`` to ``NonUniformImage`` +* :ghpull:`27676`: BLD: Downgrade FreeType to 2.6.1 on Windows ARM +* :ghpull:`27619`: Use GH action to install reviewdog +* :ghpull:`27552`: TST: Use importlib for importing in pytest +* :ghpull:`27650`: DOC: Added call out to API guidelines to contribute + small API guidelines reorg +* :ghpull:`27618`: Add option of running stubtest using tox +* :ghpull:`27656`: Bump the actions group with 1 update +* :ghpull:`27415`: Use class form of data classes +* :ghpull:`27649`: Check for latex binary before building docs +* :ghpull:`27641`: MNT: fix api changes link in PR template +* :ghpull:`27644`: ci: Fix mpl_toolkits label +* :ghpull:`27230`: Query macOS for available system fonts. +* :ghpull:`27643`: ci: Update nightly upload for artifacts v4 +* :ghpull:`27642`: Fix auto-labeler configuration +* :ghpull:`27639`: Doc: typo fix for #22699 +* :ghpull:`26978`: [pre-commit.ci] pre-commit autoupdate +* :ghpull:`27563`: Enable PyPI publishing from GitHub Actions +* :ghpull:`22699`: Proof of concept for adding kwdoc content to properties using a decorator +* :ghpull:`27633`: Auto-label PRs based on changed files +* :ghpull:`27607`: Error on bad input to hexbin extents +* :ghpull:`27629`: Don't run CI twice on dependabot branches +* :ghpull:`27562`: Avoid an extra copy/resample if imshow input has no alpha +* :ghpull:`27628`: Bump the actions group with 2 updates +* :ghpull:`27626`: CI: Group dependabot updates +* :ghpull:`27589`: Don't clip PowerNorm inputs < vmin +* :ghpull:`27613`: Fix marker validator with cycler (allow mix of classes) +* :ghpull:`27615`: MNT: add spaces to PR template +* :ghpull:`27614`: DOC: Updated link in annotation API docs to point to annotation user guide +* :ghpull:`27605`: Ignore masked values in boxplot +* :ghpull:`26884`: Remove deprecated code from _fontconfig_patterns +* :ghpull:`27602`: Let FormatStrFormatter respect axes.unicode_minus. +* :ghpull:`27601`: Clarify dollar_ticks example and FormatStrFormatter docs. +* :ghpull:`24834`: Deprecate apply_theta_transforms=True to PolarTransform +* :ghpull:`27591`: Use macOS instead of OSX in comments/docs +* :ghpull:`27577`: MNT: add the running version to pickle warning message +* :ghpull:`25191`: Deprecate 'prune' kwarg to MaxNLocator +* :ghpull:`27566`: DOC: changed tag ``plot type`` to ``plot-type`` +* :ghpull:`27105`: Use Axes instead of axes core library code +* :ghpull:`27575`: Add quotes round .[dev] in editable install command +* :ghpull:`27104`: Use Axes instead of axes in galleries +* :ghpull:`27373`: Transpose grid_finder tick representation. +* :ghpull:`27363`: ci: Improve coverage for compiled code +* :ghpull:`27200`: DOC: Add role for custom informal types like color +* :ghpull:`27548`: DOC: typo fix in contribute doc +* :ghpull:`27458`: Check if the mappable is in a different Figure than the one fig.color… +* :ghpull:`27546`: MNT: Clean up some style exceptions +* :ghpull:`27514`: Improve check for bbox +* :ghpull:`27265`: DOC: reorganizing contributing docs to clean up toc, better separate topics +* :ghpull:`27517`: Best-legend-location microoptimization +* :ghpull:`27540`: Bump github/codeql-action from 2 to 3 +* :ghpull:`27520`: [Doc] Minor consistency changes and correction of Marker docs +* :ghpull:`27505`: Download Qhull source from Github, not Qhull servers, in meson build +* :ghpull:`27518`: Micro-optimizations related to list handling +* :ghpull:`27495`: Bump actions/stale from 8 to 9 +* :ghpull:`27523`: Changes for stale GHA v9 +* :ghpull:`27519`: [Doc] Improve/correct docs for 3D +* :ghpull:`27447`: TST: Compress some hist geometry tests +* :ghpull:`27513`: Fix docs and add tests for transform and deprecate ``BboxTransformToMaxOnly`` +* :ghpull:`27511`: TST: Add tests for Affine2D +* :ghpull:`27424`: Added Axes.stairs test in test_datetime.py +* :ghpull:`27267`: Fix/restore secondary axis support for Transform-type functions +* :ghpull:`27013`: Add test_contour under test_datetime.py +* :ghpull:`27497`: Clarify that set_axisbelow doesn't move grids below images. +* :ghpull:`27498`: Remove unnecessary del local variables at end of Gcf.destroy. +* :ghpull:`27466`: Add test_eventplot to test_datetime.py +* :ghpull:`25905`: Use annotate coordinate systems to simplify label_subplots. +* :ghpull:`27471`: Doc: visualizing_tests and ``triage_tests`` tools +* :ghpull:`27474`: Added smoke test for Axes.matshow to test_datetime.py +* :ghpull:`27470`: Fix test visualization tool for non-PNG files +* :ghpull:`27426`: DOC: normalizing histograms +* :ghpull:`27452`: Cleanup unit_cube-methods +* :ghpull:`27431`: Added test for Axes.bar_label +* :ghpull:`26962`: Remove backend 3.7-deprecated API +* :ghpull:`27410`: Add test_vlines to test_datetime.py +* :ghpull:`27425`: Added test_fill_betweenx in test_datetime.py +* :ghpull:`27449`: Remove test_quiverkey from test_datetime.py +* :ghpull:`27427`: MNT/TST: remove xcorr and acorr from test_datetime +* :ghpull:`27390`: Add test_bxp in test_datetime.py +* :ghpull:`27428`: Added test for broken_barh to test_datetime.py +* :ghpull:`27222`: [TST] Added test_annotate in test_datetime.py +* :ghpull:`27135`: Added smoke test for Axes.stem +* :ghpull:`27343`: Fix draggable annotations on subfigures. +* :ghpull:`27033`: Add test_bar in test_datetime +* :ghpull:`27423`: Add test for fill_between in test_datetime.py +* :ghpull:`27409`: Fix setting ``_selection_completed`` in ``SpanSelector`` when spanselector is initialised using ``extents`` +* :ghpull:`27440`: Fix get_path for 3d artists +* :ghpull:`27422`: TST: Cache available interactive backends +* :ghpull:`27401`: Add test_fill in test_datetime.py +* :ghpull:`27419`: DOC: Add AsinhScale to list of built-in scales +* :ghpull:`27417`: Switch pytest fixture from tmpdir to tmp_path +* :ghpull:`27172`: ENH: Change logging to warning when creating a legend with no labels +* :ghpull:`27405`: Check that xerr/yerr values are not None in errorbar +* :ghpull:`27392`: Remove test_spy from test_datetime.py +* :ghpull:`27331`: Added smoke test for Axes.barbs in test_datetime.py +* :ghpull:`27393`: MNT: Fix doc makefiles +* :ghpull:`27387`: Revert "MNT: add _version.py to .gitignore" +* :ghpull:`27347`: FIX: scale norm of collections when first array is set +* :ghpull:`27374`: MNT: add _version.py to .gitignore +* :ghpull:`19011`: Simplify tk tooltip setup. +* :ghpull:`27367`: Fix _find_fonts_by_props docstring +* :ghpull:`27359`: Fix build on PyPy +* :ghpull:`27362`: Implement SubFigure.remove. +* :ghpull:`27360`: Fix removal of colorbars on nested subgridspecs. +* :ghpull:`27211`: Add test_hlines to test_datetimes.py +* :ghpull:`27353`: Refactor AxisArtistHelpers +* :ghpull:`27357`: [DOC]: Update 3d axis limits what's new +* :ghpull:`26992`: Convert TkAgg utilities to pybind11 +* :ghpull:`27215`: Add ``@QtCore.Slot()`` decorations to ``NavigationToolbar2QT`` +* :ghpull:`26907`: Removal of deprecations for Contour +* :ghpull:`27285`: Factor out common parts of qt and macos interrupt handling. +* :ghpull:`27306`: Simplify GridSpec setup in make_axes_gridspec. +* :ghpull:`27313`: FIX: allow re-shown Qt windows to be re-destroyed +* :ghpull:`27184`: Use pybind11 for qhull wrapper +* :ghpull:`26794`: Use pybind11 in _c_internal_utils module +* :ghpull:`27300`: Remove idiosyncratic get_tick_iterator API. +* :ghpull:`27275`: MAINT: fix .yml in tag issue template +* :ghpull:`27288`: Use int.from_bytes instead of implementing the conversion ourselves. +* :ghpull:`27286`: Various cleanups +* :ghpull:`27279`: Tweak a few docstrings. +* :ghpull:`27256`: merge up v3.8.1 +* :ghpull:`27254`: Remove redundant axes_grid colorbar examples. +* :ghpull:`27251`: webagg: Don't resize canvas if WebSocket isn't connected +* :ghpull:`27236`: Tagging Example - Tags for multiple figs demo +* :ghpull:`27245`: MNT: be more careful in Qt backend that there is actually a Figure +* :ghpull:`27158`: First attempt for individual hatching styles for stackplot +* :ghpull:`26851`: Establish draft Tag glossary and Tagging guidelines +* :ghpull:`27083`: DOC: Add tags infrastructure for gallery examples +* :ghpull:`27204`: BLD: Use NumPy nightly wheels for non-release builds +* :ghpull:`27208`: Add test_axvline to test_datetime.py +* :ghpull:`26989`: MNT: print fontname in missing glyph warning +* :ghpull:`27177`: Add test_axhline in test_datetime.py +* :ghpull:`27164`: docs: adding explanation for color in ``set_facecolor`` +* :ghpull:`27175`: Deprecate mixing positional and keyword args for legend(handles, labels) +* :ghpull:`27199`: DOC: clean up links under table formatting docs +* :ghpull:`27185`: Added smoke tests for Axes.errorbar in test_datetime.py +* :ghpull:`27091`: Add test_step to test_datetime.py +* :ghpull:`27182`: Add example for plotting a bihistogram +* :ghpull:`27130`: added test_axvspan in test.datetime.py +* :ghpull:`27094`: MNT: move pytest.ini configs to .toml +* :ghpull:`27139`: added test_axhspan in test_datetime.py +* :ghpull:`27058`: DOC: concise dependency heading + small clarifications +* :ghpull:`27053`: Added info for getting compilation output from meson on autorebuild +* :ghpull:`26906`: Fix masking for Axes3D.plot() +* :ghpull:`27142`: Added smoke test for Axes.text in test_datetime.py +* :ghpull:`27024`: Add test_contourf in test_datetime.py +* :ghpull:`22347`: correctly treat pan/zoom events of overlapping axes +* :ghpull:`26900`: #26865 removing deprecations to axislines.py +* :ghpull:`26696`: DOC: Fix colLoc default +* :ghpull:`27064`: Close all plot windows of a blocking show() on Ctrl+C +* :ghpull:`26882`: Add scatter test for datetime units +* :ghpull:`27114`: add test_stackplot in test_datetime.py +* :ghpull:`27084`: Add test_barh to test_datetime.py +* :ghpull:`27110`: DOC: Move figure member sections one level down +* :ghpull:`27127`: BLD: use python3 for shebang consistent with pep-394 +* :ghpull:`27111`: BLD: Fix setting FreeType build type in extension +* :ghpull:`26921`: MNT: clarify path.sketch rcparam format + test validate_sketch +* :ghpull:`27109`: TST: Use importlib for subprocess tests +* :ghpull:`27119`: Update clabel comment. +* :ghpull:`27117`: Remove datetime test for axes.pie +* :ghpull:`27095`: Deprecate nth_coord parameter from FixedAxisArtistHelper.new_fixed_axis. +* :ghpull:`27066`: Tweak array_view to be more like pybind11 +* :ghpull:`27090`: Restore figaspect() API documentation +* :ghpull:`27074`: Issue #26990: Split the histogram image into two for each code block. +* :ghpull:`27086`: Rename py namespace to mpl in extension code +* :ghpull:`27082`: MAINT: Update environment.yml to match requirements files +* :ghpull:`27072`: Remove datetime test stubs for spectral methods/table +* :ghpull:`26830`: Update stix table with Unicode names +* :ghpull:`26969`: DOC: add units to user/explain [ci doc] +* :ghpull:`27028`: Added test_hist in test_datetime.py +* :ghpull:`26876`: issue: 26871 - Remove SimplePath class from patches.py +* :ghpull:`26875`: Fix Deprecation in patches.py +* :ghpull:`26890`: Removing deprecated api from patches +* :ghpull:`27037`: add test_plot_date in test_datetime.py +* :ghpull:`27012`: Bump required C++ standard to c++17 +* :ghpull:`27021`: Add a section to Highlight past winners for JDH plotting contest in docs +* :ghpull:`27004`: Warning if handles and labels have a len mismatch +* :ghpull:`24061`: #24050 No error was thrown even number of handles mismatched labels +* :ghpull:`26754`: DOC: separate and clarify axisartist default tables +* :ghpull:`27020`: CI: Update scientific-python/upload-nightly-action to 0.2.0 +* :ghpull:`26951`: Clarify that explicit ticklabels are used without further formatting. +* :ghpull:`26894`: Deprecate setting the timer interval while starting it. +* :ghpull:`13401`: New clear() method for Radio and Check buttons +* :ghpull:`23829`: Start transitioning to pyproject.toml +* :ghpull:`26621`: Port build system to Meson +* :ghpull:`26928`: [TYP] Add tool for running stubtest +* :ghpull:`26917`: Deprecate ContourLabeler.add_label_clabeltext. +* :ghpull:`26960`: Deprecate backend_ps.get_bbox_header, and split it for internal use. +* :ghpull:`26967`: Minor cleanups. +* :ghpull:`26909`: deprecated api tri +* :ghpull:`26946`: Inline Cursor._update into its sole caller. +* :ghpull:`26915`: DOC: Clarify description and add examples in colors.Normalize +* :ghpull:`26874`: Cleaned up the span_where class method from Polycollections. +* :ghpull:`26586`: Support standard formatters in axisartist. +* :ghpull:`26788`: Fix axh{line,span} on polar axes. +* :ghpull:`26935`: add tomli to rstcheck extras +* :ghpull:`26275`: Use pybind11 in image module +* :ghpull:`26887`: DOC: improve removal for julian dates [ci doc] +* :ghpull:`26929`: DOC: Fix removal doc for Animation attributes +* :ghpull:`26918`: 26865 Removed deprecations from quiver.py +* :ghpull:`26902`: Fixed deprecated APIs in lines.py +* :ghpull:`26903`: Simplify CheckButtons and RadioButtons click handler. +* :ghpull:`26899`: MNT: only account for Artists once in fig.get_tightbbox +* :ghpull:`26861`: QT/NavigationToolbar2: configure subplots dialog should be modal +* :ghpull:`26885`: Removed deprecated code from gridspec.py +* :ghpull:`26880`: Updated offsetbox.py +* :ghpull:`26910`: Removed the deprecated code from offsetbox.py +* :ghpull:`26905`: Add users/explain to default skip subdirs +* :ghpull:`26853`: Widgets: Remove deprecations and make arguments keyword only +* :ghpull:`26877`: Fixes deprecation in lines.py +* :ghpull:`26871`: Removed the deprecated code from ``axis.py`` +* :ghpull:`26872`: Deprecated code removed in animation.py +* :ghpull:`26859`: Add datetime testing skeleton +* :ghpull:`26848`: ci: Don't install recommended packages on Circle +* :ghpull:`26852`: Remove Julian date support +* :ghpull:`26801`: [MNT]: Cleanup ticklabel_format (style=) +* :ghpull:`26840`: Reduce redundant information in _process_plot_var_args. +* :ghpull:`26731`: Explicitly set foreground color to black in svg icons +* :ghpull:`26826`: [MNT] Move NUM_VERTICES from mplutils.h to the only file it is used in +* :ghpull:`26742`: [TYP] Add typing for some private methods and modules +* :ghpull:`26819`: Reorder safe_first_element() and _safe_first_finite() code +* :ghpull:`26813`: Bump docker/setup-qemu-action from 2 to 3 +* :ghpull:`26797`: Remove deprecated draw_gouraud_triangle +* :ghpull:`26815`: Remove plt.Axes from tests +* :ghpull:`26818`: Fix doc build (alternative) +* :ghpull:`26785`: merge up v3.8.0 +* :ghpull:`25272`: Do not add padding to 3D axis limits when limits are manually set +* :ghpull:`26798`: Remove deprecated methods and attributed in Axes3D +* :ghpull:`26744`: Use cbook methods for string checking +* :ghpull:`26802`: specify input range in logs when image data must be clipped +* :ghpull:`26787`: Remove unused Axis private init helpers. +* :ghpull:`26629`: DOC: organize figure API +* :ghpull:`26690`: Make generated pgf code more robust against later changes of tex engine. +* :ghpull:`26577`: Bugfix: data sanitizing for barh +* :ghpull:`26684`: Update PR template doc links +* :ghpull:`26686`: PR template: shorten comment and pull up top +* :ghpull:`26670`: Added sanitize_sequence to kwargs in _preprocess_data +* :ghpull:`26634`: [MNT] Move SubplotParams from figure to gridspec +* :ghpull:`26609`: Cleanup AutoMinorLocator implementation. +* :ghpull:`26293`: Added get_xmargin(), get_ymargin() and get_zmargin() and tests. +* :ghpull:`26516`: Replace reference to %pylab by %matplotlib. +* :ghpull:`26483`: Improve legend(loc='best') warning and test +* :ghpull:`26482`: [DOC]: print pydata sphinx/mpl theme versions +* :ghpull:`23787`: Use pybind11 for C/C++ extensions + +Issues (97): + +* :ghissue:`28202`: [Bug]: Qt test_ipython fails on older ipython +* :ghissue:`28145`: [TST] Upcoming dependency test failures +* :ghissue:`28034`: [TST] Upcoming dependency test failures +* :ghissue:`28168`: [TST] Upcoming dependency test failures +* :ghissue:`28040`: [Bug]: vertical_axis not respected when rotating plots interactively +* :ghissue:`28146`: [Bug]: Useless recursive group in SVG output when using path_effects +* :ghissue:`28067`: [Bug]: ``LinearSegmentedColormap.from_list`` does not have all type hints for argument ``colors`` +* :ghissue:`26778`: [MNT]: Numpy 2.0 support strategy +* :ghissue:`28020`: [Bug]: imsave fails on RGBA data when origin is set to lower +* :ghissue:`7720`: WXAgg backend not rendering nicely on retina +* :ghissue:`28069`: [Bug]: Can't save with custom toolbar +* :ghissue:`28005`: [Doc]: Improve contribute instructions +* :ghissue:`22376`: [ENH]: align_titles +* :ghissue:`5506`: Confusing status bar values in presence of multiple axes +* :ghissue:`4284`: Twin axis message coordinates +* :ghissue:`18940`: WxAgg backend draws the wrong size when wxpython app is high DPI aware on Windows +* :ghissue:`27792`: [ENH]: Legend entries for boxplot +* :ghissue:`27828`: [Bug]: ".C10" does not work as plot shorthand format spec +* :ghissue:`27911`: redirect not working for updated contribute page +* :ghissue:`21876`: [Doc]: redirect-from directive appears broken? +* :ghissue:`27941`: [Bug]: ShrinkA and ShrinkB are ignored in ax.annotate(arrowprops=...) +* :ghissue:`26477`: [ENH]: Add interpolation_stage selector for images in qt figureoptions +* :ghissue:`363`: Enable hatches for Cairo backend +* :ghissue:`27852`: [Bug]: matplotlib.pyplot.matshow "(first dimension of the array) are displayed horizontally" but are displayed vertically +* :ghissue:`27400`: [Bug]: tk backend confused by presence of file named "move" in current working directory +* :ghissue:`25882`: [Bug]: plt.hist takes significantly more time with torch and jax arrays +* :ghissue:`25204`: [Bug]: Pyparsing warnings emitted in mathtext +* :ghissue:`17707`: getpwuid(): uid not found: 99 +* :ghissue:`27896`: [Doc]: Empty "User guide tutorials page" in docs +* :ghissue:`27824`: [Bug]: polygon from axvspan not correct in polar plot after set_xy +* :ghissue:`27378`: [ENH]: Suggest 'CN' if color is an integer +* :ghissue:`27843`: [Bug]: close_group is not called when using patheffects +* :ghissue:`27839`: [Bug]: PathCollection using alpha ignores 'none' facecolors +* :ghissue:`25119`: [ENH]: secondary_x/yaxis accept transform argument +* :ghissue:`27876`: [Doc]: Fix version switcher in devdocs +* :ghissue:`27301`: [Bug]: ``imshow`` allows RGB(A) images with ``np.nan`` values to pass +* :ghissue:`23839`: [MNT]: Add tests to codify ``ax.clear`` +* :ghissue:`27652`: [Doc]: Low contrast on clicked links in dark mode +* :ghissue:`27865`: [Bug]: Zoom und pan not working after writing pdf pages. +* :ghissue:`25871`: [Bug]: Colorbar cannot be added to another figure +* :ghissue:`8072`: plot_date() ignores timezone in matplotlib version 2.0.0 +* :ghissue:`27812`: [ENH]: Add split feature for violin plots +* :ghissue:`27659`: [MNT]: Improve return type of ``ioff`` and ``ion`` to improve Pyright analysis of bound variables +* :ghissue:`27805`: [Bug]: Saving a figure with indicate_inset_zoom to pdf and then pickling it causes TypeError +* :ghissue:`27701`: [Bug]: axis set_xscale('log') interferes with set_xticks +* :ghissue:`19807`: radius modification in contains_point function when linewidth is specified +* :ghissue:`27762`: [Bug]: Inconsistent treatment of list of labels in ``plot`` when the input is a dataframe +* :ghissue:`27745`: [MNT]: ``_ImageBase.draw`` and ``Axis.draw`` args and kwargs +* :ghissue:`27782`: [Doc]: Link to citation page in read me broken +* :ghissue:`8789`: legend handle size does not automatically scale with linewidth +* :ghissue:`27746`: [Doc]: Citation link in the readme.md points to 404 +* :ghissue:`20853`: Add deprecation for colormaps +* :ghissue:`26865`: [MNT]: Remove 3.7-deprecated API +* :ghissue:`24168`: [Bug]: ``subprocess-exited-with-error`` when trying to build on M1 mac +* :ghissue:`27727`: [Doc]: Text in the colormap normalization gallery doesn't match the code +* :ghissue:`27635`: [Bug]: test_figure_leak_20490 repeatedly failing on CI +* :ghissue:`14217`: [Feature request] Add a way to update the position of the Arrow patch. +* :ghissue:`20512`: Bad boxplot legend entries +* :ghissue:`22011`: [Bug]: subfigures messes up with fig.legend zorder +* :ghissue:`27414`: [Bug]: Legend overlaps shaded area in fill_between with legend location "best" +* :ghissue:`23323`: Legend with "loc=best" does not try to avoid text +* :ghissue:`27648`: [Doc]: ``Axes.inset_axes`` is still experimental +* :ghissue:`27277`: [Doc]: Two license pages in docs +* :ghissue:`24648`: [Doc]: make html fail early if latex not present +* :ghissue:`27554`: [Bug]: Large image draw performance deterioration in recent releases +* :ghissue:`25239`: [Bug]: colors.PowerNorm results in incorrect colorbar +* :ghissue:`13533`: Boxplotting Masked Arrays +* :ghissue:`25967`: [Doc]: dollar_ticks example refers to unused formatter classes +* :ghissue:`24859`: [Doc]: Document color in a consistent way, including link +* :ghissue:`27159`: [Bug]: Meson build fails due to qhull link issue. +* :ghissue:`25691`: [Bug]: Secondary axis does not support Transform as functions +* :ghissue:`25860`: [Bug]: canvas pick events not working when Axes belongs to a subfigure +* :ghissue:`27361`: [Bug]: (Tight) layout engine breaks for 3D patches +* :ghissue:`27145`: [ENH]: Make "No artists with labels found to put in legend" a warning +* :ghissue:`27399`: [Bug]: None in y or yerr arrays leads to TypeError when using errorbar +* :ghissue:`13887`: Accessing default ``norm`` of a Collection removes its colors. +* :ghissue:`26593`: [ENH]: Support SubFigure.remove() +* :ghissue:`27329`: [Bug]: Removing a colorbar for an axes positioned in a subgridspec restores the axes' position to the wrong place. +* :ghissue:`27214`: [Bug]: ``NavigationToolbar2QT`` should use ``@Slot`` annotation +* :ghissue:`27146`: [ENH]: Multi hatching in ``ax.stackplot()`` +* :ghissue:`27168`: [Doc]: Instructions for editable installation on Windows potentially missing a step +* :ghissue:`27174`: [MNT]: Build nightly wheels with NumPy nightly wheels +* :ghissue:`25043`: [ENH]: Plotting masked arrays correctly in 3D line plot +* :ghissue:`26990`: [Doc]: Histogram path example renders poorly in HTML +* :ghissue:`25738`: [MNT]: Improve readability of _mathtext_data.stix_virtual_fonts table +* :ghissue:`11129`: Highlight past winners for JDH plotting contest in docs +* :ghissue:`24050`: No error message in matplotlib.axes.Axes.legend() if there are more labels than handles +* :ghissue:`10922`: ENH: clear() method for widgets.RadioButtons +* :ghissue:`18295`: How to modify ticklabels in axisartist? +* :ghissue:`24996`: [Bug]: for non-rectilinear axes, axvline/axhline should behave as "draw a gridline at that x/y" +* :ghissue:`26841`: [Bug]: Global legend weird behaviors +* :ghissue:`25974`: [MNT]: Cleanup ticklabel_format(..., style=) +* :ghissue:`26786`: Please upload new dev wheel so we pick up 3.9.dev after 3.8 release +* :ghissue:`18052`: the limits of axes are inexact with mplot3d +* :ghissue:`25596`: [MNT]: Consistency on Interface +* :ghissue:`26557`: [ENH]: Nightly Python 3.12 builds +* :ghissue:`26281`: [ENH]: Add get_xmargin, get_ymargin, get_zmargin axes methods diff --git a/doc/users/prev_whats_new/github_stats_3.9.1.rst b/doc/users/prev_whats_new/github_stats_3.9.1.rst new file mode 100644 index 000000000000..1bd7860546cb --- /dev/null +++ b/doc/users/prev_whats_new/github_stats_3.9.1.rst @@ -0,0 +1,192 @@ +.. _github-stats-3-9-1: + +GitHub statistics for 3.9.1 (Jul 04, 2024) +========================================== + +GitHub statistics for 2024/05/15 (tag: v3.9.0) - 2024/07/04 + +These lists are automatically generated, and may be incomplete or contain duplicates. + +We closed 30 issues and merged 111 pull requests. +The full list can be seen `on GitHub `__ + +The following 29 authors contributed 184 commits. + +* Antony Lee +* Brigitta Sipőcz +* Christian Mattsson +* dale +* dependabot[bot] +* Elliott Sales de Andrade +* Eytan Adler +* Greg Lucas +* haaris +* hannah +* Ian Thomas +* Illviljan +* K900 +* Kyle Sunden +* Lumberbot (aka Jack) +* malhar2460 +* Matthew Feickert +* Melissa Weber Mendonça +* MischaMegens2 +* Oscar Gustafsson +* Ruth Comer +* Scott Shambaugh +* simond07 +* SjoerdB93 +* Takumasa N +* Takumasa N. +* Takumasa Nakamura +* Thomas A Caswell +* Tim Hoffmann + +GitHub issues and pull requests: + +Pull Requests (111): + +* :ghpull:`28507`: Backport PR #28430 on branch v3.9.x (Fix pickling of AxesWidgets.) +* :ghpull:`28506`: Backport PR #28451 on branch v3.9.x (Fix GTK cairo backends) +* :ghpull:`28430`: Fix pickling of AxesWidgets. +* :ghpull:`25861`: Fix Hidpi scaling for GTK4Cairo +* :ghpull:`28451`: Fix GTK cairo backends +* :ghpull:`28499`: Backport PR #28498 on branch v3.9.x (Don't fail if we can't query system fonts on macOS) +* :ghpull:`28498`: Don't fail if we can't query system fonts on macOS +* :ghpull:`28491`: Backport PR #28487 on branch v3.9.x (Fix autoscaling with axhspan) +* :ghpull:`28490`: Backport PR #28486 on branch v3.9.x (Fix CompositeGenericTransform.contains_branch_seperately) +* :ghpull:`28487`: Fix autoscaling with axhspan +* :ghpull:`28486`: Fix CompositeGenericTransform.contains_branch_seperately +* :ghpull:`28483`: Backport PR #28393 on branch v3.9.x (Make sticky edges only apply if the sticky edge is the most extreme limit point) +* :ghpull:`28482`: Backport PR #28473 on branch v3.9.x (Do not lowercase module:// backends) +* :ghpull:`28393`: Make sticky edges only apply if the sticky edge is the most extreme limit point +* :ghpull:`28473`: Do not lowercase module:// backends +* :ghpull:`28480`: Backport PR #28474 on branch v3.9.x (Fix typing and docs for containers) +* :ghpull:`28479`: Backport PR #28397 (FIX: stale root Figure when adding/updating subfigures) +* :ghpull:`28474`: Fix typing and docs for containers +* :ghpull:`28472`: Backport PR #28289 on branch v3.9.x (Promote mpltype Sphinx role to a public extension) +* :ghpull:`28471`: Backport PR #28342 on branch v3.9.x (DOC: Document the parameter *position* of apply_aspect() as internal) +* :ghpull:`28470`: Backport PR #28398 on branch v3.9.x (Add GIL Release to flush_events in macosx backend) +* :ghpull:`28469`: Backport PR #28355 on branch v3.9.x (MNT: Re-add matplotlib.cm.get_cmap) +* :ghpull:`28397`: FIX: stale root Figure when adding/updating subfigures +* :ghpull:`28289`: Promote mpltype Sphinx role to a public extension +* :ghpull:`28342`: DOC: Document the parameter *position* of apply_aspect() as internal +* :ghpull:`28398`: Add GIL Release to flush_events in macosx backend +* :ghpull:`28355`: MNT: Re-add matplotlib.cm.get_cmap +* :ghpull:`28468`: Backport PR #28465 on branch v3.9.x (Fix pickling of SubFigures) +* :ghpull:`28465`: Fix pickling of SubFigures +* :ghpull:`28462`: Backport PR #28440 on branch v3.9.x (DOC: Add note about simplification of to_polygons) +* :ghpull:`28460`: Backport PR #28459 on branch v3.9.x (DOC: Document kwargs scope for tick setter functions) +* :ghpull:`28461`: Backport PR #28458 on branch v3.9.x (Correct numpy dtype comparisons in image_resample) +* :ghpull:`28440`: DOC: Add note about simplification of to_polygons +* :ghpull:`28458`: Correct numpy dtype comparisons in image_resample +* :ghpull:`28459`: DOC: Document kwargs scope for tick setter functions +* :ghpull:`28450`: Backport of 28371 and 28411 +* :ghpull:`28446`: Backport PR #28403 on branch v3.9.x (FIX: Autoscale support in add_collection3d for Line3DCollection and Poly3DCollection +* :ghpull:`28445`: Backport PR #28403 on branch v3.9.x (FIX: Autoscale support in add_collection3d for Line3DCollection and Poly3DCollection) +* :ghpull:`28438`: Backport PR #28436 on branch v3.9.x (Fix ``is_color_like`` for 2-tuple of strings and fix ``to_rgba`` for ``(nth_color, alpha)``) +* :ghpull:`28403`: FIX: Autoscale support in add_collection3d for Line3DCollection and Poly3DCollection +* :ghpull:`28443`: Backport PR #28441 on branch v3.9.x (MNT: Update basic units example to work with numpy 2.0) +* :ghpull:`28441`: MNT: Update basic units example to work with numpy 2.0 +* :ghpull:`28436`: Fix ``is_color_like`` for 2-tuple of strings and fix ``to_rgba`` for ``(nth_color, alpha)`` +* :ghpull:`28426`: Backport PR #28425 on branch v3.9.x (Fix Circle yaml line length) +* :ghpull:`28427`: Fix circleci yaml +* :ghpull:`28425`: Fix Circle yaml line length +* :ghpull:`28422`: Backport PR #28401 on branch v3.9.x (FIX: Fix text wrapping) +* :ghpull:`28424`: Backport PR #28423 on branch v3.9.x (Update return type for Axes.axhspan and Axes.axvspan) +* :ghpull:`28423`: Update return type for Axes.axhspan and Axes.axvspan +* :ghpull:`28401`: FIX: Fix text wrapping +* :ghpull:`28419`: Backport PR #28414 on branch v3.9.x (Clean up obsolete widget code) +* :ghpull:`28411`: Bump the actions group with 3 updates +* :ghpull:`28414`: Clean up obsolete widget code +* :ghpull:`28415`: Backport PR #28413 on branch v3.9.x (CI: update action that got moved org) +* :ghpull:`28413`: CI: update action that got moved org +* :ghpull:`28392`: Backport PR #28388 on branch v3.9.x (Allow duplicate (name, value) entry points for backends) +* :ghpull:`28362`: Backport PR #28337 on branch v3.9.x (Bump the actions group across 1 directory with 3 updates) +* :ghpull:`28388`: Allow duplicate (name, value) entry points for backends +* :ghpull:`28389`: Backport PR #28380 on branch v3.9.x (Remove outdated docstring section in RendererBase.draw_text.) +* :ghpull:`28380`: Remove outdated docstring section in RendererBase.draw_text. +* :ghpull:`28385`: Backport PR #28377 on branch v3.9.x (DOC: Clarify scope of wrap.) +* :ghpull:`28377`: DOC: Clarify scope of wrap. +* :ghpull:`28368`: Backport PR #28359 on branch v3.9.x (Document that axes unsharing is impossible.) +* :ghpull:`28359`: Document that axes unsharing is impossible. +* :ghpull:`28337`: Bump the actions group across 1 directory with 3 updates +* :ghpull:`28351`: Backport PR #28307 on branch v3.9.x (DOC: New color line by value example) +* :ghpull:`28307`: DOC: New color line by value example +* :ghpull:`28339`: Backport PR #28336 on branch v3.9.x (DOC: Add version warning banner for docs versions different from stable) +* :ghpull:`28336`: DOC: Add version warning banner for docs versions different from stable +* :ghpull:`28334`: Backport PR #28332 on branch v3.9.x (Call IPython.enable_gui when install repl displayhook) +* :ghpull:`28332`: Call IPython.enable_gui when install repl displayhook +* :ghpull:`28331`: Backport PR #28329 on branch v3.9.x (DOC: Add example for 3D intersecting planes) +* :ghpull:`28329`: DOC: Add example for 3D intersecting planes +* :ghpull:`28327`: Backport PR #28292 on branch v3.9.x (Resolve MaxNLocator IndexError when no large steps) +* :ghpull:`28292`: Resolve MaxNLocator IndexError when no large steps +* :ghpull:`28326`: Backport PR #28041 on branch v3.9.x ([BUG]: Shift box_aspect according to vertical_axis) +* :ghpull:`28041`: [BUG]: Shift box_aspect according to vertical_axis +* :ghpull:`28320`: Backport PR #27001 on branch v3.9.x ([TYP] Add overload of ``pyplot.subplots``) +* :ghpull:`27001`: [TYP] Add overload of ``pyplot.subplots`` +* :ghpull:`28318`: Backport PR #28273 on branch v3.9.x (CI: Add GitHub artifact attestations to package distribution) +* :ghpull:`28273`: CI: Add GitHub artifact attestations to package distribution +* :ghpull:`28305`: Backport PR #28303 on branch v3.9.x (Removed drawedges repeated definition from function doc string) +* :ghpull:`28303`: Removed drawedges repeated definition from function doc string +* :ghpull:`28299`: Backport PR #28297 on branch v3.9.x (Solved #28296 Added missing comma) +* :ghpull:`28297`: Solved #28296 Added missing comma +* :ghpull:`28294`: Backport PR #28261 on branch v3.9.x (Correct roll angle units, issue #28256) +* :ghpull:`28261`: Correct roll angle units, issue #28256 +* :ghpull:`28283`: Backport PR #28280 on branch v3.9.x (DOC: Add an example for 2D images in 3D plots) +* :ghpull:`28280`: DOC: Add an example for 2D images in 3D plots +* :ghpull:`28278`: Backport PR #28272 on branch v3.9.x (BLD: Move macos builders from 11 to 12) +* :ghpull:`28277`: Backport PR #28274 on branch v3.9.x (ci: Remove deprecated codeql option) +* :ghpull:`28272`: BLD: Move macos builders from 11 to 12 +* :ghpull:`28274`: ci: Remove deprecated codeql option +* :ghpull:`28270`: Backport PR #28269 on branch v3.9.x (Handle GetForegroundWindow() returning NULL.) +* :ghpull:`28269`: Handle GetForegroundWindow() returning NULL. +* :ghpull:`28266`: Backport PR #28257 on branch v3.9.x (Clean up some Meson-related leftovers) +* :ghpull:`28257`: Clean up some Meson-related leftovers +* :ghpull:`28255`: Backport PR #28254 on branch v3.9.x ([DOC] plot type heading consistency) +* :ghpull:`28254`: [DOC] plot type heading consistency +* :ghpull:`28253`: Backport PR #28252 on branch v3.9.x (DOC: Flip the imshow plot types example to match the other examples) +* :ghpull:`28252`: DOC: Flip the imshow plot types example to match the other examples +* :ghpull:`28247`: Backport PR #28230 on branch v3.9.x (Add extra imports to improve typing) +* :ghpull:`28230`: Add extra imports to improve typing +* :ghpull:`28246`: Backport PR #28243 on branch v3.9.x (DOC: Add more 3D plot types) +* :ghpull:`28243`: DOC: Add more 3D plot types +* :ghpull:`28241`: Backport PR #28219 on branch v3.9.x (Bump the actions group with 2 updates) +* :ghpull:`28219`: Bump the actions group with 2 updates +* :ghpull:`28237`: Backport PR #28233 on branch v3.9.x (CI: Fix font install on macOS/Homebrew) +* :ghpull:`28236`: Backport PR #28231 on branch v3.9.x (DOC: we do not need the blit call in on_draw) +* :ghpull:`28233`: CI: Fix font install on macOS/Homebrew +* :ghpull:`28231`: DOC: we do not need the blit call in on_draw + +Issues (30): + +* :ghissue:`22482`: [ENH]: pickle (or save) matplotlib figure with insteractive slider +* :ghissue:`25847`: [Bug]: Graph gets cut off with scaled resolution using gtk4cairo backend +* :ghissue:`28341`: [Bug]: Incorrect X-axis scaling with date values +* :ghissue:`28383`: [Bug]: axvspan no longer participating in limit calculations +* :ghissue:`28223`: [Bug]: Inconsistent Visualization of Intervals in ax.barh for Different Duration Widths +* :ghissue:`28432`: [Bug]: Backend name specified as module gets lowercased since 3.9 +* :ghissue:`28467`: [Bug]: Incorrect type stub for ``ErrorbarContainer``'s ``lines`` attribute. +* :ghissue:`28384`: [Bug]: subfigure artists not drawn interactively +* :ghissue:`28234`: [Bug]: mpltype custom role breaks sphinx build for third-party projects that have intersphinx links to matplotlib +* :ghissue:`28464`: [Bug]: figure with subfigures cannot be pickled +* :ghissue:`28448`: [Bug]: Making an RGB image from pickled data throws error +* :ghissue:`23317`: [Bug]: ``add_collection3d`` does not update view limits +* :ghissue:`17130`: autoscale_view is not working with Line3DCollection +* :ghissue:`28434`: [Bug]: Setting exactly 2 colors with tuple in ``plot`` method gives confusing error +* :ghissue:`28417`: [Doc]: axhspan and axvspan now return Rectangles, not Polygons. +* :ghissue:`28378`: [ENH]: Switch text wrapping boundary to subfigure +* :ghissue:`28404`: [Doc]: matplotlib.widgets.CheckButtons no longer has .rectangles attribute, needs removed. +* :ghissue:`28367`: [Bug]: Backend entry points can be erroneously duplicated +* :ghissue:`28358`: [Bug]: Labels don't get wrapped when set_yticks() is used in subplots +* :ghissue:`28374`: [Bug]: rcParam ``tk.window_focus: True`` is causes crash on Linux in version 3.9.0. +* :ghissue:`28324`: [Bug]: show(block=False) freezes +* :ghissue:`28239`: [Doc]: Gallery example showing 3D slice planes +* :ghissue:`27603`: [Bug]: _raw_ticker() istep +* :ghissue:`24328`: [Bug]: class Axes3D.set_box_aspect() sets wrong aspect ratios when Axes3D.view_init( vertical_axis='y') is enabled. +* :ghissue:`28221`: [Doc]: drawedges attribute described twice in matplotlib.colorbar documentation +* :ghissue:`28296`: [Doc]: Missing comma +* :ghissue:`28256`: [Bug]: axes3d.py's _on_move() converts the roll angle to radians, but then passes it to view_init() as if it were still in degrees +* :ghissue:`28267`: [Bug]: for Python 3.11.9 gor ValueError: PyCapsule_New called with null pointer +* :ghissue:`28022`: [Bug]: Type of Axes is unknown pyright +* :ghissue:`28002`: Segfault from path editor example with QtAgg diff --git a/doc/users/prev_whats_new/whats_new_1.2.rst b/doc/users/prev_whats_new/whats_new_1.2.rst index 260941d1ca9e..43c729999d5b 100644 --- a/doc/users/prev_whats_new/whats_new_1.2.rst +++ b/doc/users/prev_whats_new/whats_new_1.2.rst @@ -86,7 +86,7 @@ minimum and maximum colorbar extensions. Z = np.cos(X) * np.sin(0.5*Y) clevs = [-.75, -.5, -.25, 0., .25, .5, .75] - cmap = plt.cm.get_cmap(name='jet', lut=8) + cmap = plt.get_cmap(name='jet', lut=8) ax1 = plt.subplot(211) cs1 = plt.contourf(x, y, Z, clevs, cmap=cmap, extend='both') diff --git a/doc/users/prev_whats_new/whats_new_1.3.rst b/doc/users/prev_whats_new/whats_new_1.3.rst index 855235069917..10811632c5c4 100644 --- a/doc/users/prev_whats_new/whats_new_1.3.rst +++ b/doc/users/prev_whats_new/whats_new_1.3.rst @@ -13,7 +13,7 @@ What's new in Matplotlib 1.3 (Aug 01, 2013) New in 1.3.1 ------------ -1.3.1 is a bugfix release, primarily dealing with improved setup and +1.3.1 is a micro release, primarily dealing with improved setup and handling of dependencies, and correcting and enhancing the documentation. diff --git a/doc/users/prev_whats_new/whats_new_1.4.rst b/doc/users/prev_whats_new/whats_new_1.4.rst index 39eefa81b168..eb0e93fd8883 100644 --- a/doc/users/prev_whats_new/whats_new_1.4.rst +++ b/doc/users/prev_whats_new/whats_new_1.4.rst @@ -221,7 +221,7 @@ Added size related functions to specialized `.Collection`\s Added the ``get_size`` and ``set_size`` functions to control the size of elements of specialized collections ( :class:`~matplotlib.collections.AsteriskPolygonCollection` -:class:`~matplotlib.collections.BrokenBarHCollection` +``matplotlib.collections.BrokenBarHCollection`` :class:`~matplotlib.collections.CircleCollection` :class:`~matplotlib.collections.PathCollection` :class:`~matplotlib.collections.PolyCollection` diff --git a/doc/users/prev_whats_new/whats_new_1.5.rst b/doc/users/prev_whats_new/whats_new_1.5.rst index 5ff5b0a97bbd..dd8e204aa957 100644 --- a/doc/users/prev_whats_new/whats_new_1.5.rst +++ b/doc/users/prev_whats_new/whats_new_1.5.rst @@ -517,7 +517,8 @@ also prevents unsafe usage by strictly defining the parameters that a user can set. To use, call ``set_params()`` on a `.Locator` instance with desired arguments: -:: + +.. code-block:: python loc = matplotlib.ticker.LogLocator() # Set given attributes for loc. diff --git a/doc/users/prev_whats_new/whats_new_3.0.rst b/doc/users/prev_whats_new/whats_new_3.0.rst index 2f268eaf0058..e3dd12c71a8e 100644 --- a/doc/users/prev_whats_new/whats_new_3.0.rst +++ b/doc/users/prev_whats_new/whats_new_3.0.rst @@ -147,7 +147,7 @@ Add ``ax.get_gridspec`` to ``SubplotBase`` New method ``SubplotBase.get_gridspec`` is added so that users can easily get the gridspec that went into making an axes: -.. code:: +.. code-block:: python import matplotlib.pyplot as plt diff --git a/doc/users/prev_whats_new/whats_new_3.4.0.rst b/doc/users/prev_whats_new/whats_new_3.4.0.rst index f90a95c7f126..003cd85fa49d 100644 --- a/doc/users/prev_whats_new/whats_new_3.4.0.rst +++ b/doc/users/prev_whats_new/whats_new_3.4.0.rst @@ -494,7 +494,7 @@ display an image of the colormap. .. only:: html - .. code-block:: + .. code-block:: ipython In[1]: cmap = plt.get_cmap('viridis').with_extremes(bad='r', under='g', over='b') @@ -547,7 +547,7 @@ for out-of-range and masked values. New ``cm.unregister_cmap`` function ----------------------------------- -`.cm.unregister_cmap` allows users to remove a colormap that they have +``matplotlib.cm.unregister_cmap`` allows users to remove a colormap that they have previously registered. New ``CenteredNorm`` for symmetrical data around a center @@ -634,8 +634,8 @@ supxlabel and supylabel ----------------------- It is possible to add x- and y-labels to a whole figure, analogous to -`.FigureBase.suptitle` using the new `.FigureBase.supxlabel` and -`.FigureBase.supylabel` methods. +`.Figure.suptitle` using the new `.Figure.supxlabel` and +`.Figure.supylabel` methods. .. plot:: diff --git a/doc/users/prev_whats_new/whats_new_3.5.0.rst b/doc/users/prev_whats_new/whats_new_3.5.0.rst index 5e69ab6be926..e67573702218 100644 --- a/doc/users/prev_whats_new/whats_new_3.5.0.rst +++ b/doc/users/prev_whats_new/whats_new_3.5.0.rst @@ -148,9 +148,9 @@ To register new colormaps use:: plt.colormaps.register(my_colormap) -We recommend to use the new API instead of the `~.cm.get_cmap` and -`~.cm.register_cmap` functions for new code. `matplotlib.cm.get_cmap` and -`matplotlib.cm.register_cmap` will eventually be deprecated and removed. +We recommend to use the new API instead of the ``matplotlib.cm.get_cmap`` and +``matplotlib.cm.register_cmap`` functions for new code. ``matplotlib.cm.get_cmap`` and +``matplotlib.cm.register_cmap`` will eventually be deprecated and removed. Within `.pyplot`, ``plt.get_cmap()`` and ``plt.register_cmap()`` will continue to be supported for backward compatibility. @@ -284,7 +284,7 @@ Simplifying the font setting for usetex mode Now the :rc:`font.family` accepts some font names as value for a more user-friendly setup. -.. code-block:: +.. code-block:: python plt.rcParams.update({ "text.usetex": True, @@ -561,7 +561,7 @@ to block callback signals from being processed by the ``CallbackRegistry``. The optional keyword, *signal*, can be used to block a specific signal from being processed and let all other signals pass. -.. code-block:: +.. code-block:: python import matplotlib.pyplot as plt diff --git a/doc/users/prev_whats_new/whats_new_3.8.0.rst b/doc/users/prev_whats_new/whats_new_3.8.0.rst index b3dc766eca75..8c34252098db 100644 --- a/doc/users/prev_whats_new/whats_new_3.8.0.rst +++ b/doc/users/prev_whats_new/whats_new_3.8.0.rst @@ -32,7 +32,7 @@ When *antialiased* is set to ``False``, antialiasing will not be applied to the When *antialiased* is not specified, antialiasing will be set by :rc:`text.antialiased` at the creation time of ``Text`` and ``Annotation`` object. Examples: -.. code-block:: +.. code-block:: python mpl.text.Text(.5, .5, "foo\nbar", antialiased=True) plt.text(0.5, 0.5, '6 inches x 2 inches', antialiased=True) @@ -41,7 +41,7 @@ Examples: If the text contains math expression, *antialiased* applies to the whole text. Examples: -.. code-block:: +.. code-block:: python # no part will be antialiased for the text below plt.text(0.5, 0.25, r"$I'm \sqrt{x}$", antialiased=False) @@ -339,7 +339,7 @@ Supports using the ``\boldsymbol{}`` command in mathtext: To change symbols to bold enclose the text in a font command as shown: -.. code-block:: +.. code-block:: none r'$\boldsymbol{a+2+\alpha}$' @@ -374,7 +374,7 @@ means that (almost) all supported symbols, operators etc are shown at :ref:`math To use it to enclose the math in a substack command as shown: -.. code-block:: +.. code-block:: none r'$\sum_{\substack{1\leq i\leq 3\\ 1\leq j\leq 5}}$' @@ -393,7 +393,7 @@ The ``\middle`` delimiter has been added, and can now be used with the To use the middle command enclose it in between the ``\left`` and ``\right`` delimiter command as shown: -.. code-block:: +.. code-block:: none r'$\left( \frac{a}{b} \middle| q \right)$' @@ -438,7 +438,7 @@ Supports use of bold-italic font style in mathtext using the ``\mathbfit{}`` com To change font to bold and italic enclose the text in a font command as shown: -.. code-block:: +.. code-block:: none r'$\mathbfit{\eta \leq C(\delta(\eta))}$ diff --git a/doc/users/prev_whats_new/whats_new_3.9.0.rst b/doc/users/prev_whats_new/whats_new_3.9.0.rst new file mode 100644 index 000000000000..e0190cca3f27 --- /dev/null +++ b/doc/users/prev_whats_new/whats_new_3.9.0.rst @@ -0,0 +1,409 @@ +============================================= +What's new in Matplotlib 3.9.0 (May 15, 2024) +============================================= + +For a list of all of the issues and pull requests since the last revision, see the +:ref:`github-stats`. + +.. contents:: Table of Contents + :depth: 4 + +.. toctree:: + :maxdepth: 4 + +Plotting and Annotation improvements +==================================== + +``Axes.inset_axes`` is no longer experimental +--------------------------------------------- + +`.Axes.inset_axes` is considered stable for use. + +Legend support for Boxplot +-------------------------- + +Boxplots now support a *label* parameter to create legend entries. Legend labels can be +passed as a list of strings to label multiple boxes in a single `.Axes.boxplot` call: + +.. plot:: + :include-source: + :alt: Example of creating 3 boxplots and assigning legend labels as a sequence. + + np.random.seed(19680801) + fruit_weights = [ + np.random.normal(130, 10, size=100), + np.random.normal(125, 20, size=100), + np.random.normal(120, 30, size=100), + ] + labels = ['peaches', 'oranges', 'tomatoes'] + colors = ['peachpuff', 'orange', 'tomato'] + + fig, ax = plt.subplots() + ax.set_ylabel('fruit weight (g)') + + bplot = ax.boxplot(fruit_weights, + patch_artist=True, # fill with color + label=labels) + + # fill with colors + for patch, color in zip(bplot['boxes'], colors): + patch.set_facecolor(color) + + ax.set_xticks([]) + ax.legend() + + +Or as a single string to each individual `.Axes.boxplot`: + +.. plot:: + :include-source: + :alt: Example of creating 2 boxplots and assigning each legend label as a string. + + fig, ax = plt.subplots() + + data_A = np.random.random((100, 3)) + data_B = np.random.random((100, 3)) + 0.2 + pos = np.arange(3) + + ax.boxplot(data_A, positions=pos - 0.2, patch_artist=True, label='Box A', + boxprops={'facecolor': 'steelblue'}) + ax.boxplot(data_B, positions=pos + 0.2, patch_artist=True, label='Box B', + boxprops={'facecolor': 'lightblue'}) + + ax.legend() + +Percent sign in pie labels auto-escaped with ``usetex=True`` +------------------------------------------------------------ + +It is common, with `.Axes.pie`, to specify labels that include a percent sign (``%``), +which denotes a comment for LaTeX. When enabling LaTeX with :rc:`text.usetex` or passing +``textprops={"usetex": True}``, this used to cause the percent sign to disappear. + +Now, the percent sign is automatically escaped (by adding a preceding backslash) so that +it appears regardless of the ``usetex`` setting. If you have pre-escaped the percent +sign, this will be detected, and remain as is. + +``hatch`` parameter for stackplot +--------------------------------- + +The `~.Axes.stackplot` *hatch* parameter now accepts a list of strings describing +hatching styles that will be applied sequentially to the layers in the stack: + +.. plot:: + :include-source: + :alt: Two charts, identified as ax1 and ax2, showing "stackplots", i.e. one-dimensional distributions of data stacked on top of one another. The first plot, ax1 has cross-hatching on all slices, having been given a single string as the "hatch" argument. The second plot, ax2 has different styles of hatching on each slice - diagonal hatching in opposite directions on the first two slices, cross-hatching on the third slice, and open circles on the fourth. + + fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(10,5)) + + cols = 10 + rows = 4 + data = ( + np.reshape(np.arange(0, cols, 1), (1, -1)) ** 2 + + np.reshape(np.arange(0, rows), (-1, 1)) + + np.random.random((rows, cols))*5 + ) + x = range(data.shape[1]) + ax1.stackplot(x, data, hatch="x") + ax2.stackplot(x, data, hatch=["//","\\","x","o"]) + + ax1.set_title("hatch='x'") + ax2.set_title("hatch=['//','\\\\','x','o']") + + plt.show() + +Add option to plot only one half of violin plot +----------------------------------------------- + +Setting the parameter *side* to 'low' or 'high' allows to only plot one half of the +`.Axes.violinplot`. + +.. plot:: + :include-source: + :alt: Three copies of a vertical violin plot; first in blue showing the default of both sides, followed by an orange copy that only shows the "low" (or left, in this case) side, and finally a green copy that only shows the "high" (or right) side. + + # Fake data with reproducible random state. + np.random.seed(19680801) + data = np.random.normal(0, 8, size=100) + + fig, ax = plt.subplots() + + ax.violinplot(data, [0], showmeans=True, showextrema=True) + ax.violinplot(data, [1], showmeans=True, showextrema=True, side='low') + ax.violinplot(data, [2], showmeans=True, showextrema=True, side='high') + + ax.set_title('Violin Sides Example') + ax.set_xticks([0, 1, 2], ['Default', 'side="low"', 'side="high"']) + ax.set_yticklabels([]) + +``axhline`` and ``axhspan`` on polar axes +----------------------------------------- + +... now draw circles and circular arcs (`~.Axes.axhline`) or annuli and wedges +(`~.Axes.axhspan`). + +.. plot:: + :include-source: + :alt: A sample polar plot, that contains an axhline at radius 1, an axhspan annulus between radius 0.8 and 0.9, and an axhspan wedge between radius 0.6 and 0.7 and 288° and 324°. + + fig = plt.figure() + ax = fig.add_subplot(projection="polar") + ax.set_rlim(0, 1.2) + + ax.axhline(1, c="C0", alpha=.5) + ax.axhspan(.8, .9, fc="C1", alpha=.5) + ax.axhspan(.6, .7, .8, .9, fc="C2", alpha=.5) + +Subplot titles can now be automatically aligned +----------------------------------------------- + +Subplot axes titles can be misaligned vertically if tick labels or xlabels are placed at +the top of one subplot. The new `~.Figure.align_titles` method on the `.Figure` class +will now align the titles vertically. + +.. plot:: + :include-source: + :alt: A figure with two Axes side-by-side, the second of which with ticks on top. The Axes titles and x-labels appear unaligned with each other due to these ticks. + + fig, axs = plt.subplots(1, 2, layout='constrained') + + axs[0].plot(np.arange(0, 1e6, 1000)) + axs[0].set_title('Title 0') + axs[0].set_xlabel('XLabel 0') + + axs[1].plot(np.arange(1, 0, -0.1) * 2000, np.arange(1, 0, -0.1)) + axs[1].set_title('Title 1') + axs[1].set_xlabel('XLabel 1') + axs[1].xaxis.tick_top() + axs[1].tick_params(axis='x', rotation=55) + +.. plot:: + :include-source: + :alt: A figure with two Axes side-by-side, the second of which with ticks on top. Unlike the previous figure, the Axes titles and x-labels appear aligned. + + fig, axs = plt.subplots(1, 2, layout='constrained') + + axs[0].plot(np.arange(0, 1e6, 1000)) + axs[0].set_title('Title 0') + axs[0].set_xlabel('XLabel 0') + + axs[1].plot(np.arange(1, 0, -0.1) * 2000, np.arange(1, 0, -0.1)) + axs[1].set_title('Title 1') + axs[1].set_xlabel('XLabel 1') + axs[1].xaxis.tick_top() + axs[1].tick_params(axis='x', rotation=55) + + fig.align_labels() + fig.align_titles() + +``axisartist`` can now be used together with standard ``Formatters`` +-------------------------------------------------------------------- + +... instead of being limited to axisartist-specific ones. + +Toggle minorticks on Axis +------------------------- + +Minor ticks on an `~matplotlib.axis.Axis` can be displayed or removed using +`~matplotlib.axis.Axis.minorticks_on` and `~matplotlib.axis.Axis.minorticks_off`; e.g., +``ax.xaxis.minorticks_on()``. See also `~matplotlib.axes.Axes.minorticks_on`. + +``StrMethodFormatter`` now respects ``axes.unicode_minus`` +---------------------------------------------------------- + +When formatting negative values, `.StrMethodFormatter` will now use unicode minus signs +if :rc:`axes.unicode_minus` is set. + + >>> from matplotlib.ticker import StrMethodFormatter + >>> with plt.rc_context({'axes.unicode_minus': False}): + ... formatter = StrMethodFormatter('{x}') + ... print(formatter.format_data(-10)) + -10 + + >>> with plt.rc_context({'axes.unicode_minus': True}): + ... formatter = StrMethodFormatter('{x}') + ... print(formatter.format_data(-10)) + −10 + +Figure, Axes, and Legend Layout +=============================== + +Subfigures now have controllable zorders +---------------------------------------- + +Previously, setting the zorder of a subfigure had no effect, and those were plotted on +top of any figure-level artists (i.e for example on top of fig-level legends). Now, +subfigures behave like any other artists, and their zorder can be controlled, with +default a zorder of 0. + +.. plot:: + :include-source: + :alt: Example on controlling the zorder of a subfigure + + x = np.linspace(1, 10, 10) + y1, y2 = x, -x + fig = plt.figure(constrained_layout=True) + subfigs = fig.subfigures(nrows=1, ncols=2) + for subfig in subfigs: + axarr = subfig.subplots(2, 1) + for ax in axarr.flatten(): + (l1,) = ax.plot(x, y1, label="line1") + (l2,) = ax.plot(x, y2, label="line2") + subfigs[0].set_zorder(6) + l = fig.legend(handles=[l1, l2], loc="upper center", ncol=2) + +Getters for xmargin, ymargin and zmargin +---------------------------------------- + +`.Axes.get_xmargin`, `.Axes.get_ymargin` and `.Axes3D.get_zmargin` methods have been +added to return the margin values set by `.Axes.set_xmargin`, `.Axes.set_ymargin` and +`.Axes3D.set_zmargin`, respectively. + +Mathtext improvements +===================== + +``mathtext`` documentation improvements +--------------------------------------- + +The documentation is updated to take information directly from the parser. This means +that (almost) all supported symbols, operators, etc. are shown at :ref:`mathtext`. + +``mathtext`` spacing corrections +-------------------------------- + +As consequence of the updated documentation, the spacing on a number of relational and +operator symbols were correctly classified and therefore will be spaced properly. + +Widget Improvements +=================== + +Check and Radio Button widgets support clearing +----------------------------------------------- + +The `.CheckButtons` and `.RadioButtons` widgets now support clearing their state by +calling their ``.clear`` method. Note that it is not possible to have no selected radio +buttons, so the selected option at construction time is selected. + +3D plotting improvements +======================== + +Setting 3D axis limits now set the limits exactly +------------------------------------------------- + +Previously, setting the limits of a 3D axis would always add a small margin to the +limits. Limits are now set exactly by default. The newly introduced rcparam +``axes3d.automargin`` can be used to revert to the old behavior where margin is +automatically added. + +.. plot:: + :include-source: + :alt: Example of the new behavior of 3D axis limits, and how setting the rcParam reverts to the old behavior. + + fig, axs = plt.subplots(1, 2, subplot_kw={'projection': '3d'}) + + plt.rcParams['axes3d.automargin'] = True + axs[0].set(xlim=(0, 1), ylim=(0, 1), zlim=(0, 1), title='Old Behavior') + + plt.rcParams['axes3d.automargin'] = False # the default in 3.9.0 + axs[1].set(xlim=(0, 1), ylim=(0, 1), zlim=(0, 1), title='New Behavior') + +Other improvements +================== + +BackendRegistry +--------------- + +New :class:`~matplotlib.backends.registry.BackendRegistry` class is the single source of +truth for available backends. The singleton instance is +``matplotlib.backends.backend_registry``. It is used internally by Matplotlib, and also +IPython (and therefore Jupyter) starting with IPython 8.24.0. + +There are three sources of backends: built-in (source code is within the Matplotlib +repository), explicit ``module://some.backend`` syntax (backend is obtained by loading +the module), or via an entry point (self-registering backend in an external package). + +To obtain a list of all registered backends use: + + >>> from matplotlib.backends import backend_registry + >>> backend_registry.list_all() + +Add ``widths``, ``heights`` and ``angles`` setter to ``EllipseCollection`` +-------------------------------------------------------------------------- + +The ``widths``, ``heights`` and ``angles`` values of the +`~matplotlib.collections.EllipseCollection` can now be changed after the collection has +been created. + +.. plot:: + :include-source: + + from matplotlib.collections import EllipseCollection + + rng = np.random.default_rng(0) + + widths = (2, ) + heights = (3, ) + angles = (45, ) + offsets = rng.random((10, 2)) * 10 + + fig, ax = plt.subplots() + + ec = EllipseCollection( + widths=widths, + heights=heights, + angles=angles, + offsets=offsets, + units='x', + offset_transform=ax.transData, + ) + + ax.add_collection(ec) + ax.set_xlim(-2, 12) + ax.set_ylim(-2, 12) + + new_widths = rng.random((10, 2)) * 2 + new_heights = rng.random((10, 2)) * 3 + new_angles = rng.random((10, 2)) * 180 + + ec.set(widths=new_widths, heights=new_heights, angles=new_angles) + +``image.interpolation_stage`` rcParam +------------------------------------- + +This new rcParam controls whether image interpolation occurs in "data" space or in +"rgba" space. + +Arrow patch position is now modifiable +-------------------------------------- + +A setter method has been added that allows updating the position of the `.patches.Arrow` +object without requiring a full re-draw. + +.. plot:: + :include-source: + :alt: Example of changing the position of the arrow with the new ``set_data`` method. + + from matplotlib import animation + from matplotlib.patches import Arrow + + fig, ax = plt.subplots() + ax.set_xlim(0, 10) + ax.set_ylim(0, 10) + + a = Arrow(2, 0, 0, 10) + ax.add_patch(a) + + + # code for modifying the arrow + def update(i): + a.set_data(x=.5, dx=i, dy=6, width=2) + + + ani = animation.FuncAnimation(fig, update, frames=15, interval=90, blit=False) + + plt.show() + +NonUniformImage now has mouseover support +----------------------------------------- + +When mousing over a `~matplotlib.image.NonUniformImage`, the data values are now +displayed. diff --git a/doc/users/release_notes.rst b/doc/users/release_notes.rst index e81efe77bbe3..74bc0f13bf1f 100644 --- a/doc/users/release_notes.rst +++ b/doc/users/release_notes.rst @@ -13,6 +13,19 @@ Release notes .. include:: release_notes_next.rst +Version 3.9 +^^^^^^^^^^^ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_3.9.0.rst + ../api/prev_api_changes/api_changes_3.9.2.rst + ../api/prev_api_changes/api_changes_3.9.1.rst + ../api/prev_api_changes/api_changes_3.9.0.rst + github_stats.rst + prev_whats_new/github_stats_3.9.1.rst + prev_whats_new/github_stats_3.9.0.rst + Version 3.8 ^^^^^^^^^^^ .. toctree:: @@ -21,7 +34,6 @@ Version 3.8 prev_whats_new/whats_new_3.8.0.rst ../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.3.rst prev_whats_new/github_stats_3.8.2.rst prev_whats_new/github_stats_3.8.1.rst @@ -78,10 +90,6 @@ Version 3.4 prev_whats_new/github_stats_3.4.1.rst prev_whats_new/github_stats_3.4.0.rst -============= -Past versions -============= - Version 3.3 ^^^^^^^^^^^ .. toctree:: diff --git a/doc/users/resources/index.rst b/doc/users/resources/index.rst index a2c4ccd4a7fc..77010f176048 100644 --- a/doc/users/resources/index.rst +++ b/doc/users/resources/index.rst @@ -71,10 +71,6 @@ Videos Tutorials ========= - -* `The Python Graph Gallery `_ - by Yan Holtz - * `Matplotlib tutorial `_ by Nicolas P. Rougier @@ -85,3 +81,13 @@ Tutorials * `Beyond the Basics: Data Visualization in Python `_ by Stefanie Molin + +========= +Galleries +========= + +* `Past winners for JDH plotting contest `_ + by Nelle Varoquaux + +* `The Python Graph Gallery `_ + by Yan Holtz diff --git a/environment.yml b/environment.yml index eea12387f431..2930ccf17e83 100644 --- a/environment.yml +++ b/environment.yml @@ -2,8 +2,9 @@ # # conda env create -f environment.yml # conda activate mpl-dev -# pip install -e . +# pip install -e .[dev] # +--- name: mpl-dev channels: - conda-forge @@ -15,14 +16,15 @@ dependencies: - fonttools>=4.22.0 - importlib-resources>=3.2.0 - kiwisolver>=1.3.1 - - numpy>=1.21 - - pillow>=8 - pybind11>=2.6.0 + - meson-python>=0.13.1 + - numpy>=1.23 + - pillow>=8 + - pkg-config - pygobject - pyparsing>=2.3.1 - pyqt - python-dateutil>=2.1 - - setuptools - setuptools_scm - wxpython # building documentation @@ -30,20 +32,22 @@ dependencies: - graphviz - ipython - ipywidgets - - numpydoc>=0.8 - - packaging - - pydata-sphinx-theme + - numpydoc>=1.0 + - packaging>=20 + - pydata-sphinx-theme~=0.15.0 - pyyaml - - sphinx>=1.8.1,!=2.0.0 + - sphinx>=3.0.0,!=6.1.2 - sphinx-copybutton - - sphinx-gallery>=0.12 + - sphinx-gallery>=0.12.0 - sphinx-design + - sphinx-tags>=0.3.0 - pip - pip: - - mpl-sphinx-theme - - sphinxcontrib-svg2pdfconverter + - mpl-sphinx-theme~=3.8.0 + - sphinxcontrib-svg2pdfconverter>=1.1.0 - pikepdf # testing + - black<24 - coverage - flake8>=3.8 - flake8-docstrings>=1.4.0 @@ -62,4 +66,4 @@ dependencies: - pytest-xdist - tornado - pytz - - black<24 + - tox diff --git a/extern/agg24-svn/meson.build b/extern/agg24-svn/meson.build new file mode 100644 index 000000000000..a1c088423cb8 --- /dev/null +++ b/extern/agg24-svn/meson.build @@ -0,0 +1,22 @@ +# We need a patched Agg not available elsewhere, so always use the vendored +# version. + +agg_incdir = include_directories('include') + +agg_lib = static_library('agg', + 'src/agg_bezier_arc.cpp', + 'src/agg_curves.cpp', + 'src/agg_image_filters.cpp', + 'src/agg_trans_affine.cpp', + 'src/agg_vcgen_contour.cpp', + 'src/agg_vcgen_dash.cpp', + 'src/agg_vcgen_stroke.cpp', + 'src/agg_vpgen_segmentator.cpp', + include_directories : agg_incdir, + gnu_symbol_visibility: 'inlineshidden', +) + +agg_dep = declare_dependency( + include_directories: agg_incdir, + link_with: agg_lib, +) diff --git a/extern/meson.build b/extern/meson.build new file mode 100644 index 000000000000..662feb7872da --- /dev/null +++ b/extern/meson.build @@ -0,0 +1,34 @@ +# Bundled code. +subdir('agg24-svn') +subdir('ttconv') + +# External code. + +# FreeType 2.3 has libtool version 9.11.3 as can be checked from the tarball. +# For FreeType>=2.4, there is a conversion table in docs/VERSIONS.txt in the +# FreeType source tree. +if get_option('system-freetype') + freetype_dep = dependency('freetype2', version: '>=9.11.3') +else + # This is the version of FreeType to use when building a local version. It + # must match the value in `lib/matplotlib.__init__.py`. Also update the docs + # in `docs/devel/dependencies.rst`. Bump the cache key in + # `.circleci/config.yml` when changing requirements. + LOCAL_FREETYPE_VERSION = '2.6.1' + + freetype_proj = subproject( + f'freetype-@LOCAL_FREETYPE_VERSION@', + default_options: ['default_library=static']) + freetype_dep = freetype_proj.get_variable('freetype_dep') +endif + +if get_option('system-qhull') + qhull_dep = dependency('qhull_r', version: '>=8.0.2', required: false) + if not qhull_dep.found() + cc.check_header('libqhull_r/qhull_ra.h', required: true) + qhull_dep = cc.find_library('qhull_r') + endif +else + qhull_proj = subproject('qhull') + qhull_dep = qhull_proj.get_variable('qhull_dep') +endif diff --git a/extern/ttconv/meson.build b/extern/ttconv/meson.build new file mode 100644 index 000000000000..939eb3069c43 --- /dev/null +++ b/extern/ttconv/meson.build @@ -0,0 +1,14 @@ +ttconv_lib = static_library('ttconv', + 'pprdrv_tt2.cpp', + 'pprdrv_tt.cpp', + 'ttutil.cpp', + 'pprdrv.h', + 'truetype.h', + dependencies: [py3_dep], + gnu_symbol_visibility: 'inlineshidden', +) + +ttconv_dep = declare_dependency( + include_directories: include_directories('.'), + link_with: ttconv_lib, +) diff --git a/galleries/examples/README.txt b/galleries/examples/README.txt index bab703b4168e..31d4beae578d 100644 --- a/galleries/examples/README.txt +++ b/galleries/examples/README.txt @@ -14,3 +14,8 @@ and source code. For longer tutorials, see our :ref:`tutorials page `. You can also find :ref:`external resources ` and a :ref:`FAQ ` in our :ref:`user guide `. + + +.. admonition:: Tagging! + + You can also browse the example gallery by :ref:`tags `. diff --git a/galleries/examples/animation/animated_histogram.py b/galleries/examples/animation/animated_histogram.py index 03fb04d8acda..b040cc3c3442 100644 --- a/galleries/examples/animation/animated_histogram.py +++ b/galleries/examples/animation/animated_histogram.py @@ -54,3 +54,6 @@ def animate(frame_number, bar_container): anim = functools.partial(animate, bar_container=bar_container) ani = animation.FuncAnimation(fig, anim, 50, repeat=False, blit=True) plt.show() + +# %% +# .. tags:: plot-type: histogram, animation diff --git a/galleries/examples/animation/multiple_axes.py b/galleries/examples/animation/multiple_axes.py index 2c3442c36d63..b0bac3a5f6bc 100644 --- a/galleries/examples/animation/multiple_axes.py +++ b/galleries/examples/animation/multiple_axes.py @@ -1,6 +1,6 @@ """ ======================= -Multiple axes animation +Multiple Axes animation ======================= This example showcases: @@ -80,3 +80,5 @@ def animate(i): # # - `matplotlib.patches.ConnectionPatch` # - `matplotlib.animation.FuncAnimation` +# +# .. tags:: component: axes, animation diff --git a/galleries/examples/animation/random_walk.py b/galleries/examples/animation/random_walk.py index d108da5633b5..4be0b461f933 100644 --- a/galleries/examples/animation/random_walk.py +++ b/galleries/examples/animation/random_walk.py @@ -25,9 +25,7 @@ def random_walk(num_steps, max_step=0.05): def update_lines(num, walks, lines): for line, walk in zip(lines, walks): - # NOTE: there is no .set_data() for 3 dim data... - line.set_data(walk[:num, :2].T) - line.set_3d_properties(walk[:num, 2]) + line.set_data_3d(walk[:num, :].T) return lines @@ -42,7 +40,7 @@ def update_lines(num, walks, lines): # Create lines initially without data lines = [ax.plot([], [], [])[0] for _ in walks] -# Setting the axes properties +# Setting the Axes properties ax.set(xlim3d=(0, 1), xlabel='X') ax.set(ylim3d=(0, 1), ylabel='Y') ax.set(zlim3d=(0, 1), zlabel='Z') diff --git a/galleries/examples/axes_grid1/demo_axes_divider.py b/galleries/examples/axes_grid1/demo_axes_divider.py index ed2e163154c4..42be8aacd8be 100644 --- a/galleries/examples/axes_grid1/demo_axes_divider.py +++ b/galleries/examples/axes_grid1/demo_axes_divider.py @@ -3,8 +3,8 @@ Axes divider ============ -Axes divider to calculate location of axes and -create a divider for them using existing axes instances. +Axes divider to calculate location of Axes and +create a divider for them using existing Axes instances. """ import matplotlib.pyplot as plt @@ -30,13 +30,13 @@ def demo_locatable_axes_hard(fig): divider = SubplotDivider(fig, 2, 2, 2, aspect=True) - # axes for image + # Axes for image ax = fig.add_subplot(axes_locator=divider.new_locator(nx=0, ny=0)) - # axes for colorbar + # Axes for colorbar ax_cb = fig.add_subplot(axes_locator=divider.new_locator(nx=2, ny=0)) divider.set_horizontal([ - Size.AxesX(ax), # main axes + Size.AxesX(ax), # main Axes Size.Fixed(0.05), # padding, 0.1 inch Size.Fixed(0.2), # colorbar, 0.3 inch ]) diff --git a/galleries/examples/axes_grid1/demo_axes_grid.py b/galleries/examples/axes_grid1/demo_axes_grid.py index 70bb19da38fa..d29ca6a05859 100644 --- a/galleries/examples/axes_grid1/demo_axes_grid.py +++ b/galleries/examples/axes_grid1/demo_axes_grid.py @@ -3,7 +3,7 @@ Demo Axes Grid ============== -Grid of 2x2 images with a single colorbar or with one colorbar per axes. +Grid of 2x2 images with a single colorbar or with one colorbar per Axes. """ import matplotlib.pyplot as plt @@ -17,13 +17,13 @@ # A grid of 2x2 images with 0.05 inch pad between images and only the -# lower-left axes is labeled. +# lower-left Axes is labeled. grid = ImageGrid( fig, 141, # similar to fig.add_subplot(141). nrows_ncols=(2, 2), axes_pad=0.05, label_mode="1") for ax in grid: ax.imshow(Z, extent=extent) -# This only affects axes in first column and second row as share_all=False. +# This only affects Axes in first column and second row as share_all=False. grid.axes_llc.set(xticks=[-2, 0, 2], yticks=[-2, 0, 2]) @@ -37,7 +37,7 @@ grid.cbar_axes[0].colorbar(im) for cax in grid.cbar_axes: cax.tick_params(labeltop=False) -# This affects all axes as share_all = True. +# This affects all Axes as share_all = True. grid.axes_llc.set(xticks=[-2, 0, 2], yticks=[-2, 0, 2]) @@ -50,7 +50,7 @@ im = ax.imshow(Z, extent=extent) cax.colorbar(im) cax.tick_params(labeltop=False) -# This affects all axes as share_all = True. +# This affects all Axes as share_all = True. grid.axes_llc.set(xticks=[-2, 0, 2], yticks=[-2, 0, 2]) @@ -65,7 +65,7 @@ im = ax.imshow(Z, extent=extent, vmin=vlim[0], vmax=vlim[1]) cb = cax.colorbar(im) cb.set_ticks((vlim[0], vlim[1])) -# This affects all axes as share_all = True. +# This affects all Axes as share_all = True. grid.axes_llc.set(xticks=[-2, 0, 2], yticks=[-2, 0, 2]) diff --git a/galleries/examples/axes_grid1/demo_axes_grid2.py b/galleries/examples/axes_grid1/demo_axes_grid2.py index 98eeb15ead51..458e83b6d68f 100644 --- a/galleries/examples/axes_grid1/demo_axes_grid2.py +++ b/galleries/examples/axes_grid1/demo_axes_grid2.py @@ -33,7 +33,7 @@ def add_inner_title(ax, title, loc, **kwargs): ZS = [Z[i::3, :] for i in range(3)] extent = extent[0], extent[1]/3., extent[2], extent[3] -# *** Demo 1: colorbar at each axes *** +# *** Demo 1: colorbar at each Axes *** grid = ImageGrid( # 211 = at the position of fig.add_subplot(211) fig, 211, nrows_ncols=(1, 3), axes_pad=0.05, label_mode="1", share_all=True, @@ -60,7 +60,7 @@ def add_inner_title(ax, title, loc, **kwargs): for ax, z in zip(grid2, ZS): im = ax.imshow(z, clim=clim, origin="lower", extent=extent) -# With cbar_mode="single", cax attribute of all axes are identical. +# With cbar_mode="single", cax attribute of all Axes are identical. ax.cax.colorbar(im) for ax, im_title in zip(grid2, ["(a)", "(b)", "(c)"]): diff --git a/galleries/examples/axes_grid1/demo_axes_hbox_divider.py b/galleries/examples/axes_grid1/demo_axes_hbox_divider.py index 65d6764a3374..5188bdf66d6f 100644 --- a/galleries/examples/axes_grid1/demo_axes_hbox_divider.py +++ b/galleries/examples/axes_grid1/demo_axes_hbox_divider.py @@ -5,7 +5,7 @@ Using an `.HBoxDivider` to arrange subplots. -Note that both axes' location are adjusted so that they have +Note that both Axes' location are adjusted so that they have equal heights while maintaining their aspect ratios. """ @@ -36,7 +36,7 @@ # %% # Using a `.VBoxDivider` to arrange subplots. # -# Note that both axes' location are adjusted so that they have +# Note that both Axes' location are adjusted so that they have # equal widths while maintaining their aspect ratios. fig, (ax1, ax2) = plt.subplots(2, 1) diff --git a/galleries/examples/axes_grid1/demo_colorbar_of_inset_axes.py b/galleries/examples/axes_grid1/demo_colorbar_of_inset_axes.py deleted file mode 100644 index 341e11ff52b5..000000000000 --- a/galleries/examples/axes_grid1/demo_colorbar_of_inset_axes.py +++ /dev/null @@ -1,33 +0,0 @@ -""" -=============================== -Adding a colorbar to inset axes -=============================== -""" - -import matplotlib.pyplot as plt - -from matplotlib import cbook -from mpl_toolkits.axes_grid1.inset_locator import inset_axes, zoomed_inset_axes - -fig, ax = plt.subplots(figsize=[5, 4]) -ax.set(aspect=1, xlim=(-15, 15), ylim=(-20, 5)) - -Z = cbook.get_sample_data("axes_grid/bivariate_normal.npy") -extent = (-3, 4, -4, 3) - -axins = zoomed_inset_axes(ax, zoom=2, loc='upper left') -axins.set(xticks=[], yticks=[]) -im = axins.imshow(Z, extent=extent, origin="lower") - -# colorbar -cax = inset_axes(axins, - width="5%", # width = 10% of parent_bbox width - height="100%", # height : 50% - loc='lower left', - bbox_to_anchor=(1.05, 0., 1, 1), - bbox_transform=axins.transAxes, - borderpad=0, - ) -fig.colorbar(im, cax=cax) - -plt.show() diff --git a/galleries/examples/axes_grid1/demo_colorbar_with_axes_divider.py b/galleries/examples/axes_grid1/demo_colorbar_with_axes_divider.py index 9e4611c65bb7..a04fac2c3ebe 100644 --- a/galleries/examples/axes_grid1/demo_colorbar_with_axes_divider.py +++ b/galleries/examples/axes_grid1/demo_colorbar_with_axes_divider.py @@ -5,15 +5,17 @@ Colorbar with AxesDivider ========================= -The `.axes_divider.make_axes_locatable` function takes an existing axes, adds +The `.axes_divider.make_axes_locatable` function takes an existing Axes, adds it to a new `.AxesDivider` and returns the `.AxesDivider`. The `.append_axes` -method of the `.AxesDivider` can then be used to create a new axes on a given -side ("top", "right", "bottom", or "left") of the original axes. This example -uses `.append_axes` to add colorbars next to axes. +method of the `.AxesDivider` can then be used to create a new Axes on a given +side ("top", "right", "bottom", or "left") of the original Axes. This example +uses `.append_axes` to add colorbars next to Axes. -Users should consider simply passing the main axes to the *ax* keyword argument of -`~.Figure.colorbar` instead of creating a locatable axes manually like this. +Users should consider simply passing the main Axes to the *ax* keyword argument of +`~.Figure.colorbar` instead of creating a locatable Axes manually like this. See :ref:`colorbar_placement`. + +.. redirect-from:: /gallery/axes_grid1/simple_colorbar """ import matplotlib.pyplot as plt diff --git a/galleries/examples/axes_grid1/demo_colorbar_with_inset_locator.py b/galleries/examples/axes_grid1/demo_colorbar_with_inset_locator.py index 8ec7d0e7271b..d989fb44bbab 100644 --- a/galleries/examples/axes_grid1/demo_colorbar_with_inset_locator.py +++ b/galleries/examples/axes_grid1/demo_colorbar_with_inset_locator.py @@ -8,7 +8,7 @@ This example shows how to control the position, height, and width of colorbars using `~mpl_toolkits.axes_grid1.inset_locator.inset_axes`. -Inset axes placement is controlled as for legends: either by providing a *loc* +Inset Axes placement is controlled as for legends: either by providing a *loc* option ("upper right", "best", ...), or by providing a locator with respect to the parent bbox. Parameters such as *bbox_to_anchor* and *borderpad* likewise work in the same way, and are also demonstrated here. @@ -16,6 +16,7 @@ Users should consider using `.Axes.inset_axes` instead (see :ref:`colorbar_placement`). +.. redirect-from:: /gallery/axes_grid1/demo_colorbar_of_inset_axes """ import matplotlib.pyplot as plt diff --git a/galleries/examples/axes_grid1/demo_edge_colorbar.py b/galleries/examples/axes_grid1/demo_edge_colorbar.py index e0d5f2b45f77..bde482977991 100644 --- a/galleries/examples/axes_grid1/demo_edge_colorbar.py +++ b/galleries/examples/axes_grid1/demo_edge_colorbar.py @@ -44,7 +44,7 @@ def demo_bottom_cbar(fig): for cax in grid.cbar_axes: cax.axis[cax.orientation].set_label("Bar") - # This affects all axes as share_all = True. + # This affects all Axes as share_all = True. grid.axes_llc.set_xticks([-2, 0, 2]) grid.axes_llc.set_yticks([-2, 0, 2]) @@ -73,7 +73,7 @@ def demo_right_cbar(fig): for cax in grid.cbar_axes: cax.axis[cax.orientation].set_label('Foo') - # This affects all axes because we set share_all = True. + # This affects all Axes because we set share_all = True. grid.axes_llc.set_xticks([-2, 0, 2]) grid.axes_llc.set_yticks([-2, 0, 2]) diff --git a/galleries/examples/axes_grid1/demo_fixed_size_axes.py b/galleries/examples/axes_grid1/demo_fixed_size_axes.py index 7f56c97ee126..a8f8685a115c 100644 --- a/galleries/examples/axes_grid1/demo_fixed_size_axes.py +++ b/galleries/examples/axes_grid1/demo_fixed_size_axes.py @@ -16,7 +16,7 @@ fig = plt.figure(figsize=(6, 6)) -# The first items are for padding and the second items are for the axes. +# The first items are for padding and the second items are for the Axes. # sizes are in inch. h = [Size.Fixed(1.0), Size.Fixed(4.5)] v = [Size.Fixed(0.7), Size.Fixed(5.)] @@ -35,7 +35,7 @@ fig = plt.figure(figsize=(6, 6)) # The first & third items are for padding and the second items are for the -# axes. Sizes are in inches. +# Axes. Sizes are in inches. h = [Size.Fixed(1.0), Size.Scaled(1.), Size.Fixed(.2)] v = [Size.Fixed(0.7), Size.Scaled(1.), Size.Fixed(.5)] diff --git a/galleries/examples/axes_grid1/inset_locator_demo.py b/galleries/examples/axes_grid1/inset_locator_demo.py index b0af820a4253..fa9c4593d932 100644 --- a/galleries/examples/axes_grid1/inset_locator_demo.py +++ b/galleries/examples/axes_grid1/inset_locator_demo.py @@ -7,7 +7,7 @@ # %% # The `.inset_locator`'s `~.inset_locator.inset_axes` allows -# easily placing insets in the corners of the axes by specifying a width and +# easily placing insets in the corners of the Axes by specifying a width and # height and optionally a location (loc) that accepts locations as codes, # similar to `~matplotlib.axes.Axes.legend`. # By default, the inset is offset by some points from the axes, @@ -23,17 +23,17 @@ # at the default upper right location axins = inset_axes(ax, width=1.3, height=0.9) -# Create inset of width 30% and height 40% of the parent axes' bounding box +# Create inset of width 30% and height 40% of the parent Axes' bounding box # at the lower left corner (loc=3) axins2 = inset_axes(ax, width="30%", height="40%", loc=3) # Create inset of mixed specifications in the second subplot; -# width is 30% of parent axes' bounding box and +# width is 30% of parent Axes' bounding box and # height is 1 inch at the upper left corner (loc=2) axins3 = inset_axes(ax2, width="30%", height=1., loc=2) # Create an inset in the lower right corner (loc=4) with borderpad=1, i.e. -# 10 points padding (as 10pt is the default fontsize) to the parent axes +# 10 points padding (as 10pt is the default fontsize) to the parent Axes axins4 = inset_axes(ax2, width="20%", height="20%", loc=4, borderpad=1) # Turn ticklabels of insets off @@ -54,9 +54,9 @@ fig = plt.figure(figsize=[5.5, 2.8]) ax = fig.add_subplot(121) -# We use the axes transform as bbox_transform. Therefore, the bounding box +# We use the Axes transform as bbox_transform. Therefore, the bounding box # needs to be specified in axes coordinates ((0, 0) is the lower left corner -# of the axes, (1, 1) is the upper right corner). +# of the Axes, (1, 1) is the upper right corner). # The bounding box (.2, .4, .6, .5) starts at (.2, .4) and ranges to (.8, .9) # in those coordinates. # Inside this bounding box an inset of half the bounding box' width and @@ -78,7 +78,7 @@ # Note how the two following insets are created at the same positions, one by -# use of the default parent axes' bbox and the other via a bbox in axes +# use of the default parent Axes' bbox and the other via a bbox in Axes # coordinates and the respective transform. ax2 = fig.add_subplot(222) axins2 = inset_axes(ax2, width="30%", height="50%") @@ -101,8 +101,8 @@ # %% -# In the above the axes transform together with 4-tuple bounding boxes has been -# used as it mostly is useful to specify an inset relative to the axes it is +# In the above the Axes transform together with 4-tuple bounding boxes has been +# used as it mostly is useful to specify an inset relative to the Axes it is # an inset to. However, other use cases are equally possible. The following # example examines some of those. # @@ -110,7 +110,7 @@ fig = plt.figure(figsize=[5.5, 2.8]) ax = fig.add_subplot(131) -# Create an inset outside the axes +# Create an inset outside the Axes axins = inset_axes(ax, width="100%", height="100%", bbox_to_anchor=(1.05, .6, .5, .4), bbox_transform=ax.transAxes, loc=2, borderpad=0) @@ -134,7 +134,7 @@ bbox_transform=ax2.transData, loc=2, borderpad=0) # Create an inset horizontally centered in figure coordinates and vertically -# bound to line up with the axes. +# bound to line up with the Axes. from matplotlib.transforms import blended_transform_factory # noqa transform = blended_transform_factory(fig.transFigure, ax2.transAxes) diff --git a/galleries/examples/axes_grid1/inset_locator_demo2.py b/galleries/examples/axes_grid1/inset_locator_demo2.py index 38a58a7df2ff..f648c38e8d55 100644 --- a/galleries/examples/axes_grid1/inset_locator_demo2.py +++ b/galleries/examples/axes_grid1/inset_locator_demo2.py @@ -26,7 +26,7 @@ ax.set_aspect(1) axins = zoomed_inset_axes(ax, zoom=0.5, loc='upper right') -# fix the number of ticks on the inset axes +# fix the number of ticks on the inset Axes axins.yaxis.get_major_locator().set_params(nbins=7) axins.xaxis.get_major_locator().set_params(nbins=7) axins.tick_params(labelleft=False, labelbottom=False) @@ -61,13 +61,13 @@ def add_sizebar(ax, size): x1, x2, y1, y2 = -1.5, -0.9, -2.5, -1.9 axins2.set_xlim(x1, x2) axins2.set_ylim(y1, y2) -# fix the number of ticks on the inset axes +# fix the number of ticks on the inset Axes axins2.yaxis.get_major_locator().set_params(nbins=7) axins2.xaxis.get_major_locator().set_params(nbins=7) axins2.tick_params(labelleft=False, labelbottom=False) -# draw a bbox of the region of the inset axes in the parent axes and -# connecting lines between the bbox and the inset axes area +# draw a bbox of the region of the inset Axes in the parent Axes and +# connecting lines between the bbox and the inset Axes area mark_inset(ax2, axins2, loc1=2, loc2=4, fc="none", ec="0.5") plt.show() diff --git a/galleries/examples/axes_grid1/scatter_hist_locatable_axes.py b/galleries/examples/axes_grid1/scatter_hist_locatable_axes.py index 8304334b90eb..e5ff19d9ee08 100644 --- a/galleries/examples/axes_grid1/scatter_hist_locatable_axes.py +++ b/galleries/examples/axes_grid1/scatter_hist_locatable_axes.py @@ -6,12 +6,12 @@ Show the marginal distributions of a scatter plot as histograms at the sides of the plot. -For a nice alignment of the main axes with the marginals, the axes positions +For a nice alignment of the main Axes with the marginals, the Axes positions are defined by a ``Divider``, produced via `.make_axes_locatable`. Note that -the ``Divider`` API allows setting axes sizes and pads in inches, which is its +the ``Divider`` API allows setting Axes sizes and pads in inches, which is its main feature. -If one wants to set axes sizes and pads relative to the main Figure, see the +If one wants to set Axes sizes and pads relative to the main Figure, see the :doc:`/gallery/lines_bars_and_markers/scatter_hist` example. """ @@ -33,10 +33,10 @@ # the scatter plot: ax.scatter(x, y) -# Set aspect of the main axes. +# Set aspect of the main Axes. ax.set_aspect(1.) -# create new axes on the right and on the top of the current axes +# create new Axes on the right and on the top of the current Axes divider = make_axes_locatable(ax) # below height and pad are in inches ax_histx = divider.append_axes("top", 1.2, pad=0.1, sharex=ax) diff --git a/galleries/examples/axes_grid1/simple_axes_divider1.py b/galleries/examples/axes_grid1/simple_axes_divider1.py index 4b7edc27ae9b..414672dc3596 100644 --- a/galleries/examples/axes_grid1/simple_axes_divider1.py +++ b/galleries/examples/axes_grid1/simple_axes_divider1.py @@ -20,7 +20,7 @@ def label_axes(ax, text): # %% -# Fixed axes sizes; fixed paddings. +# Fixed Axes sizes; fixed paddings. fig = plt.figure(figsize=(6, 6)) fig.suptitle("Fixed axes sizes, fixed paddings") @@ -30,7 +30,7 @@ def label_axes(ax, text): vert = [Size.Fixed(1.5), Size.Fixed(.5), Size.Fixed(1.)] rect = (0.1, 0.1, 0.8, 0.8) -# Divide the axes rectangle into a grid with sizes specified by horiz * vert. +# Divide the Axes rectangle into a grid with sizes specified by horiz * vert. div = Divider(fig, rect, horiz, vert, aspect=False) # The rect parameter will actually be ignored and overridden by axes_locator. @@ -53,7 +53,7 @@ def label_axes(ax, text): vert = [Size.Scaled(1.), Size.Fixed(.5), Size.Scaled(1.5)] rect = (0.1, 0.1, 0.8, 0.8) -# Divide the axes rectangle into a grid with sizes specified by horiz * vert. +# Divide the Axes rectangle into a grid with sizes specified by horiz * vert. div = Divider(fig, rect, horiz, vert, aspect=False) # The rect parameter will actually be ignored and overridden by axes_locator. diff --git a/galleries/examples/axes_grid1/simple_axes_divider3.py b/galleries/examples/axes_grid1/simple_axes_divider3.py index 54050633d4ea..e2f195bcb753 100644 --- a/galleries/examples/axes_grid1/simple_axes_divider3.py +++ b/galleries/examples/axes_grid1/simple_axes_divider3.py @@ -21,7 +21,7 @@ horiz = [Size.AxesX(ax[0]), Size.Fixed(.5), Size.AxesX(ax[1])] vert = [Size.AxesY(ax[0]), Size.Fixed(.5), Size.AxesY(ax[2])] -# divide the axes rectangle into grid whose size is specified by horiz * vert +# divide the Axes rectangle into grid whose size is specified by horiz * vert divider = Divider(fig, rect, horiz, vert, aspect=False) diff --git a/galleries/examples/axes_grid1/simple_axesgrid.py b/galleries/examples/axes_grid1/simple_axesgrid.py index ef13fdd74aee..bc9ffce692f8 100644 --- a/galleries/examples/axes_grid1/simple_axesgrid.py +++ b/galleries/examples/axes_grid1/simple_axesgrid.py @@ -18,8 +18,8 @@ fig = plt.figure(figsize=(4., 4.)) grid = ImageGrid(fig, 111, # similar to subplot(111) - nrows_ncols=(2, 2), # creates 2x2 grid of axes - axes_pad=0.1, # pad between axes in inch. + nrows_ncols=(2, 2), # creates 2x2 grid of Axes + axes_pad=0.1, # pad between Axes in inch. ) for ax, im in zip(grid, [im1, im2, im3, im4]): diff --git a/galleries/examples/axes_grid1/simple_colorbar.py b/galleries/examples/axes_grid1/simple_colorbar.py deleted file mode 100644 index 5617cb0b6b4b..000000000000 --- a/galleries/examples/axes_grid1/simple_colorbar.py +++ /dev/null @@ -1,22 +0,0 @@ -""" -=============== -Simple Colorbar -=============== - -""" -import matplotlib.pyplot as plt -import numpy as np - -from mpl_toolkits.axes_grid1 import make_axes_locatable - -ax = plt.subplot() -im = ax.imshow(np.arange(100).reshape((10, 10))) - -# create an Axes on the right side of ax. The width of cax will be 5% -# of ax and the padding between cax and ax will be fixed at 0.05 inch. -divider = make_axes_locatable(ax) -cax = divider.append_axes("right", size="5%", pad=0.05) - -plt.colorbar(im, cax=cax) - -plt.show() diff --git a/galleries/examples/axisartist/demo_axis_direction.py b/galleries/examples/axisartist/demo_axis_direction.py index 00ba40004a59..8c57b6c5a351 100644 --- a/galleries/examples/axisartist/demo_axis_direction.py +++ b/galleries/examples/axisartist/demo_axis_direction.py @@ -20,7 +20,10 @@ def setup_axes(fig, rect): """Polar projection, but in a rectangular box.""" # see demo_curvelinear_grid.py for details grid_helper = GridHelperCurveLinear( - Affine2D().scale(np.pi/180., 1.) + PolarAxes.PolarTransform(), + ( + Affine2D().scale(np.pi/180., 1.) + + PolarAxes.PolarTransform(apply_theta_transforms=False) + ), extreme_finder=angle_helper.ExtremeFinderCycle( 20, 20, lon_cycle=360, lat_cycle=None, diff --git a/galleries/examples/axisartist/demo_axisline_style.py b/galleries/examples/axisartist/demo_axisline_style.py index 644864be8103..3fd1d4d8b767 100644 --- a/galleries/examples/axisartist/demo_axisline_style.py +++ b/galleries/examples/axisartist/demo_axisline_style.py @@ -5,7 +5,7 @@ This example shows some configurations for axis style. -Note: The `mpl_toolkits.axisartist` axes classes may be confusing for new +Note: The `mpl_toolkits.axisartist` Axes classes may be confusing for new users. If the only aim is to obtain arrow heads at the ends of the axes, rather check out the :doc:`/gallery/spines/centered_spines_with_arrows` example. diff --git a/galleries/examples/axisartist/demo_curvelinear_grid.py b/galleries/examples/axisartist/demo_curvelinear_grid.py index 76ade00ecb5e..40853dee12cb 100644 --- a/galleries/examples/axisartist/demo_curvelinear_grid.py +++ b/galleries/examples/axisartist/demo_curvelinear_grid.py @@ -54,7 +54,8 @@ def curvelinear_test2(fig): # PolarAxes.PolarTransform takes radian. However, we want our coordinate # system in degree - tr = Affine2D().scale(np.pi/180, 1) + PolarAxes.PolarTransform() + tr = Affine2D().scale(np.pi/180, 1) + PolarAxes.PolarTransform( + apply_theta_transforms=False) # Polar projection, which involves cycle, and also has limits in # its coordinates, needs a special method to find the extremes # (min, max of the coordinate within the view). @@ -90,7 +91,7 @@ def curvelinear_test2(fig): ax1.grid(True, zorder=0) - # A parasite axes with given transform + # A parasite Axes with given transform ax2 = ax1.get_aux_axes(tr) # note that ax2.transData == tr + ax1.transData # Anything you draw in ax2 will match the ticks and grids of ax1. diff --git a/galleries/examples/axisartist/demo_curvelinear_grid2.py b/galleries/examples/axisartist/demo_curvelinear_grid2.py index 4a8c115f196f..9bf3c5598244 100644 --- a/galleries/examples/axisartist/demo_curvelinear_grid2.py +++ b/galleries/examples/axisartist/demo_curvelinear_grid2.py @@ -7,7 +7,7 @@ This example demonstrates how to use GridHelperCurveLinear to define custom grids and ticklines by applying a transformation on the grid. -As showcase on the plot, a 5x5 matrix is displayed on the axes. +As showcase on the plot, a 5x5 matrix is displayed on the Axes. """ import matplotlib.pyplot as plt diff --git a/galleries/examples/axisartist/demo_floating_axes.py b/galleries/examples/axisartist/demo_floating_axes.py index 87577b29c756..632f6d237aa6 100644 --- a/galleries/examples/axisartist/demo_floating_axes.py +++ b/galleries/examples/axisartist/demo_floating_axes.py @@ -54,7 +54,7 @@ def setup_axes2(fig, rect): With custom locator and formatter. Note that the extreme values are swapped. """ - tr = PolarAxes.PolarTransform() + tr = PolarAxes.PolarTransform(apply_theta_transforms=False) pi = np.pi angle_ticks = [(0, r"$0$"), @@ -76,7 +76,7 @@ def setup_axes2(fig, rect): rect, axes_class=floating_axes.FloatingAxes, grid_helper=grid_helper) ax1.grid() - # create a parasite axes whose transData in RA, cz + # create a parasite Axes whose transData in RA, cz aux_ax = ax1.get_aux_axes(tr) aux_ax.patch = ax1.patch # for aux_ax to have a clip path as in ax @@ -99,7 +99,8 @@ def setup_axes3(fig, rect): # scale degree to radians tr_scale = Affine2D().scale(np.pi/180., 1.) - tr = tr_rotate + tr_scale + PolarAxes.PolarTransform() + tr = tr_rotate + tr_scale + PolarAxes.PolarTransform( + apply_theta_transforms=False) grid_locator1 = angle_helper.LocatorHMS(4) tick_formatter1 = angle_helper.FormatterHMS() @@ -134,7 +135,7 @@ def setup_axes3(fig, rect): ax1.axis["top"].label.set_text(r"$\alpha_{1950}$") ax1.grid() - # create a parasite axes whose transData in RA, cz + # create a parasite Axes whose transData in RA, cz aux_ax = ax1.get_aux_axes(tr) aux_ax.patch = ax1.patch # for aux_ax to have a clip path as in ax diff --git a/galleries/examples/axisartist/demo_floating_axis.py b/galleries/examples/axisartist/demo_floating_axis.py index 0894bf8f4ce1..5296b682367b 100644 --- a/galleries/examples/axisartist/demo_floating_axis.py +++ b/galleries/examples/axisartist/demo_floating_axis.py @@ -22,7 +22,8 @@ def curvelinear_test2(fig): """Polar projection, but in a rectangular box.""" # see demo_curvelinear_grid.py for details - tr = Affine2D().scale(np.pi / 180., 1.) + PolarAxes.PolarTransform() + tr = Affine2D().scale(np.pi / 180., 1.) + PolarAxes.PolarTransform( + apply_theta_transforms=False) extreme_finder = angle_helper.ExtremeFinderCycle(20, 20, diff --git a/galleries/examples/axisartist/demo_parasite_axes.py b/galleries/examples/axisartist/demo_parasite_axes.py index fb04e1e77873..8565ef455c7e 100644 --- a/galleries/examples/axisartist/demo_parasite_axes.py +++ b/galleries/examples/axisartist/demo_parasite_axes.py @@ -3,7 +3,7 @@ Parasite Axes demo ================== -Create a parasite axes. Such axes would share the x scale with a host axes, +Create a parasite Axes. Such Axes would share the x scale with a host Axes, but show a different scale in y direction. This approach uses `mpl_toolkits.axes_grid1.parasite_axes.HostAxes` and diff --git a/galleries/examples/axisartist/simple_axis_pad.py b/galleries/examples/axisartist/simple_axis_pad.py index 7027a88d3549..9c613c820b2b 100644 --- a/galleries/examples/axisartist/simple_axis_pad.py +++ b/galleries/examples/axisartist/simple_axis_pad.py @@ -21,7 +21,8 @@ def setup_axes(fig, rect): """Polar projection, but in a rectangular box.""" # see demo_curvelinear_grid.py for details - tr = Affine2D().scale(np.pi/180., 1.) + PolarAxes.PolarTransform() + tr = Affine2D().scale(np.pi/180., 1.) + PolarAxes.PolarTransform( + apply_theta_transforms=False) extreme_finder = angle_helper.ExtremeFinderCycle(20, 20, lon_cycle=360, diff --git a/galleries/examples/axisartist/simple_axisartist1.py b/galleries/examples/axisartist/simple_axisartist1.py index 4c0807d86d24..386347e142a1 100644 --- a/galleries/examples/axisartist/simple_axisartist1.py +++ b/galleries/examples/axisartist/simple_axisartist1.py @@ -19,7 +19,7 @@ from mpl_toolkits import axisartist fig = plt.figure(figsize=(6, 3), layout="constrained") -# To construct axes of two different classes, we need to use gridspec (or +# To construct Axes of two different classes, we need to use gridspec (or # MATLAB-style add_subplot calls). gs = fig.add_gridspec(1, 2) diff --git a/galleries/examples/color/colorbar_basics.py b/galleries/examples/color/colorbar_basics.py index 30fb7ed3096b..506789916637 100644 --- a/galleries/examples/color/colorbar_basics.py +++ b/galleries/examples/color/colorbar_basics.py @@ -5,7 +5,7 @@ Use `~.Figure.colorbar` by specifying the mappable object (here the `.AxesImage` returned by `~.axes.Axes.imshow`) -and the axes to attach the colorbar to. +and the Axes to attach the colorbar to. """ import matplotlib.pyplot as plt @@ -28,7 +28,7 @@ # add the colorbar using the figure's method, # telling which mappable we're talking about and -# which axes object it should be near +# which Axes object it should be near fig.colorbar(pos, ax=ax1) # repeat everything above for the negative data diff --git a/galleries/examples/color/custom_cmap.py b/galleries/examples/color/custom_cmap.py index ea3c06e13cac..667dc3133819 100644 --- a/galleries/examples/color/custom_cmap.py +++ b/galleries/examples/color/custom_cmap.py @@ -279,4 +279,4 @@ # - `matplotlib.colors.LinearSegmentedColormap.from_list` # - `matplotlib.cm` # - `matplotlib.cm.ScalarMappable.set_cmap` -# - `matplotlib.cm.register_cmap` +# - `matplotlib.cm.ColormapRegistry.register` diff --git a/galleries/examples/event_handling/data_browser.py b/galleries/examples/event_handling/data_browser.py index c24c77ecc57f..6d9068940285 100644 --- a/galleries/examples/event_handling/data_browser.py +++ b/galleries/examples/event_handling/data_browser.py @@ -23,7 +23,7 @@ class PointBrowser: """ Click on a point to select and highlight it -- the data that - generated the point will be shown in the lower axes. Use the 'n' + generated the point will be shown in the lower Axes. Use the 'n' and 'p' keys to browse through the next and previous points """ diff --git a/galleries/examples/event_handling/figure_axes_enter_leave.py b/galleries/examples/event_handling/figure_axes_enter_leave.py index d55fb6e69d6a..0793f2901585 100644 --- a/galleries/examples/event_handling/figure_axes_enter_leave.py +++ b/galleries/examples/event_handling/figure_axes_enter_leave.py @@ -42,7 +42,7 @@ def on_leave_figure(event): fig, axs = plt.subplots(2, 1) -fig.suptitle('mouse hover over figure or axes to trigger events') +fig.suptitle('mouse hover over figure or Axes to trigger events') fig.canvas.mpl_connect('figure_enter_event', on_enter_figure) fig.canvas.mpl_connect('figure_leave_event', on_leave_figure) diff --git a/galleries/examples/event_handling/path_editor.py b/galleries/examples/event_handling/path_editor.py index d6e84b454008..2af54bad53ed 100644 --- a/galleries/examples/event_handling/path_editor.py +++ b/galleries/examples/event_handling/path_editor.py @@ -94,7 +94,6 @@ def on_draw(self, event): self.background = self.canvas.copy_from_bbox(self.ax.bbox) self.ax.draw_artist(self.pathpatch) self.ax.draw_artist(self.line) - self.canvas.blit(self.ax.bbox) def on_button_press(self, event): """Callback for mouse button presses.""" diff --git a/galleries/examples/event_handling/pong_sgskip.py b/galleries/examples/event_handling/pong_sgskip.py index 5b26c143e7ac..e93f89580621 100644 --- a/galleries/examples/event_handling/pong_sgskip.py +++ b/galleries/examples/event_handling/pong_sgskip.py @@ -234,7 +234,7 @@ def draw(self): puck.disp.set_offsets([[puck.x, puck.y]]) self.ax.draw_artist(puck.disp) - # just redraw the axes rectangle + # just redraw the Axes rectangle self.canvas.blit(self.ax.bbox) self.canvas.flush_events() if self.cnt == 50000: diff --git a/galleries/examples/event_handling/resample.py b/galleries/examples/event_handling/resample.py index c034254d060a..913cac9cdf0c 100644 --- a/galleries/examples/event_handling/resample.py +++ b/galleries/examples/event_handling/resample.py @@ -75,3 +75,6 @@ def update(self, ax): ax.callbacks.connect('xlim_changed', d.update) ax.set_xlim(16, 365) plt.show() + +# %% +# .. tags:: interactivity: zoom, event-handling diff --git a/galleries/examples/images_contours_and_fields/colormap_normalizations.py b/galleries/examples/images_contours_and_fields/colormap_normalizations.py index 40fa620df7b3..1d81336b4964 100644 --- a/galleries/examples/images_contours_and_fields/colormap_normalizations.py +++ b/galleries/examples/images_contours_and_fields/colormap_normalizations.py @@ -13,85 +13,90 @@ import matplotlib.colors as colors -# %% -# Lognorm: Instead of pcolor log10(Z1) you can have colorbars that have -# the exponential labels using a norm. - N = 100 -X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)] -# A low hump with a spike coming out of the top. Needs to have -# z/colour axis on a log scale, so we see both hump and spike. -# A linear scale only shows the spike. +# %% +# LogNorm +# ------- +# This example data has a low hump with a spike coming out of its center. If plotted +# using a linear colour scale, then only the spike will be visible. To see both hump and +# spike, this requires the z/colour axis on a log scale. +# +# Instead of transforming the data with ``pcolor(log10(Z))``, the color mapping can be +# made logarithmic using a `.LogNorm`. +X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)] Z1 = np.exp(-X**2 - Y**2) Z2 = np.exp(-(X * 10)**2 - (Y * 10)**2) Z = Z1 + 50 * Z2 fig, ax = plt.subplots(2, 1) -pcm = ax[0].pcolor(X, Y, Z, - norm=colors.LogNorm(vmin=Z.min(), vmax=Z.max()), - cmap='PuBu_r', shading='nearest') -fig.colorbar(pcm, ax=ax[0], extend='max') - -pcm = ax[1].pcolor(X, Y, Z, cmap='PuBu_r', shading='nearest') -fig.colorbar(pcm, ax=ax[1], extend='max') +pcm = ax[0].pcolor(X, Y, Z, cmap='PuBu_r', shading='nearest') +fig.colorbar(pcm, ax=ax[0], extend='max', label='linear scaling') +pcm = ax[1].pcolor(X, Y, Z, cmap='PuBu_r', shading='nearest', + norm=colors.LogNorm(vmin=Z.min(), vmax=Z.max())) +fig.colorbar(pcm, ax=ax[1], extend='max', label='LogNorm') # %% -# PowerNorm: Here a power-law trend in X partially obscures a rectified -# sine wave in Y. We can remove the power law using a PowerNorm. +# PowerNorm +# --------- +# This example data mixes a power-law trend in X with a rectified sine wave in Y. If +# plotted using a linear colour scale, then the power-law trend in X partially obscures +# the sine wave in Y. +# +# The power law can be removed using a `.PowerNorm`. X, Y = np.mgrid[0:3:complex(0, N), 0:2:complex(0, N)] -Z1 = (1 + np.sin(Y * 10.)) * X**2 +Z = (1 + np.sin(Y * 10)) * X**2 fig, ax = plt.subplots(2, 1) -pcm = ax[0].pcolormesh(X, Y, Z1, norm=colors.PowerNorm(gamma=1. / 2.), - cmap='PuBu_r', shading='nearest') -fig.colorbar(pcm, ax=ax[0], extend='max') +pcm = ax[0].pcolormesh(X, Y, Z, cmap='PuBu_r', shading='nearest') +fig.colorbar(pcm, ax=ax[0], extend='max', label='linear scaling') -pcm = ax[1].pcolormesh(X, Y, Z1, cmap='PuBu_r', shading='nearest') -fig.colorbar(pcm, ax=ax[1], extend='max') +pcm = ax[1].pcolormesh(X, Y, Z, cmap='PuBu_r', shading='nearest', + norm=colors.PowerNorm(gamma=0.5)) +fig.colorbar(pcm, ax=ax[1], extend='max', label='PowerNorm') # %% -# SymLogNorm: two humps, one negative and one positive, The positive -# with 5-times the amplitude. Linearly, you cannot see detail in the -# negative hump. Here we logarithmically scale the positive and -# negative data separately. +# SymLogNorm +# ---------- +# This example data has two humps, one negative and one positive, The positive hump has +# 5 times the amplitude of the negative. If plotted with a linear colour scale, then +# the detail in the negative hump is obscured. +# +# Here we logarithmically scale the positive and negative data separately with +# `.SymLogNorm`. # # Note that colorbar labels do not come out looking very good. X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)] -Z = 5 * np.exp(-X**2 - Y**2) +Z1 = np.exp(-X**2 - Y**2) +Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2) +Z = (5 * Z1 - Z2) * 2 fig, ax = plt.subplots(2, 1) -pcm = ax[0].pcolormesh(X, Y, Z, - norm=colors.SymLogNorm(linthresh=0.03, linscale=0.03, - vmin=-1.0, vmax=1.0, base=10), - cmap='RdBu_r', shading='nearest') -fig.colorbar(pcm, ax=ax[0], extend='both') +pcm = ax[0].pcolormesh(X, Y, Z, cmap='RdBu_r', shading='nearest', + vmin=-np.max(Z)) +fig.colorbar(pcm, ax=ax[0], extend='both', label='linear scaling') -pcm = ax[1].pcolormesh(X, Y, Z, cmap='RdBu_r', vmin=-np.max(Z), - shading='nearest') -fig.colorbar(pcm, ax=ax[1], extend='both') +pcm = ax[1].pcolormesh(X, Y, Z, cmap='RdBu_r', shading='nearest', + norm=colors.SymLogNorm(linthresh=0.015, + vmin=-10.0, vmax=10.0, base=10)) +fig.colorbar(pcm, ax=ax[1], extend='both', label='SymLogNorm') # %% -# Custom Norm: An example with a customized normalization. This one -# uses the example above, and normalizes the negative data differently -# from the positive. +# Custom Norm +# ----------- +# Alternatively, the above example data can be scaled with a customized normalization. +# This one normalizes the negative data differently from the positive. -X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)] -Z1 = np.exp(-X**2 - Y**2) -Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2) -Z = (Z1 - Z2) * 2 # Example of making your own norm. Also see matplotlib.colors. # From Joe Kington: This one gives two different linear ramps: - - class MidpointNormalize(colors.Normalize): def __init__(self, vmin=None, vmax=None, midpoint=None, clip=False): self.midpoint = midpoint @@ -107,38 +112,42 @@ def __call__(self, value, clip=None): # %% fig, ax = plt.subplots(2, 1) -pcm = ax[0].pcolormesh(X, Y, Z, - norm=MidpointNormalize(midpoint=0.), - cmap='RdBu_r', shading='nearest') -fig.colorbar(pcm, ax=ax[0], extend='both') +pcm = ax[0].pcolormesh(X, Y, Z, cmap='RdBu_r', shading='nearest', + vmin=-np.max(Z)) +fig.colorbar(pcm, ax=ax[0], extend='both', label='linear scaling') -pcm = ax[1].pcolormesh(X, Y, Z, cmap='RdBu_r', vmin=-np.max(Z), - shading='nearest') -fig.colorbar(pcm, ax=ax[1], extend='both') +pcm = ax[1].pcolormesh(X, Y, Z, cmap='RdBu_r', shading='nearest', + norm=MidpointNormalize(midpoint=0)) +fig.colorbar(pcm, ax=ax[1], extend='both', label='Custom norm') # %% -# BoundaryNorm: For this one you provide the boundaries for your colors, -# and the Norm puts the first color in between the first pair, the -# second color between the second pair, etc. - -fig, ax = plt.subplots(3, 1, figsize=(8, 8)) -ax = ax.flatten() -# even bounds gives a contour-like effect -bounds = np.linspace(-1, 1, 10) -norm = colors.BoundaryNorm(boundaries=bounds, ncolors=256) -pcm = ax[0].pcolormesh(X, Y, Z, - norm=norm, - cmap='RdBu_r', shading='nearest') -fig.colorbar(pcm, ax=ax[0], extend='both', orientation='vertical') +# BoundaryNorm +# ------------ +# For arbitrarily dividing the color scale, the `.BoundaryNorm` may be used; by +# providing the boundaries for colors, this norm puts the first color in between the +# first pair, the second color between the second pair, etc. + +fig, ax = plt.subplots(3, 1, layout='constrained') -# uneven bounds changes the colormapping: -bounds = np.array([-0.25, -0.125, 0, 0.5, 1]) +pcm = ax[0].pcolormesh(X, Y, Z, cmap='RdBu_r', shading='nearest', + vmin=-np.max(Z)) +fig.colorbar(pcm, ax=ax[0], extend='both', orientation='vertical', + label='linear scaling') + +# Evenly-spaced bounds gives a contour-like effect. +bounds = np.linspace(-2, 2, 11) norm = colors.BoundaryNorm(boundaries=bounds, ncolors=256) -pcm = ax[1].pcolormesh(X, Y, Z, norm=norm, cmap='RdBu_r', shading='nearest') -fig.colorbar(pcm, ax=ax[1], extend='both', orientation='vertical') +pcm = ax[1].pcolormesh(X, Y, Z, cmap='RdBu_r', shading='nearest', + norm=norm) +fig.colorbar(pcm, ax=ax[1], extend='both', orientation='vertical', + label='BoundaryNorm\nlinspace(-2, 2, 11)') -pcm = ax[2].pcolormesh(X, Y, Z, cmap='RdBu_r', vmin=-np.max(Z1), - shading='nearest') -fig.colorbar(pcm, ax=ax[2], extend='both', orientation='vertical') +# Unevenly-spaced bounds changes the colormapping. +bounds = np.array([-1, -0.5, 0, 2.5, 5]) +norm = colors.BoundaryNorm(boundaries=bounds, ncolors=256) +pcm = ax[2].pcolormesh(X, Y, Z, cmap='RdBu_r', shading='nearest', + norm=norm) +fig.colorbar(pcm, ax=ax[2], extend='both', orientation='vertical', + label='BoundaryNorm\n[-1, -0.5, 0, 2.5, 5]') plt.show() diff --git a/galleries/examples/images_contours_and_fields/image_annotated_heatmap.py b/galleries/examples/images_contours_and_fields/image_annotated_heatmap.py index 1802af0e6823..23d9fd48dff8 100644 --- a/galleries/examples/images_contours_and_fields/image_annotated_heatmap.py +++ b/galleries/examples/images_contours_and_fields/image_annotated_heatmap.py @@ -114,7 +114,7 @@ def heatmap(data, row_labels, col_labels, ax=None, A list or array of length N with the labels for the columns. ax A `matplotlib.axes.Axes` instance to which the heatmap is plotted. If - not provided, use current axes or create a new one. Optional. + not provided, use current Axes or create a new one. Optional. cbar_kw A dictionary with arguments to `matplotlib.Figure.colorbar`. Optional. cbarlabel diff --git a/galleries/examples/images_contours_and_fields/multi_image.py b/galleries/examples/images_contours_and_fields/multi_image.py index 5634a32abeb9..8be048055dec 100644 --- a/galleries/examples/images_contours_and_fields/multi_image.py +++ b/galleries/examples/images_contours_and_fields/multi_image.py @@ -1,9 +1,19 @@ """ -=============== -Multiple images -=============== +================================= +Multiple images with one colorbar +================================= -Make a set of images with a single colormap, norm, and colorbar. +Use a single colorbar for multiple images. + +Currently, a colorbar can only be connected to one image. The connection +guarantees that the data coloring is consistent with the colormap scale +(i.e. the color of value *x* in the colormap is used for coloring a data +value *x* in the image). + +If we want one colorbar to be representative for multiple images, we have +to explicitly ensure consistent data coloring by using the same data +normalization for all the images. We ensure this by explicitly creating a +``norm`` object that we pass to all the image plotting methods. """ import matplotlib.pyplot as plt @@ -12,47 +22,53 @@ from matplotlib import colors np.random.seed(19680801) -Nr = 3 -Nc = 2 -fig, axs = plt.subplots(Nr, Nc) +datasets = [ + (i+1)/10 * np.random.rand(10, 20) + for i in range(4) +] + +fig, axs = plt.subplots(2, 2) fig.suptitle('Multiple images') -images = [] -for i in range(Nr): - for j in range(Nc): - # Generate data with a range that varies from one plot to the next. - data = ((1 + i + j) / 10) * np.random.rand(10, 20) - images.append(axs[i, j].imshow(data)) - axs[i, j].label_outer() +# create a single norm to be shared across all images +norm = colors.Normalize(vmin=np.min(datasets), vmax=np.max(datasets)) -# Find the min and max of all colors for use in setting the color scale. -vmin = min(image.get_array().min() for image in images) -vmax = max(image.get_array().max() for image in images) -norm = colors.Normalize(vmin=vmin, vmax=vmax) -for im in images: - im.set_norm(norm) +images = [] +for ax, data in zip(axs.flat, datasets): + images.append(ax.imshow(data, norm=norm)) fig.colorbar(images[0], ax=axs, orientation='horizontal', fraction=.1) - -# Make images respond to changes in the norm of other images (e.g. via the -# "edit axis, curves and images parameters" GUI on Qt), but be careful not to -# recurse infinitely! -def update(changed_image): - for im in images: - if (changed_image.get_cmap() != im.get_cmap() - or changed_image.get_clim() != im.get_clim()): - im.set_cmap(changed_image.get_cmap()) - im.set_clim(changed_image.get_clim()) - - -for im in images: - im.callbacks.connect('changed', update) - plt.show() # %% +# The colors are now kept consistent across all images when changing the +# scaling, e.g. through zooming in the colorbar or via the "edit axis, +# curves and images parameters" GUI of the Qt backend. This is sufficient +# for most practical use cases. +# +# Advanced: Additionally sync the colormap +# ---------------------------------------- +# +# Sharing a common norm object guarantees synchronized scaling because scale +# changes modify the norm object in-place and thus propagate to all images +# that use this norm. This approach does not help with synchronizing colormaps +# because changing the colormap of an image (e.g. through the "edit axis, +# curves and images parameters" GUI of the Qt backend) results in the image +# referencing the new colormap object. Thus, the other images are not updated. +# +# To update the other images, sync the +# colormaps using the following code:: +# +# def sync_cmaps(changed_image): +# for im in images: +# if changed_image.get_cmap() != im.get_cmap(): +# im.set_cmap(changed_image.get_cmap()) +# +# for im in images: +# im.callbacks.connect('changed', sync_cmaps) +# # # .. admonition:: References # @@ -63,6 +79,4 @@ def update(changed_image): # - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar` # - `matplotlib.colors.Normalize` # - `matplotlib.cm.ScalarMappable.set_cmap` -# - `matplotlib.cm.ScalarMappable.set_norm` -# - `matplotlib.cm.ScalarMappable.set_clim` # - `matplotlib.cbook.CallbackRegistry.connect` diff --git a/galleries/examples/lines_bars_and_markers/capstyle.py b/galleries/examples/lines_bars_and_markers/capstyle.py index f573aaa871b8..d17f86c6be58 100644 --- a/galleries/examples/lines_bars_and_markers/capstyle.py +++ b/galleries/examples/lines_bars_and_markers/capstyle.py @@ -3,8 +3,8 @@ CapStyle ========= -The `matplotlib._enums.CapStyle` controls how Matplotlib draws the corners -where two different line segments meet. For more details, see the +The `matplotlib._enums.CapStyle` controls how Matplotlib draws the two +endpoints (caps) of an unclosed line. For more details, see the `~matplotlib._enums.CapStyle` docs. """ diff --git a/galleries/examples/lines_bars_and_markers/categorical_variables.py b/galleries/examples/lines_bars_and_markers/categorical_variables.py index bb42ec182ada..e28dda0dda47 100644 --- a/galleries/examples/lines_bars_and_markers/categorical_variables.py +++ b/galleries/examples/lines_bars_and_markers/categorical_variables.py @@ -20,7 +20,7 @@ # %% -# This works on both axes: +# This works on both Axes: cat = ["bored", "happy", "bored", "bored", "happy", "bored"] dog = ["happy", "happy", "happy", "happy", "bored", "bored"] diff --git a/galleries/examples/lines_bars_and_markers/eventcollection_demo.py b/galleries/examples/lines_bars_and_markers/eventcollection_demo.py index f82fb829c248..18783e1649bc 100644 --- a/galleries/examples/lines_bars_and_markers/eventcollection_demo.py +++ b/galleries/examples/lines_bars_and_markers/eventcollection_demo.py @@ -4,7 +4,7 @@ ==================== Plot two curves, then use `.EventCollection`\s to mark the locations of the x -and y data points on the respective axes for each curve. +and y data points on the respective Axes for each curve. """ import matplotlib.pyplot as plt diff --git a/galleries/examples/lines_bars_and_markers/fill_between_demo.py b/galleries/examples/lines_bars_and_markers/fill_between_demo.py index 1bdab8921415..656a8695ba18 100644 --- a/galleries/examples/lines_bars_and_markers/fill_between_demo.py +++ b/galleries/examples/lines_bars_and_markers/fill_between_demo.py @@ -113,8 +113,8 @@ # Selectively marking horizontal regions across the whole Axes # ------------------------------------------------------------ # The same selection mechanism can be applied to fill the full vertical height -# of the axes. To be independent of y-limits, we add a transform that -# interprets the x-values in data coordinates and the y-values in axes +# of the Axes. To be independent of y-limits, we add a transform that +# interprets the x-values in data coordinates and the y-values in Axes # coordinates. # # The following example marks the regions in which the y-data are above a diff --git a/galleries/examples/lines_bars_and_markers/filled_step.py b/galleries/examples/lines_bars_and_markers/filled_step.py index abc806fc5acf..65a7d31a425a 100644 --- a/galleries/examples/lines_bars_and_markers/filled_step.py +++ b/galleries/examples/lines_bars_and_markers/filled_step.py @@ -25,7 +25,7 @@ def filled_hist(ax, edges, values, bottoms=None, orientation='v', Parameters ---------- ax : Axes - The axes to plot to + The Axes to plot to. edges : array A length n+1 array giving the left edges of each bin and the @@ -85,7 +85,7 @@ def stack_hist(ax, stacked_data, sty_cycle, bottoms=None, Parameters ---------- ax : axes.Axes - The axes to add artists too + The Axes to add artists to. stacked_data : array or Mapping A (M, N) shaped array. The first dimension will be iterated over to diff --git a/galleries/examples/lines_bars_and_markers/gradient_bar.py b/galleries/examples/lines_bars_and_markers/gradient_bar.py index 1c61a4bb908e..4cd86f26590f 100644 --- a/galleries/examples/lines_bars_and_markers/gradient_bar.py +++ b/galleries/examples/lines_bars_and_markers/gradient_bar.py @@ -30,7 +30,7 @@ def gradient_image(ax, direction=0.3, cmap_range=(0, 1), **kwargs): Parameters ---------- ax : Axes - The axes to draw on. + The Axes to draw on. direction : float The direction of the gradient. This is a number in range 0 (=vertical) to 1 (=horizontal). diff --git a/galleries/examples/lines_bars_and_markers/multicolored_line.py b/galleries/examples/lines_bars_and_markers/multicolored_line.py index 5d0727e69181..3d14ecaf8567 100644 --- a/galleries/examples/lines_bars_and_markers/multicolored_line.py +++ b/galleries/examples/lines_bars_and_markers/multicolored_line.py @@ -3,47 +3,188 @@ Multicolored lines ================== -This example shows how to make a multicolored line. In this example, the line -is colored based on its derivative. +The example shows two ways to plot a line with the a varying color defined by +a third value. The first example defines the color at each (x, y) point. +The second example defines the color between pairs of points, so the length +of the color value list is one less than the length of the x and y lists. + +Color values at points +---------------------- + """ +import warnings + import matplotlib.pyplot as plt import numpy as np from matplotlib.collections import LineCollection -from matplotlib.colors import BoundaryNorm, ListedColormap + +def colored_line(x, y, c, ax, **lc_kwargs): + """ + Plot a line with a color specified along the line by a third value. + + It does this by creating a collection of line segments. Each line segment is + made up of two straight lines each connecting the current (x, y) point to the + midpoints of the lines connecting the current point with its two neighbors. + This creates a smooth line with no gaps between the line segments. + + Parameters + ---------- + x, y : array-like + The horizontal and vertical coordinates of the data points. + c : array-like + The color values, which should be the same size as x and y. + ax : Axes + Axis object on which to plot the colored line. + **lc_kwargs + Any additional arguments to pass to matplotlib.collections.LineCollection + constructor. This should not include the array keyword argument because + that is set to the color argument. If provided, it will be overridden. + + Returns + ------- + matplotlib.collections.LineCollection + The generated line collection representing the colored line. + """ + if "array" in lc_kwargs: + warnings.warn('The provided "array" keyword argument will be overridden') + + # Default the capstyle to butt so that the line segments smoothly line up + default_kwargs = {"capstyle": "butt"} + default_kwargs.update(lc_kwargs) + + # Compute the midpoints of the line segments. Include the first and last points + # twice so we don't need any special syntax later to handle them. + x = np.asarray(x) + y = np.asarray(y) + x_midpts = np.hstack((x[0], 0.5 * (x[1:] + x[:-1]), x[-1])) + y_midpts = np.hstack((y[0], 0.5 * (y[1:] + y[:-1]), y[-1])) + + # Determine the start, middle, and end coordinate pair of each line segment. + # Use the reshape to add an extra dimension so each pair of points is in its + # own list. Then concatenate them to create: + # [ + # [(x1_start, y1_start), (x1_mid, y1_mid), (x1_end, y1_end)], + # [(x2_start, y2_start), (x2_mid, y2_mid), (x2_end, y2_end)], + # ... + # ] + coord_start = np.column_stack((x_midpts[:-1], y_midpts[:-1]))[:, np.newaxis, :] + coord_mid = np.column_stack((x, y))[:, np.newaxis, :] + coord_end = np.column_stack((x_midpts[1:], y_midpts[1:]))[:, np.newaxis, :] + segments = np.concatenate((coord_start, coord_mid, coord_end), axis=1) + + lc = LineCollection(segments, **default_kwargs) + lc.set_array(c) # set the colors of each segment + + return ax.add_collection(lc) + + +# -------------- Create and show plot -------------- +# Some arbitrary function that gives x, y, and color values +t = np.linspace(-7.4, -0.5, 200) +x = 0.9 * np.sin(t) +y = 0.9 * np.cos(1.6 * t) +color = np.linspace(0, 2, t.size) + +# Create a figure and plot the line on it +fig1, ax1 = plt.subplots() +lines = colored_line(x, y, color, ax1, linewidth=10, cmap="plasma") +fig1.colorbar(lines) # add a color legend + +# Set the axis limits and tick positions +ax1.set_xlim(-1, 1) +ax1.set_ylim(-1, 1) +ax1.set_xticks((-1, 0, 1)) +ax1.set_yticks((-1, 0, 1)) +ax1.set_title("Color at each point") + +plt.show() + +#################################################################### +# This method is designed to give a smooth impression when distances and color +# differences between adjacent points are not too large. The following example +# does not meet this criteria and by that serves to illustrate the segmentation +# and coloring mechanism. +x = [0, 1, 2, 3, 4] +y = [0, 1, 2, 1, 1] +c = [1, 2, 3, 4, 5] +fig, ax = plt.subplots() +ax.scatter(x, y, c=c, cmap='rainbow') +colored_line(x, y, c=c, ax=ax, cmap='rainbow') + +plt.show() + +#################################################################### +# Color values between points +# --------------------------- +# + + +def colored_line_between_pts(x, y, c, ax, **lc_kwargs): + """ + Plot a line with a color specified between (x, y) points by a third value. + + It does this by creating a collection of line segments between each pair of + neighboring points. The color of each segment is determined by the + made up of two straight lines each connecting the current (x, y) point to the + midpoints of the lines connecting the current point with its two neighbors. + This creates a smooth line with no gaps between the line segments. + + Parameters + ---------- + x, y : array-like + The horizontal and vertical coordinates of the data points. + c : array-like + The color values, which should have a size one less than that of x and y. + ax : Axes + Axis object on which to plot the colored line. + **lc_kwargs + Any additional arguments to pass to matplotlib.collections.LineCollection + constructor. This should not include the array keyword argument because + that is set to the color argument. If provided, it will be overridden. + + Returns + ------- + matplotlib.collections.LineCollection + The generated line collection representing the colored line. + """ + if "array" in lc_kwargs: + warnings.warn('The provided "array" keyword argument will be overridden') + + # Check color array size (LineCollection still works, but values are unused) + if len(c) != len(x) - 1: + warnings.warn( + "The c argument should have a length one less than the length of x and y. " + "If it has the same length, use the colored_line function instead." + ) + + # Create a set of line segments so that we can color them individually + # This creates the points as an N x 1 x 2 array so that we can stack points + # together easily to get the segments. The segments array for line collection + # needs to be (numlines) x (points per line) x 2 (for x and y) + points = np.array([x, y]).T.reshape(-1, 1, 2) + segments = np.concatenate([points[:-1], points[1:]], axis=1) + lc = LineCollection(segments, **lc_kwargs) + + # Set the values used for colormapping + lc.set_array(c) + + return ax.add_collection(lc) + + +# -------------- Create and show plot -------------- x = np.linspace(0, 3 * np.pi, 500) y = np.sin(x) dydx = np.cos(0.5 * (x[:-1] + x[1:])) # first derivative -# Create a set of line segments so that we can color them individually -# This creates the points as an N x 1 x 2 array so that we can stack points -# together easily to get the segments. The segments array for line collection -# needs to be (numlines) x (points per line) x 2 (for x and y) -points = np.array([x, y]).T.reshape(-1, 1, 2) -segments = np.concatenate([points[:-1], points[1:]], axis=1) - -fig, axs = plt.subplots(2, 1, sharex=True, sharey=True) - -# Create a continuous norm to map from data points to colors -norm = plt.Normalize(dydx.min(), dydx.max()) -lc = LineCollection(segments, cmap='viridis', norm=norm) -# Set the values used for colormapping -lc.set_array(dydx) -lc.set_linewidth(2) -line = axs[0].add_collection(lc) -fig.colorbar(line, ax=axs[0]) - -# Use a boundary norm instead -cmap = ListedColormap(['r', 'g', 'b']) -norm = BoundaryNorm([-1, -0.5, 0.5, 1], cmap.N) -lc = LineCollection(segments, cmap=cmap, norm=norm) -lc.set_array(dydx) -lc.set_linewidth(2) -line = axs[1].add_collection(lc) -fig.colorbar(line, ax=axs[1]) - -axs[0].set_xlim(x.min(), x.max()) -axs[0].set_ylim(-1.1, 1.1) +fig2, ax2 = plt.subplots() +line = colored_line_between_pts(x, y, dydx, ax2, linewidth=2, cmap="viridis") +fig2.colorbar(line, ax=ax2, label="dy/dx") + +ax2.set_xlim(x.min(), x.max()) +ax2.set_ylim(-1.1, 1.1) +ax2.set_title("Color between points") + plt.show() diff --git a/galleries/examples/lines_bars_and_markers/scatter_hist.py b/galleries/examples/lines_bars_and_markers/scatter_hist.py index 9e4598a15957..95a373961aa1 100644 --- a/galleries/examples/lines_bars_and_markers/scatter_hist.py +++ b/galleries/examples/lines_bars_and_markers/scatter_hist.py @@ -6,23 +6,23 @@ Show the marginal distributions of a scatter plot as histograms at the sides of the plot. -For a nice alignment of the main axes with the marginals, two options are shown +For a nice alignment of the main Axes with the marginals, two options are shown below: .. contents:: :local: While `.Axes.inset_axes` may be a bit more complex, it allows correct handling -of main axes with a fixed aspect ratio. +of main Axes with a fixed aspect ratio. An alternative method to produce a similar figure using the ``axes_grid1`` toolkit is shown in the :doc:`/gallery/axes_grid1/scatter_hist_locatable_axes` -example. Finally, it is also possible to position all axes in absolute +example. Finally, it is also possible to position all Axes in absolute coordinates using `.Figure.add_axes` (not shown here). Let us first define a function that takes x and y data as input, as well -as three axes, the main axes for the scatter, and two marginal axes. It will -then create the scatter and histograms inside the provided axes. +as three Axes, the main Axes for the scatter, and two marginal Axes. It will +then create the scatter and histograms inside the provided Axes. """ import matplotlib.pyplot as plt @@ -56,7 +56,7 @@ def scatter_hist(x, y, ax, ax_histx, ax_histy): # %% # -# Defining the axes positions using a gridspec +# Defining the Axes positions using a gridspec # -------------------------------------------- # # We define a gridspec with unequal width- and height-ratios to achieve desired @@ -65,7 +65,7 @@ def scatter_hist(x, y, ax, ax_histx, ax_histy): # Start with a square Figure. fig = plt.figure(figsize=(6, 6)) # Add a gridspec with two rows and two columns and a ratio of 1 to 4 between -# the size of the marginal axes and the main axes in both directions. +# the size of the marginal Axes and the main Axes in both directions. # Also adjust the subplot parameters for a square plot. gs = fig.add_gridspec(2, 2, width_ratios=(4, 1), height_ratios=(1, 4), left=0.1, right=0.9, bottom=0.1, top=0.9, @@ -80,26 +80,26 @@ def scatter_hist(x, y, ax, ax_histx, ax_histy): # %% # -# Defining the axes positions using inset_axes +# Defining the Axes positions using inset_axes # -------------------------------------------- # # `~.Axes.inset_axes` can be used to position marginals *outside* the main -# axes. The advantage of doing so is that the aspect ratio of the main axes +# Axes. The advantage of doing so is that the aspect ratio of the main Axes # can be fixed, and the marginals will always be drawn relative to the position -# of the axes. +# of the Axes. # Create a Figure, which doesn't have to be square. fig = plt.figure(layout='constrained') -# Create the main axes, leaving 25% of the figure space at the top and on the +# Create the main Axes, leaving 25% of the figure space at the top and on the # right to position marginals. ax = fig.add_gridspec(top=0.75, right=0.75).subplots() -# The main axes' aspect can be fixed. +# The main Axes' aspect can be fixed. ax.set(aspect=1) -# Create marginal axes, which have 25% of the size of the main axes. Note that -# the inset axes are positioned *outside* (on the right and the top) of the -# main axes, by specifying axes coordinates greater than 1. Axes coordinates +# Create marginal Axes, which have 25% of the size of the main Axes. Note that +# the inset Axes are positioned *outside* (on the right and the top) of the +# main Axes, by specifying axes coordinates greater than 1. Axes coordinates # less than 0 would likewise specify positions on the left and the bottom of -# the main axes. +# the main Axes. ax_histx = ax.inset_axes([0, 1.05, 1, 0.25], sharex=ax) ax_histy = ax.inset_axes([1.05, 0, 0.25, 1], sharey=ax) # Draw the scatter plot and marginals. diff --git a/galleries/examples/lines_bars_and_markers/stackplot_demo.py b/galleries/examples/lines_bars_and_markers/stackplot_demo.py index 890a827fa156..d02a9af73da3 100644 --- a/galleries/examples/lines_bars_and_markers/stackplot_demo.py +++ b/galleries/examples/lines_bars_and_markers/stackplot_demo.py @@ -16,15 +16,17 @@ import matplotlib.pyplot as plt import numpy as np +import matplotlib.ticker as mticker + # data from United Nations World Population Prospects (Revision 2019) # https://population.un.org/wpp/, license: CC BY 3.0 IGO year = [1950, 1960, 1970, 1980, 1990, 2000, 2010, 2018] population_by_continent = { - 'africa': [228, 284, 365, 477, 631, 814, 1044, 1275], - 'americas': [340, 425, 519, 619, 727, 840, 943, 1006], - 'asia': [1394, 1686, 2120, 2625, 3202, 3714, 4169, 4560], - 'europe': [220, 253, 276, 295, 310, 303, 294, 293], - 'oceania': [12, 15, 19, 22, 26, 31, 36, 39], + 'Africa': [.228, .284, .365, .477, .631, .814, 1.044, 1.275], + 'the Americas': [.340, .425, .519, .619, .727, .840, .943, 1.006], + 'Asia': [1.394, 1.686, 2.120, 2.625, 3.202, 3.714, 4.169, 4.560], + 'Europe': [.220, .253, .276, .295, .310, .303, .294, .293], + 'Oceania': [.012, .015, .019, .022, .026, .031, .036, .039], } fig, ax = plt.subplots() @@ -33,7 +35,9 @@ ax.legend(loc='upper left', reverse=True) ax.set_title('World population') ax.set_xlabel('Year') -ax.set_ylabel('Number of people (millions)') +ax.set_ylabel('Number of people (billions)') +# add tick at every 200 million people +ax.yaxis.set_minor_locator(mticker.MultipleLocator(.2)) plt.show() diff --git a/galleries/examples/lines_bars_and_markers/timeline.py b/galleries/examples/lines_bars_and_markers/timeline.py index f94f00799caa..93b98a403620 100644 --- a/galleries/examples/lines_bars_and_markers/timeline.py +++ b/galleries/examples/lines_bars_and_markers/timeline.py @@ -28,22 +28,19 @@ data = json.loads(urllib.request.urlopen(url, timeout=1).read().decode()) dates = [] - names = [] + releases = [] for item in data: if 'rc' not in item['tag_name'] and 'b' not in item['tag_name']: dates.append(item['published_at'].split("T")[0]) - names.append(item['tag_name']) - # Convert date strings (e.g. 2014-10-18) to datetime - dates = [datetime.strptime(d, "%Y-%m-%d") for d in dates] + releases.append(item['tag_name'].lstrip("v")) except Exception: # In case the above fails, e.g. because of missing internet connection # use the following lists as fallback. - names = ['v2.2.4', 'v3.0.3', 'v3.0.2', 'v3.0.1', 'v3.0.0', 'v2.2.3', - 'v2.2.2', 'v2.2.1', 'v2.2.0', 'v2.1.2', 'v2.1.1', 'v2.1.0', - 'v2.0.2', 'v2.0.1', 'v2.0.0', 'v1.5.3', 'v1.5.2', 'v1.5.1', - 'v1.5.0', 'v1.4.3', 'v1.4.2', 'v1.4.1', 'v1.4.0'] - + releases = ['2.2.4', '3.0.3', '3.0.2', '3.0.1', '3.0.0', '2.2.3', + '2.2.2', '2.2.1', '2.2.0', '2.1.2', '2.1.1', '2.1.0', + '2.0.2', '2.0.1', '2.0.0', '1.5.3', '1.5.2', '1.5.1', + '1.5.0', '1.4.3', '1.4.2', '1.4.1', '1.4.0'] dates = ['2019-02-26', '2019-02-26', '2018-11-10', '2018-11-10', '2018-09-18', '2018-08-10', '2018-03-17', '2018-03-16', '2018-03-06', '2018-01-18', '2017-12-10', '2017-10-07', @@ -51,8 +48,8 @@ '2016-07-03', '2016-01-10', '2015-10-29', '2015-02-16', '2014-10-26', '2014-10-18', '2014-08-26'] - # Convert date strings (e.g. 2014-10-18) to datetime - dates = [datetime.strptime(d, "%Y-%m-%d") for d in dates] +dates = [datetime.strptime(d, "%Y-%m-%d") for d in dates] # Convert strs to dates. +dates, releases = zip(*sorted(zip(dates, releases))) # Sort by increasing date. # %% # Next, we'll create a stem plot with some variation in levels as to @@ -64,32 +61,45 @@ # # Note that Matplotlib will automatically plot datetime inputs. - -# Choose some nice levels -levels = np.tile([-5, 5, -3, 3, -1, 1], - int(np.ceil(len(dates)/6)))[:len(dates)] - -# Create figure and plot a stem plot with the date +# Choose some nice levels: alternate minor releases between top and bottom, and +# progressievly shorten the stems for bugfix releases. +levels = [] +major_minor_releases = sorted({release[:3] for release in releases}) +for release in releases: + major_minor = release[:3] + bugfix = int(release[4]) + h = 1 + 0.8 * (5 - bugfix) + level = h if major_minor_releases.index(major_minor) % 2 == 0 else -h + levels.append(level) + +# The figure and the axes. fig, ax = plt.subplots(figsize=(8.8, 4), layout="constrained") ax.set(title="Matplotlib release dates") -ax.vlines(dates, 0, levels, color="tab:red") # The vertical stems. -ax.plot(dates, np.zeros_like(dates), "-o", - color="k", markerfacecolor="w") # Baseline and markers on it. - -# annotate lines -for d, l, r in zip(dates, levels, names): - ax.annotate(r, xy=(d, l), - xytext=(-3, np.sign(l)*3), textcoords="offset points", - horizontalalignment="right", - verticalalignment="bottom" if l > 0 else "top") - -# format x-axis with 4-month intervals -ax.xaxis.set_major_locator(mdates.MonthLocator(interval=4)) -ax.xaxis.set_major_formatter(mdates.DateFormatter("%b %Y")) -plt.setp(ax.get_xticklabels(), rotation=30, ha="right") - -# remove y-axis and spines +# The vertical stems. +ax.vlines(dates, 0, levels, + color=[("tab:red", 1 if release.endswith(".0") else .5) + for release in releases]) +# The baseline. +ax.axhline(0, c="black") +# The markers on the baseline. +minor_dates = [date for date, release in zip(dates, releases) if release[-1] == '0'] +bugfix_dates = [date for date, release in zip(dates, releases) if release[-1] != '0'] +ax.plot(bugfix_dates, np.zeros_like(bugfix_dates), "ko", mfc="white") +ax.plot(minor_dates, np.zeros_like(minor_dates), "ko", mfc="tab:red") + +# Annotate the lines. +for date, level, release in zip(dates, levels, releases): + ax.annotate(release, xy=(date, level), + xytext=(-3, np.sign(level)*3), textcoords="offset points", + verticalalignment="bottom" if level > 0 else "top", + weight="bold" if release.endswith(".0") else "normal", + bbox=dict(boxstyle='square', pad=0, lw=0, fc=(1, 1, 1, 0.7))) + +ax.yaxis.set(major_locator=mdates.YearLocator(), + major_formatter=mdates.DateFormatter("%Y")) + +# Remove the y-axis and some spines. ax.yaxis.set_visible(False) ax.spines[["left", "top", "right"]].set_visible(False) diff --git a/galleries/examples/lines_bars_and_markers/vline_hline_demo.py b/galleries/examples/lines_bars_and_markers/vline_hline_demo.py index 564d44ce7401..c2f5d025b15c 100644 --- a/galleries/examples/lines_bars_and_markers/vline_hline_demo.py +++ b/galleries/examples/lines_bars_and_markers/vline_hline_demo.py @@ -21,7 +21,7 @@ vax.plot(t, s + nse, '^') vax.vlines(t, [0], s) # By using ``transform=vax.get_xaxis_transform()`` the y coordinates are scaled -# such that 0 maps to the bottom of the axes and 1 to the top. +# such that 0 maps to the bottom of the Axes and 1 to the top. vax.vlines([1, 2], 0, 1, transform=vax.get_xaxis_transform(), colors='r') vax.set_xlabel('time (s)') vax.set_title('Vertical lines demo') diff --git a/galleries/examples/misc/coords_report.py b/galleries/examples/misc/coords_report.py index 127ce712fc1e..84503be35c5f 100644 --- a/galleries/examples/misc/coords_report.py +++ b/galleries/examples/misc/coords_report.py @@ -3,7 +3,7 @@ Coords Report ============= -Override the default reporting of coords as the mouse moves over the axes +Override the default reporting of coords as the mouse moves over the Axes in an interactive backend. """ diff --git a/galleries/examples/misc/custom_projection.py b/galleries/examples/misc/custom_projection.py index e63ba8771ee1..1bd4ce772b0a 100644 --- a/galleries/examples/misc/custom_projection.py +++ b/galleries/examples/misc/custom_projection.py @@ -90,7 +90,7 @@ def _set_lim_and_transforms(self): # the inline documentation there. # The goal of the first two transformations is to get from the - # data space (in this case longitude and latitude) to axes + # data space (in this case longitude and latitude) to Axes # space. It is separated into a non-affine and affine part so # that the non-affine part does not have to be recomputed when # a simple affine change to the figure has been made (such as @@ -102,12 +102,12 @@ def _set_lim_and_transforms(self): # 2) The above has an output range that is not in the unit # rectangle, so scale and translate it so it fits correctly - # within the axes. The peculiar calculations of xscale and + # within the Axes. The peculiar calculations of xscale and # yscale are specific to an Aitoff-Hammer projection, so don't # worry about them too much. self.transAffine = self._get_affine_transform() - # 3) This is the transformation from axes space to display + # 3) This is the transformation from Axes space to display # space. self.transAxes = BboxTransformTo(self.bbox) @@ -125,7 +125,7 @@ def _set_lim_and_transforms(self): # gridlines and tick labels. # Longitude gridlines and ticklabels. The input to these - # transforms are in display space in x and axes space in y. + # transforms are in display space in x and Axes space in y. # Therefore, the input values will be in range (-xmin, 0), # (xmax, 1). The goal of these transforms is to go from that # space to display space. The tick labels will be offset 4 @@ -147,11 +147,11 @@ def _set_lim_and_transforms(self): Affine2D().translate(0.0, -4.0) # Now set up the transforms for the latitude ticks. The input to - # these transforms are in axes space in x and display space in + # these transforms are in Axes space in x and display space in # y. Therefore, the input values will be in range (0, -ymin), # (1, ymax). The goal of these transforms is to go from that # space to display space. The tick labels will be offset 4 - # pixels from the edge of the axes ellipse. + # pixels from the edge of the Axes ellipse. yaxis_stretch = Affine2D().scale(np.pi*2, 1).translate(-np.pi, 0) yaxis_space = Affine2D().scale(1.0, 1.1) self._yaxis_transform = \ @@ -235,7 +235,7 @@ def _gen_axes_patch(self): Override this method to define the shape that is used for the background of the plot. It should be a subclass of Patch. - In this case, it is a Circle (that may be warped by the axes + In this case, it is a Circle (that may be warped by the Axes transform into an ellipse). Any data and gridlines will be clipped to this shape. """ diff --git a/galleries/examples/misc/demo_ribbon_box.py b/galleries/examples/misc/demo_ribbon_box.py index 6e79e6f9fde1..d5121ba6ff5c 100644 --- a/galleries/examples/misc/demo_ribbon_box.py +++ b/galleries/examples/misc/demo_ribbon_box.py @@ -49,7 +49,7 @@ def __init__(self, ax, bbox, color, *, extent=(0, 1, 0, 1), **kwargs): self._ribbonbox = RibbonBox(color) self.set_transform(BboxTransformTo(bbox)) - def draw(self, renderer, *args, **kwargs): + def draw(self, renderer): stretch_factor = self._bbox.height / self._bbox.width ny = int(stretch_factor*self._ribbonbox.nx) @@ -57,7 +57,7 @@ def draw(self, renderer, *args, **kwargs): arr = self._ribbonbox.get_stretched_image(stretch_factor) self.set_array(arr) - super().draw(renderer, *args, **kwargs) + super().draw(renderer) def main(): diff --git a/galleries/examples/misc/fig_x.py b/galleries/examples/misc/fig_x.py index 2985c2f1cffc..e2af3e766028 100644 --- a/galleries/examples/misc/fig_x.py +++ b/galleries/examples/misc/fig_x.py @@ -3,7 +3,7 @@ Adding lines to figures ======================= -Adding lines to a figure without any axes. +Adding lines to a figure without any Axes. .. redirect-from:: /gallery/pyplots/fig_x """ diff --git a/galleries/examples/misc/histogram_path.py b/galleries/examples/misc/histogram_path.py index 55260f9699bb..d35e291aa8ce 100644 --- a/galleries/examples/misc/histogram_path.py +++ b/galleries/examples/misc/histogram_path.py @@ -20,8 +20,6 @@ import matplotlib.patches as patches import matplotlib.path as path -fig, axs = plt.subplots(2) - np.random.seed(19680801) # Fixing random state for reproducibility # histogram our data with numpy @@ -44,8 +42,11 @@ # make a patch out of it, don't add a margin at y=0 patch = patches.PathPatch(barpath) patch.sticky_edges.y[:] = [0] -axs[0].add_patch(patch) -axs[0].autoscale_view() + +fig, ax = plt.subplots() +ax.add_patch(patch) +ax.autoscale_view() +plt.show() # %% # Instead of creating a three-dimensional array and using @@ -72,9 +73,10 @@ # make a patch out of it, don't add a margin at y=0 patch = patches.PathPatch(barpath) patch.sticky_edges.y[:] = [0] -axs[1].add_patch(patch) -axs[1].autoscale_view() +fig, ax = plt.subplots() +ax.add_patch(patch) +ax.autoscale_view() plt.show() # %% diff --git a/galleries/examples/misc/logos2.py b/galleries/examples/misc/logos2.py index 3a070535f83f..aca348474e7b 100644 --- a/galleries/examples/misc/logos2.py +++ b/galleries/examples/misc/logos2.py @@ -36,7 +36,7 @@ def get_font_properties(): def create_icon_axes(fig, ax_position, lw_bars, lw_grid, lw_border, rgrid): """ - Create a polar axes containing the matplotlib radar plot. + Create a polar Axes containing the matplotlib radar plot. Parameters ---------- diff --git a/galleries/examples/misc/rasterization_demo.py b/galleries/examples/misc/rasterization_demo.py index 7755e66745cb..ce0cf02dfac2 100644 --- a/galleries/examples/misc/rasterization_demo.py +++ b/galleries/examples/misc/rasterization_demo.py @@ -9,7 +9,7 @@ Whether rasterization should be used can be specified per artist. This can be useful to reduce the file size of large artists, while maintaining the -advantages of vector graphics for other artists such as the axes +advantages of vector graphics for other artists such as the Axes and text. For instance a complicated `~.Axes.pcolormesh` or `~.Axes.contourf` can be made significantly simpler by rasterizing. Setting rasterization only affects vector backends such as PDF, SVG, or PS. diff --git a/galleries/examples/misc/svg_filter_pie.py b/galleries/examples/misc/svg_filter_pie.py index 052cde48a245..b823cc9670c9 100644 --- a/galleries/examples/misc/svg_filter_pie.py +++ b/galleries/examples/misc/svg_filter_pie.py @@ -17,7 +17,7 @@ from matplotlib.patches import Shadow -# make a square figure and axes +# make a square figure and Axes fig = plt.figure(figsize=(6, 6)) ax = fig.add_axes([0.1, 0.1, 0.8, 0.8]) diff --git a/galleries/examples/misc/table_demo.py b/galleries/examples/misc/table_demo.py index 71fb98343d3f..c89fb5cf83df 100644 --- a/galleries/examples/misc/table_demo.py +++ b/galleries/examples/misc/table_demo.py @@ -40,7 +40,7 @@ colors = colors[::-1] cell_text.reverse() -# Add a table at the bottom of the axes +# Add a table at the bottom of the Axes the_table = plt.table(cellText=cell_text, rowLabels=rows, rowColours=colors, diff --git a/galleries/examples/misc/transoffset.py b/galleries/examples/misc/transoffset.py index 5254974b46e6..b3163e8df703 100644 --- a/galleries/examples/misc/transoffset.py +++ b/galleries/examples/misc/transoffset.py @@ -31,7 +31,7 @@ # If we want the same offset for each text instance, # we only need to make one transform. To get the -# transform argument to offset_copy, we need to make the axes +# transform argument to offset_copy, we need to make the Axes # first; the subplot function above is one way to do this. trans_offset = mtransforms.offset_copy(ax.transData, fig=fig, x=0.05, y=0.10, units='inches') diff --git a/galleries/examples/misc/zorder_demo.py b/galleries/examples/misc/zorder_demo.py index 36c5e7965ba2..e077dc56d2d0 100644 --- a/galleries/examples/misc/zorder_demo.py +++ b/galleries/examples/misc/zorder_demo.py @@ -15,7 +15,7 @@ `.Patch`, `.PatchCollection` 1 `.Line2D`, `.LineCollection` (including minor ticks, grid lines) 2 Major ticks 2.01 -`.Text` (including axes labels and titles) 3 +`.Text` (including Axes labels and titles) 3 `.Legend` 5 ================================================================ ======= diff --git a/galleries/examples/mplot3d/3d_bars.py b/galleries/examples/mplot3d/3d_bars.py index 609adb8df380..40a09ae33f68 100644 --- a/galleries/examples/mplot3d/3d_bars.py +++ b/galleries/examples/mplot3d/3d_bars.py @@ -9,7 +9,7 @@ import matplotlib.pyplot as plt import numpy as np -# set up the figure and axes +# set up the figure and Axes fig = plt.figure(figsize=(8, 3)) ax1 = fig.add_subplot(121, projection='3d') ax2 = fig.add_subplot(122, projection='3d') diff --git a/galleries/examples/mplot3d/imshow3d.py b/galleries/examples/mplot3d/imshow3d.py new file mode 100644 index 000000000000..557d96e1bce5 --- /dev/null +++ b/galleries/examples/mplot3d/imshow3d.py @@ -0,0 +1,88 @@ +""" +=============== +2D images in 3D +=============== + +This example demonstrates how to plot 2D color coded images (similar to +`.Axes.imshow`) as a plane in 3D. + +Matplotlib does not have a native function for this. Below we build one by relying +on `.Axes3D.plot_surface`. For simplicity, there are some differences to +`.Axes.imshow`: This function does not set the aspect of the Axes, hence pixels are +not necessarily square. Also, pixel edges are on integer values rather than pixel +centers. Furthermore, many optional parameters of `.Axes.imshow` are not implemented. + +Multiple calls of ``imshow3d`` use independent norms and thus different color scales +by default. If you want to have a single common color scale, you need to construct +a suitable norm beforehand and pass it to all ``imshow3d`` calls. + +A fundamental limitation of the 3D plotting engine is that intersecting objects cannot +be drawn correctly. One object will always be drawn after the other. Therefore, +multiple image planes can well be used in the background as shown in this example. +But this approach is not suitable if the planes intersect. +""" + +import matplotlib.pyplot as plt +import numpy as np + +from matplotlib.colors import Normalize + + +def imshow3d(ax, array, value_direction='z', pos=0, norm=None, cmap=None): + """ + Display a 2D array as a color-coded 2D image embedded in 3d. + + The image will be in a plane perpendicular to the coordinate axis *value_direction*. + + Parameters + ---------- + ax : Axes3D + The 3D Axes to plot into. + array : 2D numpy array + The image values. + value_direction : {'x', 'y', 'z'} + The axis normal to the image plane. + pos : float + The numeric value on the *value_direction* axis at which the image plane is + located. + norm : `~matplotlib.colors.Normalize`, default: Normalize + The normalization method used to scale scalar data. See `imshow()`. + cmap : str or `~matplotlib.colors.Colormap`, default: :rc:`image.cmap` + The Colormap instance or registered colormap name used to map scalar data + to colors. + """ + if norm is None: + norm = Normalize() + colors = plt.get_cmap(cmap)(norm(array)) + + if value_direction == 'x': + nz, ny = array.shape + zi, yi = np.mgrid[0:nz + 1, 0:ny + 1] + xi = np.full_like(yi, pos) + elif value_direction == 'y': + nx, nz = array.shape + xi, zi = np.mgrid[0:nx + 1, 0:nz + 1] + yi = np.full_like(zi, pos) + elif value_direction == 'z': + ny, nx = array.shape + yi, xi = np.mgrid[0:ny + 1, 0:nx + 1] + zi = np.full_like(xi, pos) + else: + raise ValueError(f"Invalid value_direction: {value_direction!r}") + ax.plot_surface(xi, yi, zi, rstride=1, cstride=1, facecolors=colors, shade=False) + + +fig = plt.figure() +ax = fig.add_subplot(projection='3d') +ax.set(xlabel="x", ylabel="y", zlabel="z") + +nx, ny, nz = 8, 10, 5 +data_xy = np.arange(ny * nx).reshape(ny, nx) + 15 * np.random.random((ny, nx)) +data_yz = np.arange(nz * ny).reshape(nz, ny) + 10 * np.random.random((nz, ny)) +data_zx = np.arange(nx * nz).reshape(nx, nz) + 8 * np.random.random((nx, nz)) + +imshow3d(ax, data_xy) +imshow3d(ax, data_yz, value_direction='x', cmap='magma') +imshow3d(ax, data_zx, value_direction='y', pos=ny, cmap='plasma') + +plt.show() diff --git a/galleries/examples/mplot3d/intersecting_planes.py b/galleries/examples/mplot3d/intersecting_planes.py new file mode 100644 index 000000000000..b8aa08fd7e18 --- /dev/null +++ b/galleries/examples/mplot3d/intersecting_planes.py @@ -0,0 +1,89 @@ +""" +=================== +Intersecting planes +=================== + +This examples demonstrates drawing intersecting planes in 3D. It is a generalization +of :doc:`/gallery/mplot3d/imshow3d`. + +Drawing intersecting planes in `.mplot3d` is complicated, because `.mplot3d` is not a +real 3D renderer, but only projects the Artists into 3D and draws them in the right +order. This does not work correctly if Artists overlap each other mutually. In this +example, we lift the problem of mutual overlap by segmenting the planes at their +intersections, making four parts out of each plane. + +This examples only works correctly for planes that cut each other in haves. This +limitation is intentional to keep the code more readable. Cutting at arbitrary +positions would of course be possible but makes the code even more complex. +Thus, this example is more a demonstration of the concept how to work around +limitations of the 3D visualization, it's not a refined solution for drawing +arbitrary intersecting planes, which you can copy-and-paste as is. +""" +import matplotlib.pyplot as plt +import numpy as np + + +def plot_quadrants(ax, array, fixed_coord, cmap): + """For a given 3d *array* plot a plane with *fixed_coord*, using four quadrants.""" + nx, ny, nz = array.shape + index = { + 'x': (nx // 2, slice(None), slice(None)), + 'y': (slice(None), ny // 2, slice(None)), + 'z': (slice(None), slice(None), nz // 2), + }[fixed_coord] + plane_data = array[index] + + n0, n1 = plane_data.shape + quadrants = [ + plane_data[:n0 // 2, :n1 // 2], + plane_data[:n0 // 2, n1 // 2:], + plane_data[n0 // 2:, :n1 // 2], + plane_data[n0 // 2:, n1 // 2:] + ] + + min_val = array.min() + max_val = array.max() + + cmap = plt.get_cmap(cmap) + + for i, quadrant in enumerate(quadrants): + facecolors = cmap((quadrant - min_val) / (max_val - min_val)) + if fixed_coord == 'x': + Y, Z = np.mgrid[0:ny // 2, 0:nz // 2] + X = nx // 2 * np.ones_like(Y) + Y_offset = (i // 2) * ny // 2 + Z_offset = (i % 2) * nz // 2 + ax.plot_surface(X, Y + Y_offset, Z + Z_offset, rstride=1, cstride=1, + facecolors=facecolors, shade=False) + elif fixed_coord == 'y': + X, Z = np.mgrid[0:nx // 2, 0:nz // 2] + Y = ny // 2 * np.ones_like(X) + X_offset = (i // 2) * nx // 2 + Z_offset = (i % 2) * nz // 2 + ax.plot_surface(X + X_offset, Y, Z + Z_offset, rstride=1, cstride=1, + facecolors=facecolors, shade=False) + elif fixed_coord == 'z': + X, Y = np.mgrid[0:nx // 2, 0:ny // 2] + Z = nz // 2 * np.ones_like(X) + X_offset = (i // 2) * nx // 2 + Y_offset = (i % 2) * ny // 2 + ax.plot_surface(X + X_offset, Y + Y_offset, Z, rstride=1, cstride=1, + facecolors=facecolors, shade=False) + + +def figure_3D_array_slices(array, cmap=None): + """Plot a 3d array using three intersecting centered planes.""" + fig = plt.figure() + ax = fig.add_subplot(projection='3d') + ax.set_box_aspect(array.shape) + plot_quadrants(ax, array, 'x', cmap=cmap) + plot_quadrants(ax, array, 'y', cmap=cmap) + plot_quadrants(ax, array, 'z', cmap=cmap) + return fig, ax + + +nx, ny, nz = 70, 100, 50 +r_square = (np.mgrid[-1:1:1j * nx, -1:1:1j * ny, -1:1:1j * nz] ** 2).sum(0) + +figure_3D_array_slices(r_square, cmap='viridis_r') +plt.show() diff --git a/galleries/examples/mplot3d/mixed_subplots.py b/galleries/examples/mplot3d/mixed_subplots.py index 963044020f47..dc196f05f90d 100644 --- a/galleries/examples/mplot3d/mixed_subplots.py +++ b/galleries/examples/mplot3d/mixed_subplots.py @@ -1,6 +1,6 @@ """ ============================= -2D and 3D axes in same figure +2D and 3D Axes in same figure ============================= This example shows a how to plot a 2D and a 3D plot on the same figure. diff --git a/galleries/examples/mplot3d/pathpatch3d.py b/galleries/examples/mplot3d/pathpatch3d.py index f195e19e17a9..335b68003d31 100644 --- a/galleries/examples/mplot3d/pathpatch3d.py +++ b/galleries/examples/mplot3d/pathpatch3d.py @@ -17,7 +17,7 @@ def text3d(ax, xyz, s, zdir="z", size=None, angle=0, usetex=False, **kwargs): """ - Plots the string *s* on the axes *ax*, with position *xyz*, size *size*, + Plots the string *s* on the Axes *ax*, with position *xyz*, size *size*, and rotation angle *angle*. *zdir* gives the axis which is to be treated as the third dimension. *usetex* is a boolean indicating whether the string should be run through a LaTeX subprocess or not. Any additional keyword diff --git a/galleries/examples/mplot3d/subplot3d.py b/galleries/examples/mplot3d/subplot3d.py index 1dfeeb216f58..47e374dc74b9 100644 --- a/galleries/examples/mplot3d/subplot3d.py +++ b/galleries/examples/mplot3d/subplot3d.py @@ -18,7 +18,7 @@ # ============= # First subplot # ============= -# set up the axes for the first plot +# set up the Axes for the first plot ax = fig.add_subplot(1, 2, 1, projection='3d') # plot a 3D surface like in the example mplot3d/surface3d_demo @@ -35,7 +35,7 @@ # ============== # Second subplot # ============== -# set up the axes for the second plot +# set up the Axes for the second plot ax = fig.add_subplot(1, 2, 2, projection='3d') # plot a 3D wireframe like in the example mplot3d/wire3d_demo diff --git a/galleries/examples/pie_and_polar_charts/bar_of_pie.py b/galleries/examples/pie_and_polar_charts/bar_of_pie.py index f386f11bc9aa..ef68b3d79971 100644 --- a/galleries/examples/pie_and_polar_charts/bar_of_pie.py +++ b/galleries/examples/pie_and_polar_charts/bar_of_pie.py @@ -6,7 +6,7 @@ Make a "bar of pie" chart where the first slice of the pie is "exploded" into a bar chart with a further breakdown of said slice's characteristics. The example demonstrates using a figure with multiple -sets of axes and using the axes patches list to add two ConnectionPatches +sets of Axes and using the Axes patches list to add two ConnectionPatches to link the subplot charts. """ diff --git a/galleries/examples/pie_and_polar_charts/nested_pie.py b/galleries/examples/pie_and_polar_charts/nested_pie.py index 8331618fe45c..c83b4f6f84ee 100644 --- a/galleries/examples/pie_and_polar_charts/nested_pie.py +++ b/galleries/examples/pie_and_polar_charts/nested_pie.py @@ -46,7 +46,7 @@ # %% # However, you can accomplish the same output by using a bar plot on -# axes with a polar coordinate system. This may give more flexibility on +# Axes with a polar coordinate system. This may give more flexibility on # the exact design of the plot. # # In this case, we need to map x-values of the bar chart onto radians of diff --git a/galleries/examples/pie_and_polar_charts/polar_legend.py b/galleries/examples/pie_and_polar_charts/polar_legend.py index a4508b0e9f3a..7972b0aaffd4 100644 --- a/galleries/examples/pie_and_polar_charts/polar_legend.py +++ b/galleries/examples/pie_and_polar_charts/polar_legend.py @@ -17,9 +17,9 @@ ax.plot(theta, r, color="tab:orange", lw=3, label="a line") ax.plot(0.5 * theta, r, color="tab:blue", ls="--", lw=3, label="another line") ax.tick_params(grid_color="palegoldenrod") -# For polar axes, it may be useful to move the legend slightly away from the -# axes center, to avoid overlap between the legend and the axes. The following -# snippet places the legend's lower left corner just outside the polar axes +# For polar Axes, it may be useful to move the legend slightly away from the +# Axes center, to avoid overlap between the legend and the Axes. The following +# snippet places the legend's lower left corner just outside the polar Axes # at an angle of 67.5 degrees in polar coordinates. angle = np.deg2rad(67.5) ax.legend(loc="lower left", diff --git a/galleries/examples/pyplots/pyplot_text.py b/galleries/examples/pyplots/pyplot_text.py index 9fb9f4f1608c..72f977c2f985 100644 --- a/galleries/examples/pyplots/pyplot_text.py +++ b/galleries/examples/pyplots/pyplot_text.py @@ -5,7 +5,7 @@ Set the special text objects `~.pyplot.title`, `~.pyplot.xlabel`, and `~.pyplot.ylabel` through the dedicated pyplot functions. Additional text -objects can be placed in the axes using `~.pyplot.text`. +objects can be placed in the Axes using `~.pyplot.text`. You can use TeX-like mathematical typesetting in all texts; see also :ref:`mathtext`. diff --git a/galleries/examples/shapes_and_collections/arrow_guide.py b/galleries/examples/shapes_and_collections/arrow_guide.py index 7ca969b7b190..d9fad893a873 100644 --- a/galleries/examples/shapes_and_collections/arrow_guide.py +++ b/galleries/examples/shapes_and_collections/arrow_guide.py @@ -69,7 +69,7 @@ # change shape or position if you pan or scale the plot. # # In this case we use `.patches.FancyArrowPatch`, and pass the keyword argument -# ``transform=ax.transAxes`` where ``ax`` is the axes we are adding the patch +# ``transform=ax.transAxes`` where ``ax`` is the Axes we are adding the patch # to. # # Note that when the axis limits are changed, the arrow shape and location diff --git a/galleries/examples/shapes_and_collections/fancybox_demo.py b/galleries/examples/shapes_and_collections/fancybox_demo.py index 49692c1be5e6..91cc1d1749ea 100644 --- a/galleries/examples/shapes_and_collections/fancybox_demo.py +++ b/galleries/examples/shapes_and_collections/fancybox_demo.py @@ -90,7 +90,7 @@ def draw_control_points_for_patches(ax): title='boxstyle="round,pad=0.1"\n mutation_scale=2') ax = axs[1, 1] -# When the aspect ratio of the axes is not 1, the fancy box may not be what you +# When the aspect ratio of the Axes is not 1, the fancy box may not be what you # expected (green). fancy = add_fancy_patch_around(ax, bb, boxstyle="round,pad=0.2") fancy.set(facecolor="none", edgecolor="green") diff --git a/galleries/examples/shapes_and_collections/hatch_demo.py b/galleries/examples/shapes_and_collections/hatch_demo.py index 7acb3ca1bba8..f2ca490c4e37 100644 --- a/galleries/examples/shapes_and_collections/hatch_demo.py +++ b/galleries/examples/shapes_and_collections/hatch_demo.py @@ -5,7 +5,7 @@ Hatches can be added to most polygons in Matplotlib, including `~.Axes.bar`, `~.Axes.fill_between`, `~.Axes.contourf`, and children of `~.patches.Polygon`. -They are currently supported in the PS, PDF, SVG, OSX, and Agg backends. The WX +They are currently supported in the PS, PDF, SVG, macosx, and Agg backends. The WX and Cairo backends do not currently support hatching. See also :doc:`/gallery/images_contours_and_fields/contourf_hatching` for diff --git a/galleries/examples/shapes_and_collections/hatch_style_reference.py b/galleries/examples/shapes_and_collections/hatch_style_reference.py index 4d6b517e3876..724abde051b4 100644 --- a/galleries/examples/shapes_and_collections/hatch_style_reference.py +++ b/galleries/examples/shapes_and_collections/hatch_style_reference.py @@ -5,7 +5,7 @@ Hatches can be added to most polygons in Matplotlib, including `~.Axes.bar`, `~.Axes.fill_between`, `~.Axes.contourf`, and children of `~.patches.Polygon`. -They are currently supported in the PS, PDF, SVG, OSX, and Agg backends. The WX +They are currently supported in the PS, PDF, SVG, macosx, and Agg backends. The WX and Cairo backends do not currently support hatching. See also :doc:`/gallery/images_contours_and_fields/contourf_hatching` for diff --git a/galleries/examples/shapes_and_collections/line_collection.py b/galleries/examples/shapes_and_collections/line_collection.py index d534913ad2b2..a27496f62e0e 100644 --- a/galleries/examples/shapes_and_collections/line_collection.py +++ b/galleries/examples/shapes_and_collections/line_collection.py @@ -3,8 +3,7 @@ Plotting multiple lines with a LineCollection ============================================= -Matplotlib can efficiently draw multiple lines at once using a -`~.LineCollection`, as showcased below. +Matplotlib can efficiently draw multiple lines at once using a `~.LineCollection`. """ import matplotlib.pyplot as plt @@ -12,60 +11,52 @@ from matplotlib.collections import LineCollection -x = np.arange(100) -# Here are many sets of y to plot vs. x -ys = x[:50, np.newaxis] + x[np.newaxis, :] +colors = ["indigo", "blue", "green", "yellow", "orange", "red"] -segs = np.zeros((50, 100, 2)) -segs[:, :, 1] = ys -segs[:, :, 0] = x +# create a list of half-circles with varying radii +theta = np.linspace(0, np.pi, 36) +radii = np.linspace(4, 5, num=len(colors)) +arcs = [np.column_stack([r * np.cos(theta), r * np.sin(theta)]) for r in radii] -# Mask some values to test masked array support: -segs = np.ma.masked_where((segs > 50) & (segs < 60), segs) +fig, ax = plt.subplots(figsize=(6.4, 3.2)) +# set axes limits manually because Collections do not take part in autoscaling +ax.set_xlim(-6, 6) +ax.set_ylim(0, 6) +ax.set_aspect("equal") # to make the arcs look circular -# We need to set the plot limits, they will not autoscale -fig, ax = plt.subplots() -ax.set_xlim(x.min(), x.max()) -ax.set_ylim(ys.min(), ys.max()) +# create a LineCollection with the half-circles +# its properties can be set per line by passing a sequence (here used for *colors*) +# or they can be set for all lines by passing a scalar (here used for *linewidths*) +line_collection = LineCollection(arcs, colors=colors, linewidths=4) +ax.add_collection(line_collection) -# *colors* is sequence of rgba tuples. -# *linestyle* is a string or dash tuple. Legal string values are -# solid|dashed|dashdot|dotted. The dash tuple is (offset, onoffseq) where -# onoffseq is an even length tuple of on and off ink in points. If linestyle -# is omitted, 'solid' is used. -# See `matplotlib.collections.LineCollection` for more information. -colors = plt.rcParams['axes.prop_cycle'].by_key()['color'] - -line_segments = LineCollection(segs, linewidths=(0.5, 1, 1.5, 2), - colors=colors, linestyle='solid') -ax.add_collection(line_segments) -ax.set_title('Line collection with masked arrays') plt.show() # %% -# In the following example, instead of passing a list of colors -# (``colors=colors``), we pass an array of values (``array=x``) that get -# colormapped. +# Instead of passing a list of colors (``colors=colors``), we can alternatively use +# colormapping. The lines are then color-coded based on an additional array of values +# passed to the *array* parameter. In the below example, we color the lines based on +# their radius by passing ``array=radii``. -N = 50 -x = np.arange(N) -ys = [x + i for i in x] # Many sets of y to plot vs. x -segs = [np.column_stack([x, y]) for y in ys] +num_arcs = 15 +theta = np.linspace(0, np.pi, 36) +radii = np.linspace(4, 5.5, num=num_arcs) +arcs = [np.column_stack([r * np.cos(theta), r * np.sin(theta)]) for r in radii] -fig, ax = plt.subplots() -ax.set_xlim(np.min(x), np.max(x)) -ax.set_ylim(np.min(ys), np.max(ys)) +fig, ax = plt.subplots(figsize=(6.4, 3)) +# set axes limits manually because Collections do not take part in autoscaling +ax.set_xlim(-6, 6) +ax.set_ylim(0, 6) +ax.set_aspect("equal") # to make the arcs look circular -line_segments = LineCollection(segs, array=x, - linewidths=(0.5, 1, 1.5, 2), - linestyles='solid') -ax.add_collection(line_segments) -axcb = fig.colorbar(line_segments) -axcb.set_label('Line Number') -ax.set_title('Line Collection with mapped colors') -plt.sci(line_segments) # This allows interactive changing of the colormap. -plt.show() +# create a LineCollection with the half-circles and color mapping +line_collection = LineCollection(arcs, array=radii, cmap="rainbow") +ax.add_collection(line_collection) +fig.colorbar(line_collection, label="Radius") +ax.set_title("Line Collection with mapped colors") + +plt.show() # %% # # .. admonition:: References @@ -73,9 +64,7 @@ # The use of the following functions, methods, classes and modules is shown # in this example: # -# - `matplotlib.collections` # - `matplotlib.collections.LineCollection` -# - `matplotlib.cm.ScalarMappable.set_array` +# - `matplotlib.collections.Collection.set_array` # - `matplotlib.axes.Axes.add_collection` # - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar` -# - `matplotlib.pyplot.sci` diff --git a/galleries/examples/shapes_and_collections/patch_collection.py b/galleries/examples/shapes_and_collections/patch_collection.py index 8ac9c3a7c304..ca0dd8e1045d 100644 --- a/galleries/examples/shapes_and_collections/patch_collection.py +++ b/galleries/examples/shapes_and_collections/patch_collection.py @@ -6,7 +6,7 @@ This example demonstrates how to use `.collections.PatchCollection`. See also :doc:`/gallery/shapes_and_collections/artist_reference`, which instead -adds each artist separately to its own axes. +adds each artist separately to its own Axes. """ import matplotlib.pyplot as plt diff --git a/galleries/examples/showcase/pan_zoom_overlap.py b/galleries/examples/showcase/pan_zoom_overlap.py new file mode 100644 index 000000000000..6650b90f01b2 --- /dev/null +++ b/galleries/examples/showcase/pan_zoom_overlap.py @@ -0,0 +1,74 @@ +""" +=================================== +Pan/zoom events of overlapping axes +=================================== + +Example to illustrate how pan/zoom events of overlapping axes are treated. + + +The default is the following: + +- Axes with a visible patch capture pan/zoom events +- Axes with an invisible patch forward pan/zoom events to axes below +- Shared axes always trigger with their parent axes + (irrespective of the patch visibility) + +Note: The visibility of the patch hereby refers to the value of +``ax.patch.get_visible()``. The color and transparency of a +patch have no effect on the treatment of pan/zoom events! + + +``ax.set_forward_navigation_events(val)`` can be used to override the +default behaviour: + +- ``True``: Forward navigation events to axes below. +- ``False``: Execute navigation events only on this axes. +- ``"auto"``: Use the default behaviour. + +To disable pan/zoom events completely, use ``ax.set_navigate(False)`` + +""" + + +import matplotlib.pyplot as plt + +fig = plt.figure(figsize=(11, 6)) +fig.suptitle("Showcase for pan/zoom events on overlapping axes.") + +ax = fig.add_axes((.05, .05, .9, .9)) +ax.patch.set_color(".75") +ax_twin = ax.twinx() + +ax1 = fig.add_subplot(221) +ax1_twin = ax1.twinx() +ax1.text(.5, .5, + "Visible patch\n\n" + "Pan/zoom events are NOT\n" + "forwarded to axes below", + ha="center", va="center", transform=ax1.transAxes) + +ax11 = fig.add_subplot(223, sharex=ax1, sharey=ax1) +ax11.set_forward_navigation_events(True) +ax11.text(.5, .5, + "Visible patch\n\n" + "Override capture behavior:\n\n" + "ax.set_forward_navigation_events(True)", + ha="center", va="center", transform=ax11.transAxes) + +ax2 = fig.add_subplot(222) +ax2_twin = ax2.twinx() +ax2.patch.set_visible(False) +ax2.text(.5, .5, + "Invisible patch\n\n" + "Pan/zoom events are\n" + "forwarded to axes below", + ha="center", va="center", transform=ax2.transAxes) + +ax22 = fig.add_subplot(224, sharex=ax2, sharey=ax2) +ax22.patch.set_visible(False) +ax22.set_forward_navigation_events(False) +ax22.text(.5, .5, + "Invisible patch\n\n" + "Override capture behavior:\n\n" + "ax.set_forward_navigation_events(False)", + ha="center", va="center", transform=ax22.transAxes) diff --git a/galleries/examples/specialty_plots/ishikawa_diagram.py b/galleries/examples/specialty_plots/ishikawa_diagram.py index 18761ca36043..072d7b463c00 100644 --- a/galleries/examples/specialty_plots/ishikawa_diagram.py +++ b/galleries/examples/specialty_plots/ishikawa_diagram.py @@ -9,11 +9,12 @@ Source: https://en.wikipedia.org/wiki/Ishikawa_diagram """ +import math + import matplotlib.pyplot as plt from matplotlib.patches import Polygon, Wedge -# Create the fishbone diagram fig, ax = plt.subplots(figsize=(10, 6), layout='constrained') ax.set_xlim(-5, 5) ax.set_ylim(-5, 5) @@ -22,18 +23,18 @@ def problems(data: str, problem_x: float, problem_y: float, - prob_angle_x: float, prob_angle_y: float): + angle_x: float, angle_y: float): """ Draw each problem section of the Ishikawa plot. Parameters ---------- data : str - The category name. + The name of the problem category. problem_x, problem_y : float, optional The `X` and `Y` positions of the problem arrows (`Y` defaults to zero). - prob_angle_x, prob_angle_y : float, optional - The angle of the problem annotations. They are angled towards + angle_x, angle_y : float, optional + The angle of the problem annotations. They are always angled towards the tail of the plot. Returns @@ -42,8 +43,8 @@ def problems(data: str, """ ax.annotate(str.upper(data), xy=(problem_x, problem_y), - xytext=(prob_angle_x, prob_angle_y), - fontsize='10', + xytext=(angle_x, angle_y), + fontsize=10, color='white', weight='bold', xycoords='data', @@ -56,7 +57,8 @@ def problems(data: str, pad=0.8)) -def causes(data: list, cause_x: float, cause_y: float, +def causes(data: list, + cause_x: float, cause_y: float, cause_xytext=(-9, -0.3), top: bool = True): """ Place each cause to a position relative to the problems @@ -72,7 +74,9 @@ def causes(data: list, cause_x: float, cause_y: float, cause_xytext : tuple, optional Adjust to set the distance of the cause text from the problem arrow in fontsize units. - top : bool + top : bool, default: True + Determines whether the next cause annotation will be + plotted above or below the previous one. Returns ------- @@ -80,26 +84,23 @@ def causes(data: list, cause_x: float, cause_y: float, """ for index, cause in enumerate(data): - # First cause annotation is placed in the middle of the problems arrow + # [, ] + coords = [[0.02, 0], + [0.23, 0.5], + [-0.46, -1], + [0.69, 1.5], + [-0.92, -2], + [1.15, 2.5]] + + # First 'cause' annotation is placed in the middle of the 'problems' arrow # and each subsequent cause is plotted above or below it in succession. - - # [, [, ]] - coords = [[0, [0, 0]], - [0.23, [0.5, -0.5]], - [-0.46, [-1, 1]], - [0.69, [1.5, -1.5]], - [-0.92, [-2, 2]], - [1.15, [2.5, -2.5]]] - if top: - cause_y += coords[index][1][0] - else: - cause_y += coords[index][1][1] cause_x -= coords[index][0] + cause_y += coords[index][1] if top else -coords[index][1] ax.annotate(cause, xy=(cause_x, cause_y), horizontalalignment='center', xytext=cause_xytext, - fontsize='9', + fontsize=9, xycoords='data', textcoords='offset fontsize', arrowprops=dict(arrowstyle="->", @@ -108,82 +109,74 @@ def causes(data: list, cause_x: float, cause_y: float, def draw_body(data: dict): """ - Place each section in its correct place by changing + Place each problem section in its correct place by changing the coordinates on each loop. Parameters ---------- data : dict - The input data (can be list or tuple). ValueError is - raised if more than six arguments are passed. + The input data (can be a dict of lists or tuples). ValueError + is raised if more than six arguments are passed. Returns ------- None. """ - second_sections = [] - third_sections = [] - # Resize diagram to automatically scale in response to the number - # of problems in the input data. - if len(data) == 1 or len(data) == 2: - spine_length = (-2.1, 2) - head_pos = (2, 0) - tail_pos = ((-2.8, 0.8), (-2.8, -0.8), (-2.0, -0.01)) - first_section = [1.6, 0.8] - elif len(data) == 3 or len(data) == 4: - spine_length = (-3.1, 3) - head_pos = (3, 0) - tail_pos = ((-3.8, 0.8), (-3.8, -0.8), (-3.0, -0.01)) - first_section = [2.6, 1.8] - second_sections = [-0.4, -1.2] - else: # len(data) == 5 or 6 - spine_length = (-4.1, 4) - head_pos = (4, 0) - tail_pos = ((-4.8, 0.8), (-4.8, -0.8), (-4.0, -0.01)) - first_section = [3.5, 2.7] - second_sections = [1, 0.2] - third_sections = [-1.5, -2.3] - - # Change the coordinates of the annotations on each loop. + # Set the length of the spine according to the number of 'problem' categories. + length = (math.ceil(len(data) / 2)) - 1 + draw_spine(-2 - length, 2 + length) + + # Change the coordinates of the 'problem' annotations after each one is rendered. + offset = 0 + prob_section = [1.55, 0.8] for index, problem in enumerate(data.values()): - top_row = True - cause_arrow_y = 1.7 - if index % 2 != 0: # Plot problems below the spine. - top_row = False - y_prob_angle = -16 - cause_arrow_y = -1.7 - else: # Plot problems above the spine. - y_prob_angle = 16 - # Plot the 3 sections in pairs along the main spine. - if index in (0, 1): - prob_arrow_x = first_section[0] - cause_arrow_x = first_section[1] - elif index in (2, 3): - prob_arrow_x = second_sections[0] - cause_arrow_x = second_sections[1] - else: - prob_arrow_x = third_sections[0] - cause_arrow_x = third_sections[1] + plot_above = index % 2 == 0 + cause_arrow_y = 1.7 if plot_above else -1.7 + y_prob_angle = 16 if plot_above else -16 + + # Plot each section in pairs along the main spine. + prob_arrow_x = prob_section[0] + length + offset + cause_arrow_x = prob_section[1] + length + offset + if not plot_above: + offset -= 2.5 if index > 5: raise ValueError(f'Maximum number of problems is 6, you have entered ' f'{len(data)}') - # draw main spine - ax.plot(spine_length, [0, 0], color='tab:blue', linewidth=2) - # draw fish head - ax.text(head_pos[0] + 0.1, head_pos[1] - 0.05, 'PROBLEM', fontsize=10, - weight='bold', color='white') - semicircle = Wedge(head_pos, 1, 270, 90, fc='tab:blue') - ax.add_patch(semicircle) - # draw fishtail - triangle = Polygon(tail_pos, fc='tab:blue') - ax.add_patch(triangle) - # Pass each category name to the problems function as a string on each loop. problems(list(data.keys())[index], prob_arrow_x, 0, -12, y_prob_angle) - # Start the cause function with the first annotation being plotted at - # the cause_arrow_x, cause_arrow_y coordinates. - causes(problem, cause_arrow_x, cause_arrow_y, top=top_row) + causes(problem, cause_arrow_x, cause_arrow_y, top=plot_above) + + +def draw_spine(xmin: int, xmax: int): + """ + Draw main spine, head and tail. + + Parameters + ---------- + xmin : int + The default position of the head of the spine's + x-coordinate. + xmax : int + The default position of the tail of the spine's + x-coordinate. + + Returns + ------- + None. + + """ + # draw main spine + ax.plot([xmin - 0.1, xmax], [0, 0], color='tab:blue', linewidth=2) + # draw fish head + ax.text(xmax + 0.1, - 0.05, 'PROBLEM', fontsize=10, + weight='bold', color='white') + semicircle = Wedge((xmax, 0), 1, 270, 90, fc='tab:blue') + ax.add_patch(semicircle) + # draw fish tail + tail_pos = [[xmin - 0.8, 0.8], [xmin - 0.8, -0.8], [xmin, -0.01]] + triangle = Polygon(tail_pos, fc='tab:blue') + ax.add_patch(triangle) # Input data diff --git a/galleries/examples/specialty_plots/leftventricle_bullseye.py b/galleries/examples/specialty_plots/leftventricle_bullseye.py index 70327f49c054..e3c3d52fa6a5 100644 --- a/galleries/examples/specialty_plots/leftventricle_bullseye.py +++ b/galleries/examples/specialty_plots/leftventricle_bullseye.py @@ -23,7 +23,7 @@ def bullseye_plot(ax, data, seg_bold=None, cmap="viridis", norm=None): Parameters ---------- - ax : axes + ax : Axes data : list[float] The intensity values for each of the 17 segments. seg_bold : list[int], optional @@ -95,7 +95,7 @@ def bullseye_plot(ax, data, seg_bold=None, cmap="viridis", norm=None): data = np.arange(17) + 1 -# Make a figure and axes with dimensions as desired. +# Make a figure and Axes with dimensions as desired. fig = plt.figure(figsize=(10, 5), layout="constrained") fig.get_layout_engine().set(wspace=.1, w_pad=.2) axs = fig.subplots(1, 3, subplot_kw=dict(projection='polar')) diff --git a/galleries/examples/specialty_plots/radar_chart.py b/galleries/examples/specialty_plots/radar_chart.py index 3d17659d0bdf..a2f6df717544 100644 --- a/galleries/examples/specialty_plots/radar_chart.py +++ b/galleries/examples/specialty_plots/radar_chart.py @@ -9,7 +9,7 @@ frames don't have proper gridlines (the lines are circles instead of polygons). It's possible to get a polygon grid by setting GRIDLINE_INTERPOLATION_STEPS in `matplotlib.axis` to the desired number of vertices, but the orientation of the -polygon is not aligned with the radial axes. +polygon is not aligned with the radial axis. .. [1] https://en.wikipedia.org/wiki/Radar_chart """ @@ -27,7 +27,7 @@ def radar_factory(num_vars, frame='circle'): """ - Create a radar chart with `num_vars` axes. + Create a radar chart with `num_vars` Axes. This function creates a RadarAxes projection and registers it. @@ -36,7 +36,7 @@ def radar_factory(num_vars, frame='circle'): num_vars : int Number of variables for radar chart. frame : {'circle', 'polygon'} - Shape of frame surrounding axes. + Shape of frame surrounding Axes. """ # calculate evenly-spaced axis angles @@ -177,7 +177,7 @@ def example_data(): fig.subplots_adjust(wspace=0.25, hspace=0.20, top=0.85, bottom=0.05) colors = ['b', 'r', 'g', 'm', 'y'] - # Plot the four cases from the example data on separate axes + # Plot the four cases from the example data on separate Axes for ax, (title, case_data) in zip(axs.flat, data): ax.set_rgrids([0.2, 0.4, 0.6, 0.8]) ax.set_title(title, weight='bold', size='medium', position=(0.5, 1.1), diff --git a/galleries/examples/spines/centered_spines_with_arrows.py b/galleries/examples/spines/centered_spines_with_arrows.py index 972e7ebd5e4e..f71d071a5f82 100644 --- a/galleries/examples/spines/centered_spines_with_arrows.py +++ b/galleries/examples/spines/centered_spines_with_arrows.py @@ -21,7 +21,7 @@ # case, one of the coordinates (0) is a data coordinate (i.e., y = 0 or x = 0, # respectively) and the other one (1) is an axes coordinate (i.e., at the very # right/top of the axes). Also, disable clipping (clip_on=False) as the marker -# actually spills out of the axes. +# actually spills out of the Axes. ax.plot(1, 0, ">k", transform=ax.get_yaxis_transform(), clip_on=False) ax.plot(0, 1, "^k", transform=ax.get_xaxis_transform(), clip_on=False) diff --git a/galleries/examples/spines/multiple_yaxis_with_spines.py b/galleries/examples/spines/multiple_yaxis_with_spines.py index c1dc95a62612..a0281bdeda0f 100644 --- a/galleries/examples/spines/multiple_yaxis_with_spines.py +++ b/galleries/examples/spines/multiple_yaxis_with_spines.py @@ -4,11 +4,11 @@ =========================== Create multiple y axes with a shared x-axis. This is done by creating -a `~.axes.Axes.twinx` axes, turning all spines but the right one invisible +a `~.axes.Axes.twinx` Axes, turning all spines but the right one invisible and offset its position using `~.spines.Spine.set_position`. Note that this approach uses `matplotlib.axes.Axes` and their -`~matplotlib.spines.Spine`\s. Alternative approaches using non-standard axes +`~matplotlib.spines.Spine`\s. Alternative approaches using non-standard Axes are shown in the :doc:`/gallery/axisartist/demo_parasite_axes` and :doc:`/gallery/axisartist/demo_parasite_axes2` examples. """ diff --git a/galleries/examples/spines/spines.py b/galleries/examples/spines/spines.py index fd42292112fd..2e28fb546d86 100644 --- a/galleries/examples/spines/spines.py +++ b/galleries/examples/spines/spines.py @@ -21,7 +21,7 @@ x = np.linspace(0, 2 * np.pi, 100) y = 2 * np.sin(x) -# Constrained layout makes sure the labels don't overlap the axes. +# Constrained layout makes sure the labels don't overlap the Axes. fig, (ax0, ax1, ax2) = plt.subplots(nrows=3, layout='constrained') ax0.plot(x, y) diff --git a/galleries/examples/statistics/boxplot.py b/galleries/examples/statistics/boxplot.py index 419352380c76..ccdaab97d24a 100644 --- a/galleries/examples/statistics/boxplot.py +++ b/galleries/examples/statistics/boxplot.py @@ -8,7 +8,7 @@ individual components (note that the mean is the only value not shown by default). The second figure demonstrates how the styles of the artists can be customized. It also demonstrates how to set the limit of the whiskers to -specific percentiles (lower right axes) +specific percentiles (lower right Axes) A good general reference on boxplots and their history can be found here: https://vita.had.co.nz/papers/boxplots.pdf @@ -28,23 +28,23 @@ # Demonstrate how to toggle the display of different elements: fig, axs = plt.subplots(nrows=2, ncols=3, figsize=(6, 6), sharey=True) -axs[0, 0].boxplot(data, labels=labels) +axs[0, 0].boxplot(data, tick_labels=labels) axs[0, 0].set_title('Default', fontsize=fs) -axs[0, 1].boxplot(data, labels=labels, showmeans=True) +axs[0, 1].boxplot(data, tick_labels=labels, showmeans=True) axs[0, 1].set_title('showmeans=True', fontsize=fs) -axs[0, 2].boxplot(data, labels=labels, showmeans=True, meanline=True) +axs[0, 2].boxplot(data, tick_labels=labels, showmeans=True, meanline=True) axs[0, 2].set_title('showmeans=True,\nmeanline=True', fontsize=fs) -axs[1, 0].boxplot(data, labels=labels, showbox=False, showcaps=False) +axs[1, 0].boxplot(data, tick_labels=labels, showbox=False, showcaps=False) tufte_title = 'Tufte Style \n(showbox=False,\nshowcaps=False)' axs[1, 0].set_title(tufte_title, fontsize=fs) -axs[1, 1].boxplot(data, labels=labels, notch=True, bootstrap=10000) +axs[1, 1].boxplot(data, tick_labels=labels, notch=True, bootstrap=10000) axs[1, 1].set_title('notch=True,\nbootstrap=10000', fontsize=fs) -axs[1, 2].boxplot(data, labels=labels, showfliers=False) +axs[1, 2].boxplot(data, tick_labels=labels, showfliers=False) axs[1, 2].set_title('showfliers=False', fontsize=fs) for ax in axs.flat: diff --git a/galleries/examples/statistics/boxplot_color.py b/galleries/examples/statistics/boxplot_color.py index eb3273e34717..3491253aaf3e 100644 --- a/galleries/examples/statistics/boxplot_color.py +++ b/galleries/examples/statistics/boxplot_color.py @@ -3,52 +3,34 @@ Box plots with custom fill colors ================================= -This plot illustrates how to create two types of box plots -(rectangular and notched), and how to fill them with custom -colors by accessing the properties of the artists of the -box plots. Additionally, the ``labels`` parameter is used to -provide x-tick labels for each sample. - -A good general reference on boxplots and their history can be found -here: http://vita.had.co.nz/papers/boxplots.pdf +To color each box of a box plot individually: + +1) use the keyword argument ``patch_artist=True`` to create filled boxes. +2) loop through the created boxes and adapt their color. """ import matplotlib.pyplot as plt import numpy as np -# Random test data np.random.seed(19680801) -all_data = [np.random.normal(0, std, size=100) for std in range(1, 4)] -labels = ['x1', 'x2', 'x3'] - -fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(9, 4)) - -# rectangular box plot -bplot1 = ax1.boxplot(all_data, - vert=True, # vertical box alignment - patch_artist=True, # fill with color - labels=labels) # will be used to label x-ticks -ax1.set_title('Rectangular box plot') - -# notch shape box plot -bplot2 = ax2.boxplot(all_data, - notch=True, # notch shape - vert=True, # vertical box alignment - patch_artist=True, # fill with color - labels=labels) # will be used to label x-ticks -ax2.set_title('Notched box plot') +fruit_weights = [ + np.random.normal(130, 10, size=100), + np.random.normal(125, 20, size=100), + np.random.normal(120, 30, size=100), +] +labels = ['peaches', 'oranges', 'tomatoes'] +colors = ['peachpuff', 'orange', 'tomato'] + +fig, ax = plt.subplots() +ax.set_ylabel('fruit weight (g)') + +bplot = ax.boxplot(fruit_weights, + patch_artist=True, # fill with color + tick_labels=labels) # will be used to label x-ticks # fill with colors -colors = ['pink', 'lightblue', 'lightgreen'] -for bplot in (bplot1, bplot2): - for patch, color in zip(bplot['boxes'], colors): - patch.set_facecolor(color) - -# adding horizontal grid lines -for ax in [ax1, ax2]: - ax.yaxis.grid(True) - ax.set_xlabel('Three separate samples') - ax.set_ylabel('Observed values') +for patch, color in zip(bplot['boxes'], colors): + patch.set_facecolor(color) plt.show() diff --git a/galleries/examples/statistics/bxp.py b/galleries/examples/statistics/bxp.py index d78747523325..b12bfebd16cc 100644 --- a/galleries/examples/statistics/bxp.py +++ b/galleries/examples/statistics/bxp.py @@ -1,107 +1,65 @@ """ -======================= -Boxplot drawer function -======================= - -This example demonstrates how to pass pre-computed box plot -statistics to the box plot drawer. The first figure demonstrates -how to remove and add individual components (note that the -mean is the only value not shown by default). The second -figure demonstrates how the styles of the artists can -be customized. - -A good general reference on boxplots and their history can be found -here: http://vita.had.co.nz/papers/boxplots.pdf -""" - -import matplotlib.pyplot as plt -import numpy as np - -import matplotlib.cbook as cbook +============================================= +Separate calculation and plotting of boxplots +============================================= -# fake data -np.random.seed(19680801) -data = np.random.lognormal(size=(37, 4), mean=1.5, sigma=1.75) -labels = list('ABCD') - -# compute the boxplot stats -stats = cbook.boxplot_stats(data, labels=labels, bootstrap=10000) +Drawing a `~.axes.Axes.boxplot` for a given data set, consists of two main operations, +that can also be used separately: -# %% -# After we've computed the stats, we can go through and change anything. -# Just to prove it, I'll set the median of each set to the median of all -# the data, and double the means +1. Calculating the boxplot statistics: `matplotlib.cbook.boxplot_stats` +2. Drawing the boxplot: `matplotlib.axes.Axes.bxp` -for n in range(len(stats)): - stats[n]['med'] = np.median(data) - stats[n]['mean'] *= 2 +Thus, ``ax.boxplot(data)`` is equivalent to :: -print(list(stats[0])) + stats = cbook.boxplot_stats(data) + ax.bxp(stats) -fs = 10 # fontsize - -# %% -# Demonstrate how to toggle the display of different elements: +All styling keyword arguments are identical between `~.axes.Axes.boxplot` and +`~.axes.Axes.bxp`, and they are passed through from `~.axes.Axes.boxplot` to +`~.axes.Axes.bxp`. However, the *tick_labels* parameter of `~.axes.Axes.boxplot` +translates to a generic *labels* parameter in `.boxplot_stats`, because the labels are +data-related and attached to the returned per-dataset dictionaries. -fig, axs = plt.subplots(nrows=2, ncols=3, figsize=(6, 6), sharey=True) -axs[0, 0].bxp(stats) -axs[0, 0].set_title('Default', fontsize=fs) +The following code demonstrates the equivalence between the two methods. -axs[0, 1].bxp(stats, showmeans=True) -axs[0, 1].set_title('showmeans=True', fontsize=fs) +""" +# sphinx_gallery_thumbnail_number = 2 -axs[0, 2].bxp(stats, showmeans=True, meanline=True) -axs[0, 2].set_title('showmeans=True,\nmeanline=True', fontsize=fs) +import matplotlib.pyplot as plt +import numpy as np -axs[1, 0].bxp(stats, showbox=False, showcaps=False) -tufte_title = 'Tufte Style\n(showbox=False,\nshowcaps=False)' -axs[1, 0].set_title(tufte_title, fontsize=fs) +from matplotlib import cbook -axs[1, 1].bxp(stats, shownotches=True) -axs[1, 1].set_title('notch=True', fontsize=fs) +np.random.seed(19680801) +data = np.random.randn(20, 3) -axs[1, 2].bxp(stats, showfliers=False) -axs[1, 2].set_title('showfliers=False', fontsize=fs) +fig, (ax1, ax2) = plt.subplots(1, 2) -for ax in axs.flat: - ax.set_yscale('log') - ax.set_yticklabels([]) +# single boxplot call +ax1.boxplot(data, tick_labels=['A', 'B', 'C'], + patch_artist=True, boxprops={'facecolor': 'bisque'}) -fig.subplots_adjust(hspace=0.4) -plt.show() +# separate calculation of statistics and plotting +stats = cbook.boxplot_stats(data, labels=['A', 'B', 'C']) +ax2.bxp(stats, patch_artist=True, boxprops={'facecolor': 'bisque'}) # %% -# Demonstrate how to customize the display different elements: - -boxprops = dict(linestyle='--', linewidth=3, color='darkgoldenrod') -flierprops = dict(marker='o', markerfacecolor='green', markersize=12, - linestyle='none') -medianprops = dict(linestyle='-.', linewidth=2.5, color='firebrick') -meanpointprops = dict(marker='D', markeredgecolor='black', - markerfacecolor='firebrick') -meanlineprops = dict(linestyle='--', linewidth=2.5, color='purple') - -fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(6, 6), sharey=True) -axs[0, 0].bxp(stats, boxprops=boxprops) -axs[0, 0].set_title('Custom boxprops', fontsize=fs) - -axs[0, 1].bxp(stats, flierprops=flierprops, medianprops=medianprops) -axs[0, 1].set_title('Custom medianprops\nand flierprops', fontsize=fs) +# Using the separate functions allows to pre-calculate statistics, in case you need +# them explicitly for other purposes, or to reuse the statistics for multiple plots. +# +# Conversely, you can also use the `~.axes.Axes.bxp` function directly, if you already +# have the statistical parameters: -axs[1, 0].bxp(stats, meanprops=meanpointprops, meanline=False, - showmeans=True) -axs[1, 0].set_title('Custom mean\nas point', fontsize=fs) +fig, ax = plt.subplots() -axs[1, 1].bxp(stats, meanprops=meanlineprops, meanline=True, - showmeans=True) -axs[1, 1].set_title('Custom mean\nas line', fontsize=fs) +stats = [ + dict(med=0, q1=-1, q3=1, whislo=-2, whishi=2, fliers=[-4, -3, 3, 4], label='A'), + dict(med=0, q1=-2, q3=2, whislo=-3, whishi=3, fliers=[], label='B'), + dict(med=0, q1=-3, q3=3, whislo=-4, whishi=4, fliers=[], label='C'), +] -for ax in axs.flat: - ax.set_yscale('log') - ax.set_yticklabels([]) +ax.bxp(stats, patch_artist=True, boxprops={'facecolor': 'bisque'}) -fig.suptitle("I never said they'd be pretty") -fig.subplots_adjust(hspace=0.4) plt.show() # %% @@ -112,4 +70,5 @@ # in this example: # # - `matplotlib.axes.Axes.bxp` +# - `matplotlib.axes.Axes.boxplot` # - `matplotlib.cbook.boxplot_stats` diff --git a/galleries/examples/statistics/confidence_ellipse.py b/galleries/examples/statistics/confidence_ellipse.py index 2a0427127773..27b06146a44e 100644 --- a/galleries/examples/statistics/confidence_ellipse.py +++ b/galleries/examples/statistics/confidence_ellipse.py @@ -30,7 +30,7 @@ # # This function plots the confidence ellipse of the covariance of the given # array-like variables x and y. The ellipse is plotted into the given -# axes-object ax. +# Axes object *ax*. # # The radiuses of the ellipse can be controlled by n_std which is the number # of standard deviations. The default value is 3 which makes the ellipse @@ -49,7 +49,7 @@ def confidence_ellipse(x, y, ax, n_std=3.0, facecolor='none', **kwargs): Input data. ax : matplotlib.axes.Axes - The axes object to draw the ellipse into. + The Axes object to draw the ellipse into. n_std : float The number of standard deviations to determine the ellipse's radiuses. diff --git a/galleries/examples/statistics/errorbars_and_boxes.py b/galleries/examples/statistics/errorbars_and_boxes.py index c182d53a701a..54c8786096c7 100644 --- a/galleries/examples/statistics/errorbars_and_boxes.py +++ b/galleries/examples/statistics/errorbars_and_boxes.py @@ -51,7 +51,7 @@ def make_error_boxes(ax, xdata, ydata, xerror, yerror, facecolor='r', pc = PatchCollection(errorboxes, facecolor=facecolor, alpha=alpha, edgecolor=edgecolor) - # Add collection to axes + # Add collection to Axes ax.add_collection(pc) # Plot errorbars @@ -61,7 +61,7 @@ def make_error_boxes(ax, xdata, ydata, xerror, yerror, facecolor='r', return artists -# Create figure and axes +# Create figure and Axes fig, ax = plt.subplots(1) # Call function to create error boxes diff --git a/galleries/examples/statistics/hist.py b/galleries/examples/statistics/hist.py index 9e1a308153ef..8b06093913df 100644 --- a/galleries/examples/statistics/hist.py +++ b/galleries/examples/statistics/hist.py @@ -36,6 +36,8 @@ axs[0].hist(dist1, bins=n_bins) axs[1].hist(dist2, bins=n_bins) +plt.show() + # %% # Updating histogram colors @@ -99,8 +101,6 @@ # We can also define custom numbers of bins for each axis axs[2].hist2d(dist1, dist2, bins=(80, 10), norm=colors.LogNorm()) -plt.show() - # %% # # .. admonition:: References diff --git a/galleries/examples/statistics/histogram_bihistogram.py b/galleries/examples/statistics/histogram_bihistogram.py new file mode 100644 index 000000000000..7bfae14e9285 --- /dev/null +++ b/galleries/examples/statistics/histogram_bihistogram.py @@ -0,0 +1,45 @@ +""" +=========== +Bihistogram +=========== + +How to plot a bihistogram with Matplotlib. +""" + +import matplotlib.pyplot as plt +import numpy as np + +# Create a random number generator with a fixed seed for reproducibility +rng = np.random.default_rng(19680801) + +# %% +# Generate data and plot a bihistogram +# ------------------------------------ +# +# To generate a bihistogram we need two datasets (each being a vector of numbers). +# We will plot both histograms using plt.hist() and set the weights of the second +# one to be negative. We'll generate data below and plot the bihistogram. + +N_points = 10_000 + +# Generate two normal distributions +dataset1 = np.random.normal(0, 1, size=N_points) +dataset2 = np.random.normal(1, 2, size=N_points) + +# Use a constant bin width to make the two histograms easier to compare visually +bin_width = 0.25 +bins = np.arange(np.min([dataset1, dataset2]), + np.max([dataset1, dataset2]) + bin_width, bin_width) + +fig, ax = plt.subplots() + +# Plot the first histogram +ax.hist(dataset1, bins=bins, label="Dataset 1") + +# Plot the second histogram +# (notice the negative weights, which flip the histogram upside down) +ax.hist(dataset2, weights=-np.ones_like(dataset2), bins=bins, label="Dataset 2") +ax.axhline(0, color="k") +ax.legend() + +plt.show() diff --git a/galleries/examples/statistics/histogram_features.py b/galleries/examples/statistics/histogram_features.py deleted file mode 100644 index 21f74fd1ac44..000000000000 --- a/galleries/examples/statistics/histogram_features.py +++ /dev/null @@ -1,60 +0,0 @@ -""" -============================================== -Some features of the histogram (hist) function -============================================== - -In addition to the basic histogram, this demo shows a few optional features: - -* Setting the number of data bins. -* The *density* parameter, which normalizes bin heights so that the integral of - the histogram is 1. The resulting histogram is an approximation of the - probability density function. - -Selecting different bin counts and sizes can significantly affect the shape -of a histogram. The Astropy docs have a great section_ on how to select these -parameters. - -.. _section: http://docs.astropy.org/en/stable/visualization/histogram.html -""" - -import matplotlib.pyplot as plt -import numpy as np - -rng = np.random.default_rng(19680801) - -# example data -mu = 106 # mean of distribution -sigma = 17 # standard deviation of distribution -x = rng.normal(loc=mu, scale=sigma, size=420) - -num_bins = 42 - -fig, ax = plt.subplots() - -# the histogram of the data -n, bins, patches = ax.hist(x, num_bins, density=True) - -# add a 'best fit' line -y = ((1 / (np.sqrt(2 * np.pi) * sigma)) * - np.exp(-0.5 * (1 / sigma * (bins - mu))**2)) -ax.plot(bins, y, '--') -ax.set_xlabel('Value') -ax.set_ylabel('Probability density') -ax.set_title('Histogram of normal distribution sample: ' - fr'$\mu={mu:.0f}$, $\sigma={sigma:.0f}$') - -# Tweak spacing to prevent clipping of ylabel -fig.tight_layout() -plt.show() - -# %% -# -# .. admonition:: References -# -# The use of the following functions, methods, classes and modules is shown -# in this example: -# -# - `matplotlib.axes.Axes.hist` / `matplotlib.pyplot.hist` -# - `matplotlib.axes.Axes.set_title` -# - `matplotlib.axes.Axes.set_xlabel` -# - `matplotlib.axes.Axes.set_ylabel` diff --git a/galleries/examples/statistics/histogram_normalization.py b/galleries/examples/statistics/histogram_normalization.py new file mode 100644 index 000000000000..9418b7af002b --- /dev/null +++ b/galleries/examples/statistics/histogram_normalization.py @@ -0,0 +1,255 @@ +""" +.. redirect-from:: /gallery/statistics/histogram_features + +=================================== +Histogram bins, density, and weight +=================================== + +The `.Axes.hist` method can flexibly create histograms in a few different ways, +which is flexible and helpful, but can also lead to confusion. In particular, +you can: + +- bin the data as you want, either with an automatically chosen number of + bins, or with fixed bin edges, +- normalize the histogram so that its integral is one, +- and assign weights to the data points, so that each data point affects the + count in its bin differently. + +The Matplotlib ``hist`` method calls `numpy.histogram` and plots the results, +therefore users should consult the numpy documentation for a definitive guide. + +Histograms are created by defining bin edges, and taking a dataset of values +and sorting them into the bins, and counting or summing how much data is in +each bin. In this simple example, 9 numbers between 1 and 4 are sorted into 3 +bins: +""" + +import matplotlib.pyplot as plt +import numpy as np + +rng = np.random.default_rng(19680801) + +xdata = np.array([1.2, 2.3, 3.3, 3.1, 1.7, 3.4, 2.1, 1.25, 1.3]) +xbins = np.array([1, 2, 3, 4]) + +# changing the style of the histogram bars just to make it +# very clear where the boundaries of the bins are: +style = {'facecolor': 'none', 'edgecolor': 'C0', 'linewidth': 3} + +fig, ax = plt.subplots() +ax.hist(xdata, bins=xbins, **style) + +# plot the xdata locations on the x axis: +ax.plot(xdata, 0*xdata, 'd') +ax.set_ylabel('Number per bin') +ax.set_xlabel('x bins (dx=1.0)') + +# %% +# Modifying bins +# ============== +# +# Changing the bin size changes the shape of this sparse histogram, so its a +# good idea to choose bins with some care with respect to your data. Here we +# make the bins half as wide. + +xbins = np.arange(1, 4.5, 0.5) + +fig, ax = plt.subplots() +ax.hist(xdata, bins=xbins, **style) +ax.plot(xdata, 0*xdata, 'd') +ax.set_ylabel('Number per bin') +ax.set_xlabel('x bins (dx=0.5)') + +# %% +# We can also let numpy (via Matplotlib) choose the bins automatically, or +# specify a number of bins to choose automatically: + +fig, ax = plt.subplot_mosaic([['auto', 'n4']], + sharex=True, sharey=True, layout='constrained') + +ax['auto'].hist(xdata, **style) +ax['auto'].plot(xdata, 0*xdata, 'd') +ax['auto'].set_ylabel('Number per bin') +ax['auto'].set_xlabel('x bins (auto)') + +ax['n4'].hist(xdata, bins=4, **style) +ax['n4'].plot(xdata, 0*xdata, 'd') +ax['n4'].set_xlabel('x bins ("bins=4")') + +# %% +# Normalizing histograms: density and weight +# ========================================== +# +# Counts-per-bin is the default length of each bar in the histogram. However, +# we can also normalize the bar lengths as a probability density function using +# the ``density`` parameter: + +fig, ax = plt.subplots() +ax.hist(xdata, bins=xbins, density=True, **style) +ax.set_ylabel('Probability density [$V^{-1}$])') +ax.set_xlabel('x bins (dx=0.5 $V$)') + +# %% +# This normalization can be a little hard to interpret when just exploring the +# data. The value attached to each bar is divided by the total number of data +# points *and* the width of the bin, and thus the values _integrate_ to one +# when integrating across the full range of data. +# e.g. :: +# +# density = counts / (sum(counts) * np.diff(bins)) +# np.sum(density * np.diff(bins)) == 1 +# +# This normalization is how `probability density functions +# `_ are defined in +# statistics. If :math:`X` is a random variable on :math:`x`, then :math:`f_X` +# is is the probability density function if :math:`P[a`_, and also calculate the +# known probability density function: + +xdata = rng.normal(size=1000) +xpdf = np.arange(-4, 4, 0.1) +pdf = 1 / (np.sqrt(2 * np.pi)) * np.exp(-xpdf**2 / 2) + +# %% +# If we don't use ``density=True``, we need to scale the expected probability +# distribution function by both the length of the data and the width of the +# bins: + +fig, ax = plt.subplot_mosaic([['False', 'True']], layout='constrained') +dx = 0.1 +xbins = np.arange(-4, 4, dx) +ax['False'].hist(xdata, bins=xbins, density=False, histtype='step', label='Counts') + +# scale and plot the expected pdf: +ax['False'].plot(xpdf, pdf * len(xdata) * dx, label=r'$N\,f_X(x)\,\delta x$') +ax['False'].set_ylabel('Count per bin') +ax['False'].set_xlabel('x bins [V]') +ax['False'].legend() + +ax['True'].hist(xdata, bins=xbins, density=True, histtype='step', label='density') +ax['True'].plot(xpdf, pdf, label='$f_X(x)$') +ax['True'].set_ylabel('Probability density [$V^{-1}$]') +ax['True'].set_xlabel('x bins [$V$]') +ax['True'].legend() + +# %% +# One advantage of using the density is therefore that the shape and amplitude +# of the histogram does not depend on the size of the bins. Consider an +# extreme case where the bins do not have the same width. In this example, the +# bins below ``x=-1.25`` are six times wider than the rest of the bins. By +# normalizing by density, we preserve the shape of the distribution, whereas if +# we do not, then the wider bins have much higher counts than the thinner bins: + +fig, ax = plt.subplot_mosaic([['False', 'True']], layout='constrained') +dx = 0.1 +xbins = np.hstack([np.arange(-4, -1.25, 6*dx), np.arange(-1.25, 4, dx)]) +ax['False'].hist(xdata, bins=xbins, density=False, histtype='step', label='Counts') +ax['False'].plot(xpdf, pdf * len(xdata) * dx, label=r'$N\,f_X(x)\,\delta x_0$') +ax['False'].set_ylabel('Count per bin') +ax['False'].set_xlabel('x bins [V]') +ax['False'].legend() + +ax['True'].hist(xdata, bins=xbins, density=True, histtype='step', label='density') +ax['True'].plot(xpdf, pdf, label='$f_X(x)$') +ax['True'].set_ylabel('Probability density [$V^{-1}$]') +ax['True'].set_xlabel('x bins [$V$]') +ax['True'].legend() + +# %% +# Similarly, if we want to compare histograms with different bin widths, we may +# want to use ``density=True``: + +fig, ax = plt.subplot_mosaic([['False', 'True']], layout='constrained') + +# expected PDF +ax['True'].plot(xpdf, pdf, '--', label='$f_X(x)$', color='k') + +for nn, dx in enumerate([0.1, 0.4, 1.2]): + xbins = np.arange(-4, 4, dx) + # expected histogram: + ax['False'].plot(xpdf, pdf*1000*dx, '--', color=f'C{nn}') + ax['False'].hist(xdata, bins=xbins, density=False, histtype='step') + + ax['True'].hist(xdata, bins=xbins, density=True, histtype='step', label=dx) + +# Labels: +ax['False'].set_xlabel('x bins [$V$]') +ax['False'].set_ylabel('Count per bin') +ax['True'].set_ylabel('Probability density [$V^{-1}$]') +ax['True'].set_xlabel('x bins [$V$]') +ax['True'].legend(fontsize='small', title='bin width:') + +# %% +# Sometimes people want to normalize so that the sum of counts is one. This is +# analogous to a `probability mass function +# `_ for a discrete +# variable where the sum of probabilities for all the values equals one. Using +# ``hist``, we can get this normalization if we set the *weights* to 1/N. +# Note that the amplitude of this normalized histogram still depends on +# width and/or number of the bins: + +fig, ax = plt.subplots(layout='constrained', figsize=(3.5, 3)) + +for nn, dx in enumerate([0.1, 0.4, 1.2]): + xbins = np.arange(-4, 4, dx) + ax.hist(xdata, bins=xbins, weights=1/len(xdata) * np.ones(len(xdata)), + histtype='step', label=f'{dx}') +ax.set_xlabel('x bins [$V$]') +ax.set_ylabel('Bin count / N') +ax.legend(fontsize='small', title='bin width:') + +# %% +# The value of normalizing histograms is comparing two distributions that have +# different sized populations. Here we compare the distribution of ``xdata`` +# with a population of 1000, and ``xdata2`` with 100 members. + +xdata2 = rng.normal(size=100) + +fig, ax = plt.subplot_mosaic([['no_norm', 'density', 'weight']], + layout='constrained', figsize=(8, 4)) + +xbins = np.arange(-4, 4, 0.25) + +ax['no_norm'].hist(xdata, bins=xbins, histtype='step') +ax['no_norm'].hist(xdata2, bins=xbins, histtype='step') +ax['no_norm'].set_ylabel('Counts') +ax['no_norm'].set_xlabel('x bins [$V$]') +ax['no_norm'].set_title('No normalization') + +ax['density'].hist(xdata, bins=xbins, histtype='step', density=True) +ax['density'].hist(xdata2, bins=xbins, histtype='step', density=True) +ax['density'].set_ylabel('Probability density [$V^{-1}$]') +ax['density'].set_title('Density=True') +ax['density'].set_xlabel('x bins [$V$]') + +ax['weight'].hist(xdata, bins=xbins, histtype='step', + weights=1 / len(xdata) * np.ones(len(xdata)), + label='N=1000') +ax['weight'].hist(xdata2, bins=xbins, histtype='step', + weights=1 / len(xdata2) * np.ones(len(xdata2)), + label='N=100') +ax['weight'].set_xlabel('x bins [$V$]') +ax['weight'].set_ylabel('Counts / N') +ax['weight'].legend(fontsize='small') +ax['weight'].set_title('Weight = 1/N') + +plt.show() + +# %% +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.axes.Axes.hist` / `matplotlib.pyplot.hist` +# - `matplotlib.axes.Axes.set_title` +# - `matplotlib.axes.Axes.set_xlabel` +# - `matplotlib.axes.Axes.set_ylabel` +# - `matplotlib.axes.Axes.legend` diff --git a/galleries/examples/statistics/violinplot.py b/galleries/examples/statistics/violinplot.py index 8779022dd96c..afcc1c977034 100644 --- a/galleries/examples/statistics/violinplot.py +++ b/galleries/examples/statistics/violinplot.py @@ -28,55 +28,73 @@ pos = [1, 2, 4, 5, 7, 8] data = [np.random.normal(0, std, size=100) for std in pos] -fig, axs = plt.subplots(nrows=2, ncols=5, figsize=(10, 6)) +fig, axs = plt.subplots(nrows=2, ncols=6, figsize=(10, 4)) axs[0, 0].violinplot(data, pos, points=20, widths=0.3, showmeans=True, showextrema=True, showmedians=True) -axs[0, 0].set_title('Custom violinplot 1', fontsize=fs) +axs[0, 0].set_title('Custom violin 1', fontsize=fs) axs[0, 1].violinplot(data, pos, points=40, widths=0.5, showmeans=True, showextrema=True, showmedians=True, bw_method='silverman') -axs[0, 1].set_title('Custom violinplot 2', fontsize=fs) +axs[0, 1].set_title('Custom violin 2', fontsize=fs) axs[0, 2].violinplot(data, pos, points=60, widths=0.7, showmeans=True, showextrema=True, showmedians=True, bw_method=0.5) -axs[0, 2].set_title('Custom violinplot 3', fontsize=fs) +axs[0, 2].set_title('Custom violin 3', fontsize=fs) axs[0, 3].violinplot(data, pos, points=60, widths=0.7, showmeans=True, showextrema=True, showmedians=True, bw_method=0.5, quantiles=[[0.1], [], [], [0.175, 0.954], [0.75], [0.25]]) -axs[0, 3].set_title('Custom violinplot 4', fontsize=fs) +axs[0, 3].set_title('Custom violin 4', fontsize=fs) axs[0, 4].violinplot(data[-1:], pos[-1:], points=60, widths=0.7, showmeans=True, showextrema=True, showmedians=True, quantiles=[0.05, 0.1, 0.8, 0.9], bw_method=0.5) -axs[0, 4].set_title('Custom violinplot 5', fontsize=fs) +axs[0, 4].set_title('Custom violin 5', fontsize=fs) + +axs[0, 5].violinplot(data[-1:], pos[-1:], points=60, widths=0.7, + showmeans=True, showextrema=True, showmedians=True, + quantiles=[0.05, 0.1, 0.8, 0.9], bw_method=0.5, side='low') + +axs[0, 5].violinplot(data[-1:], pos[-1:], points=60, widths=0.7, + showmeans=True, showextrema=True, showmedians=True, + quantiles=[0.05, 0.1, 0.8, 0.9], bw_method=0.5, side='high') +axs[0, 5].set_title('Custom violin 6', fontsize=fs) axs[1, 0].violinplot(data, pos, points=80, vert=False, widths=0.7, showmeans=True, showextrema=True, showmedians=True) -axs[1, 0].set_title('Custom violinplot 6', fontsize=fs) +axs[1, 0].set_title('Custom violin 7', fontsize=fs) axs[1, 1].violinplot(data, pos, points=100, vert=False, widths=0.9, showmeans=True, showextrema=True, showmedians=True, bw_method='silverman') -axs[1, 1].set_title('Custom violinplot 7', fontsize=fs) +axs[1, 1].set_title('Custom violin 8', fontsize=fs) axs[1, 2].violinplot(data, pos, points=200, vert=False, widths=1.1, showmeans=True, showextrema=True, showmedians=True, bw_method=0.5) -axs[1, 2].set_title('Custom violinplot 8', fontsize=fs) +axs[1, 2].set_title('Custom violin 9', fontsize=fs) axs[1, 3].violinplot(data, pos, points=200, vert=False, widths=1.1, showmeans=True, showextrema=True, showmedians=True, quantiles=[[0.1], [], [], [0.175, 0.954], [0.75], [0.25]], bw_method=0.5) -axs[1, 3].set_title('Custom violinplot 9', fontsize=fs) +axs[1, 3].set_title('Custom violin 10', fontsize=fs) axs[1, 4].violinplot(data[-1:], pos[-1:], points=200, vert=False, widths=1.1, showmeans=True, showextrema=True, showmedians=True, quantiles=[0.05, 0.1, 0.8, 0.9], bw_method=0.5) -axs[1, 4].set_title('Custom violinplot 10', fontsize=fs) +axs[1, 4].set_title('Custom violin 11', fontsize=fs) + +axs[1, 5].violinplot(data[-1:], pos[-1:], points=200, vert=False, widths=1.1, + showmeans=True, showextrema=True, showmedians=True, + quantiles=[0.05, 0.1, 0.8, 0.9], bw_method=0.5, side='low') + +axs[1, 5].violinplot(data[-1:], pos[-1:], points=200, vert=False, widths=1.1, + showmeans=True, showextrema=True, showmedians=True, + quantiles=[0.05, 0.1, 0.8, 0.9], bw_method=0.5, side='high') +axs[1, 5].set_title('Custom violin 12', fontsize=fs) for ax in axs.flat: diff --git a/galleries/examples/subplots_axes_and_figures/align_labels_demo.py b/galleries/examples/subplots_axes_and_figures/align_labels_demo.py index 88f443ca0076..4935878ee027 100644 --- a/galleries/examples/subplots_axes_and_figures/align_labels_demo.py +++ b/galleries/examples/subplots_axes_and_figures/align_labels_demo.py @@ -1,37 +1,43 @@ """ -=============== -Aligning Labels -=============== +========================== +Aligning Labels and Titles +========================== -Aligning xlabel and ylabel using `.Figure.align_xlabels` and -`.Figure.align_ylabels` +Aligning xlabel, ylabel, and title using `.Figure.align_xlabels`, +`.Figure.align_ylabels`, and `.Figure.align_titles`. -`.Figure.align_labels` wraps these two functions. +`.Figure.align_labels` wraps the x and y label functions. Note that the xlabel "XLabel1 1" would normally be much closer to the -x-axis, and "YLabel1 0" would be much closer to the y-axis of their -respective axes. +x-axis, "YLabel0 0" would be much closer to the y-axis, and title +"Title0 0" would be much closer to the top of their respective axes. """ import matplotlib.pyplot as plt import numpy as np -import matplotlib.gridspec as gridspec +fig, axs = plt.subplots(2, 2, layout='constrained') -fig = plt.figure(tight_layout=True) -gs = gridspec.GridSpec(2, 2) - -ax = fig.add_subplot(gs[0, :]) +ax = axs[0][0] ax.plot(np.arange(0, 1e6, 1000)) -ax.set_ylabel('YLabel0') -ax.set_xlabel('XLabel0') +ax.set_title('Title0 0') +ax.set_ylabel('YLabel0 0') + +ax = axs[0][1] +ax.plot(np.arange(1., 0., -0.1) * 2000., np.arange(1., 0., -0.1)) +ax.set_title('Title0 1') +ax.xaxis.tick_top() +ax.tick_params(axis='x', rotation=55) + for i in range(2): - ax = fig.add_subplot(gs[1, i]) + ax = axs[1][i] ax.plot(np.arange(1., 0., -0.1) * 2000., np.arange(1., 0., -0.1)) ax.set_ylabel('YLabel1 %d' % i) ax.set_xlabel('XLabel1 %d' % i) if i == 0: ax.tick_params(axis='x', rotation=55) + fig.align_labels() # same as fig.align_xlabels(); fig.align_ylabels() +fig.align_titles() plt.show() diff --git a/galleries/examples/subplots_axes_and_figures/auto_subplots_adjust.py b/galleries/examples/subplots_axes_and_figures/auto_subplots_adjust.py index 3bd0d3f2bec1..e0a8c76a0e61 100644 --- a/galleries/examples/subplots_axes_and_figures/auto_subplots_adjust.py +++ b/galleries/examples/subplots_axes_and_figures/auto_subplots_adjust.py @@ -83,5 +83,5 @@ def on_draw(event): # - `matplotlib.transforms.BboxBase.union` # - `matplotlib.transforms.Transform.inverted` # - `matplotlib.figure.Figure.subplots_adjust` -# - `matplotlib.figure.SubplotParams` +# - `matplotlib.gridspec.SubplotParams` # - `matplotlib.backend_bases.FigureCanvasBase.mpl_connect` diff --git a/galleries/examples/subplots_axes_and_figures/axes_box_aspect.py b/galleries/examples/subplots_axes_and_figures/axes_box_aspect.py index 26974dd49121..74b64f72c466 100644 --- a/galleries/examples/subplots_axes_and_figures/axes_box_aspect.py +++ b/galleries/examples/subplots_axes_and_figures/axes_box_aspect.py @@ -4,8 +4,8 @@ =============== This demo shows how to set the aspect of an Axes box directly via -`~.Axes.set_box_aspect`. The box aspect is the ratio between axes height -and axes width in physical units, independent of the data limits. +`~.Axes.set_box_aspect`. The box aspect is the ratio between Axes height +and Axes width in physical units, independent of the data limits. This is useful to e.g. produce a square plot, independent of the data it contains, or to have a usual plot with the same axes dimensions next to an image plot with fixed (data-)aspect. @@ -14,10 +14,10 @@ """ # %% -# A square axes, independent of data +# A square Axes, independent of data # ---------------------------------- # -# Produce a square axes, no matter what the data limits are. +# Produce a square Axes, no matter what the data limits are. import matplotlib.pyplot as plt import numpy as np @@ -30,7 +30,7 @@ plt.show() # %% -# Shared square axes +# Shared square Axes # ------------------ # # Produce shared subplots that are squared in size. @@ -46,10 +46,10 @@ plt.show() # %% -# Square twin axes +# Square twin Axes # ---------------- # -# Produce a square axes, with a twin axes. The twinned axes takes over the +# Produce a square Axes, with a twin Axes. The twinned Axes takes over the # box aspect of the parent. # @@ -70,9 +70,9 @@ # ------------------------- # # When creating an image plot with fixed data aspect and the default -# ``adjustable="box"`` next to a normal plot, the axes would be unequal in +# ``adjustable="box"`` next to a normal plot, the Axes would be unequal in # height. `~.Axes.set_box_aspect` provides an easy solution to that by allowing -# to have the normal plot's axes use the images dimensions as box aspect. +# to have the normal plot's Axes use the images dimensions as box aspect. # # This example also shows that *constrained layout* interplays nicely with # a fixed box aspect. @@ -94,8 +94,8 @@ # # It may be desirable to show marginal distributions next to a plot of joint # data. The following creates a square plot with the box aspect of the -# marginal axes being equal to the width- and height-ratios of the gridspec. -# This ensures that all axes align perfectly, independent on the size of the +# marginal Axes being equal to the width- and height-ratios of the gridspec. +# This ensures that all Axes align perfectly, independent on the size of the # figure. fig5, axs = plt.subplots(2, 2, sharex="col", sharey="row", diff --git a/galleries/examples/subplots_axes_and_figures/axes_demo.py b/galleries/examples/subplots_axes_and_figures/axes_demo.py index 46a353d7475b..f5620a9a980d 100644 --- a/galleries/examples/subplots_axes_and_figures/axes_demo.py +++ b/galleries/examples/subplots_axes_and_figures/axes_demo.py @@ -3,7 +3,7 @@ Axes Demo ========= -Example use of ``fig.add_axes`` to create inset axes within the main plot axes. +Example use of ``fig.add_axes`` to create inset Axes within the main plot Axes. Please see also the :ref:`axes_grid_examples` section, and the following three examples: @@ -32,12 +32,12 @@ main_ax.set_ylabel('current (nA)') main_ax.set_title('Gaussian colored noise') -# this is an inset axes over the main axes +# this is an inset Axes over the main Axes right_inset_ax = fig.add_axes([.65, .6, .2, .2], facecolor='k') right_inset_ax.hist(s, 400, density=True) right_inset_ax.set(title='Probability', xticks=[], yticks=[]) -# this is another inset axes over the main axes +# this is another inset Axes over the main Axes left_inset_ax = fig.add_axes([.2, .6, .2, .2], facecolor='k') left_inset_ax.plot(t[:len(r)], r) left_inset_ax.set(title='Impulse response', xlim=(0, .2), xticks=[], yticks=[]) diff --git a/galleries/examples/subplots_axes_and_figures/axes_margins.py b/galleries/examples/subplots_axes_and_figures/axes_margins.py index 934e5ac6366b..dd113c8c34e0 100644 --- a/galleries/examples/subplots_axes_and_figures/axes_margins.py +++ b/galleries/examples/subplots_axes_and_figures/axes_margins.py @@ -58,7 +58,7 @@ def f(t): ] fig, (ax1, ax2) = plt.subplots(ncols=2) -# Here we set the stickiness of the axes object... +# Here we set the stickiness of the Axes object... # ax1 we'll leave as the default, which uses sticky edges # and we'll turn off stickiness for ax2 ax2.use_sticky_edges = False diff --git a/galleries/examples/subplots_axes_and_figures/axes_zoom_effect.py b/galleries/examples/subplots_axes_and_figures/axes_zoom_effect.py index a8076db48528..49a44b9e4f43 100644 --- a/galleries/examples/subplots_axes_and_figures/axes_zoom_effect.py +++ b/galleries/examples/subplots_axes_and_figures/axes_zoom_effect.py @@ -42,17 +42,17 @@ def connect_bbox(bbox1, bbox2, def zoom_effect01(ax1, ax2, xmin, xmax, **kwargs): """ - Connect *ax1* and *ax2*. The *xmin*-to-*xmax* range in both axes will + Connect *ax1* and *ax2*. The *xmin*-to-*xmax* range in both Axes will be marked. Parameters ---------- ax1 - The main axes. + The main Axes. ax2 - The zoomed axes. + The zoomed Axes. xmin, xmax - The limits of the colored area in both plot axes. + The limits of the colored area in both plot Axes. **kwargs Arguments passed to the patch constructor. """ @@ -80,8 +80,8 @@ def zoom_effect01(ax1, ax2, xmin, xmax, **kwargs): def zoom_effect02(ax1, ax2, **kwargs): """ - ax1 : the main axes - ax1 : the zoomed axes + ax1 : the main Axes + ax1 : the zoomed Axes Similar to zoom_effect01. The xmin & xmax will be taken from the ax1.viewLim. diff --git a/galleries/examples/subplots_axes_and_figures/axhspan_demo.py b/galleries/examples/subplots_axes_and_figures/axhspan_demo.py index bc1d7bff154b..e297f4adf462 100644 --- a/galleries/examples/subplots_axes_and_figures/axhspan_demo.py +++ b/galleries/examples/subplots_axes_and_figures/axhspan_demo.py @@ -3,8 +3,8 @@ axhspan Demo ============ -Create lines or rectangles that span the axes in either the horizontal or -vertical direction, and lines than span the axes with an arbitrary orientation. +Create lines or rectangles that span the Axes in either the horizontal or +vertical direction, and lines than span the Axes with an arbitrary orientation. """ import matplotlib.pyplot as plt @@ -24,13 +24,13 @@ ax.axvline(x=1) # Thick blue vertical line at x=0 that spans the upper quadrant of the yrange. ax.axvline(x=0, ymin=0.75, linewidth=8, color='#1f77b4') -# Default hline at y=.5 that spans the middle half of the axes. +# Default hline at y=.5 that spans the middle half of the Axes. ax.axhline(y=.5, xmin=0.25, xmax=0.75) # Infinite black line going through (0, 0) to (1, 1). ax.axline((0, 0), (1, 1), color='k') -# 50%-gray rectangle spanning the axes' width from y=0.25 to y=0.75. +# 50%-gray rectangle spanning the Axes' width from y=0.25 to y=0.75. ax.axhspan(0.25, 0.75, facecolor='0.5') -# Green rectangle spanning the axes' height from x=1.25 to x=1.55. +# Green rectangle spanning the Axes' height from x=1.25 to x=1.55. ax.axvspan(1.25, 1.55, facecolor='#2ca02c') plt.show() diff --git a/galleries/examples/subplots_axes_and_figures/broken_axis.py b/galleries/examples/subplots_axes_and_figures/broken_axis.py index 343fe5d4865b..06263b9c120a 100644 --- a/galleries/examples/subplots_axes_and_figures/broken_axis.py +++ b/galleries/examples/subplots_axes_and_figures/broken_axis.py @@ -20,9 +20,9 @@ # into two portions - use the top (ax1) for the outliers, and the bottom # (ax2) for the details of the majority of our data fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True) -fig.subplots_adjust(hspace=0.05) # adjust space between axes +fig.subplots_adjust(hspace=0.05) # adjust space between Axes -# plot the same data on both axes +# plot the same data on both Axes ax1.plot(pts) ax2.plot(pts) @@ -39,9 +39,9 @@ # Now, let's turn towards the cut-out slanted lines. # We create line objects in axes coordinates, in which (0,0), (0,1), -# (1,0), and (1,1) are the four corners of the axes. +# (1,0), and (1,1) are the four corners of the Axes. # The slanted lines themselves are markers at those locations, such that the -# lines keep their angle and position, independent of the axes size or scale +# lines keep their angle and position, independent of the Axes size or scale # Finally, we need to disable clipping. d = .5 # proportion of vertical to horizontal extent of the slanted line diff --git a/galleries/examples/subplots_axes_and_figures/demo_constrained_layout.py b/galleries/examples/subplots_axes_and_figures/demo_constrained_layout.py index f16f419ece7a..9a67541e554e 100644 --- a/galleries/examples/subplots_axes_and_figures/demo_constrained_layout.py +++ b/galleries/examples/subplots_axes_and_figures/demo_constrained_layout.py @@ -1,11 +1,11 @@ """ ===================================== -Resizing axes with constrained layout +Resizing Axes with constrained layout ===================================== *Constrained layout* attempts to resize subplots in -a figure so that there are no overlaps between axes objects and labels -on the axes. +a figure so that there are no overlaps between Axes objects and labels +on the Axes. See :ref:`constrainedlayout_guide` for more details and :ref:`tight_layout_guide` for an alternative. @@ -23,7 +23,7 @@ def example_plot(ax): # %% -# If we don't use *constrained layout*, then labels overlap the axes +# If we don't use *constrained layout*, then labels overlap the Axes fig, axs = plt.subplots(nrows=2, ncols=2, layout=None) diff --git a/galleries/examples/subplots_axes_and_figures/demo_tight_layout.py b/galleries/examples/subplots_axes_and_figures/demo_tight_layout.py index 0cc5f5301db4..7ac3a7376d67 100644 --- a/galleries/examples/subplots_axes_and_figures/demo_tight_layout.py +++ b/galleries/examples/subplots_axes_and_figures/demo_tight_layout.py @@ -1,10 +1,10 @@ """ =============================== -Resizing axes with tight layout +Resizing Axes with tight layout =============================== `~.Figure.tight_layout` attempts to resize subplots in a figure so that there -are no overlaps between axes objects and labels on the axes. +are no overlaps between Axes objects and labels on the Axes. See :ref:`tight_layout_guide` for more details and :ref:`constrainedlayout_guide` for an alternative. diff --git a/galleries/examples/subplots_axes_and_figures/fahrenheit_celsius_scales.py b/galleries/examples/subplots_axes_and_figures/fahrenheit_celsius_scales.py index 07b447c97521..216641657b06 100644 --- a/galleries/examples/subplots_axes_and_figures/fahrenheit_celsius_scales.py +++ b/galleries/examples/subplots_axes_and_figures/fahrenheit_celsius_scales.py @@ -1,6 +1,6 @@ """ ================================= -Different scales on the same axes +Different scales on the same Axes ================================= Demo of how to display two scales on the left and right y-axis. diff --git a/galleries/examples/subplots_axes_and_figures/figure_title.py b/galleries/examples/subplots_axes_and_figures/figure_title.py index bc8a90366d8e..85e5044c4eba 100644 --- a/galleries/examples/subplots_axes_and_figures/figure_title.py +++ b/galleries/examples/subplots_axes_and_figures/figure_title.py @@ -3,12 +3,12 @@ Figure labels: suptitle, supxlabel, supylabel ============================================= -Each axes can have a title (or actually three - one each with *loc* "left", +Each Axes can have a title (or actually three - one each with *loc* "left", "center", and "right"), but is sometimes desirable to give a whole figure -(or `.SubFigure`) an overall title, using `.FigureBase.suptitle`. +(or `.SubFigure`) an overall title, using `.Figure.suptitle`. -We can also add figure-level x- and y-labels using `.FigureBase.supxlabel` and -`.FigureBase.supylabel`. +We can also add figure-level x- and y-labels using `.Figure.supxlabel` and +`.Figure.supylabel`. """ import matplotlib.pyplot as plt @@ -31,8 +31,8 @@ fig.suptitle('Different types of oscillations', fontsize=16) # %% -# A global x- or y-label can be set using the `.FigureBase.supxlabel` and -# `.FigureBase.supylabel` methods. +# A global x- or y-label can be set using the `.Figure.supxlabel` and +# `.Figure.supylabel` methods. with get_sample_data('Stocks.csv') as file: diff --git a/galleries/examples/subplots_axes_and_figures/ganged_plots.py b/galleries/examples/subplots_axes_and_figures/ganged_plots.py index b32d2aed83d6..e25bb16a15e5 100644 --- a/galleries/examples/subplots_axes_and_figures/ganged_plots.py +++ b/galleries/examples/subplots_axes_and_figures/ganged_plots.py @@ -21,7 +21,7 @@ s3 = s1 * s2 fig, axs = plt.subplots(3, 1, sharex=True) -# Remove vertical space between axes +# Remove vertical space between Axes fig.subplots_adjust(hspace=0) # Plot each graph, and manually set the y tick values diff --git a/galleries/examples/subplots_axes_and_figures/gridspec_and_subplots.py b/galleries/examples/subplots_axes_and_figures/gridspec_and_subplots.py index bff3ea579e9e..0535a7afdde4 100644 --- a/galleries/examples/subplots_axes_and_figures/gridspec_and_subplots.py +++ b/galleries/examples/subplots_axes_and_figures/gridspec_and_subplots.py @@ -3,12 +3,12 @@ Combining two subplots using subplots and GridSpec ================================================== -Sometimes we want to combine two subplots in an axes layout created with -`~.Figure.subplots`. We can get the `~.gridspec.GridSpec` from the axes -and then remove the covered axes and fill the gap with a new bigger axes. -Here we create a layout with the bottom two axes in the last column combined. +Sometimes we want to combine two subplots in an Axes layout created with +`~.Figure.subplots`. We can get the `~.gridspec.GridSpec` from the Axes +and then remove the covered Axes and fill the gap with a new bigger Axes. +Here we create a layout with the bottom two Axes in the last column combined. -To start with this layout (rather than removing the overlapping axes) use +To start with this layout (rather than removing the overlapping Axes) use `~.pyplot.subplot_mosaic`. See also :ref:`arranging_axes`. @@ -18,7 +18,7 @@ fig, axs = plt.subplots(ncols=3, nrows=3) gs = axs[1, 2].get_gridspec() -# remove the underlying axes +# remove the underlying Axes for ax in axs[1:, -1]: ax.remove() axbig = fig.add_subplot(gs[1:, -1]) diff --git a/galleries/examples/subplots_axes_and_figures/gridspec_nested.py b/galleries/examples/subplots_axes_and_figures/gridspec_nested.py index a2750a0ecb49..bfcb90cdfc4a 100644 --- a/galleries/examples/subplots_axes_and_figures/gridspec_nested.py +++ b/galleries/examples/subplots_axes_and_figures/gridspec_nested.py @@ -7,7 +7,7 @@ set the position for a nested grid of subplots. Note that the same functionality can be achieved more directly with -`~.FigureBase.subfigures`; see +`~.Figure.subfigures`; see :doc:`/gallery/subplots_axes_and_figures/subfigures`. """ diff --git a/galleries/examples/subplots_axes_and_figures/invert_axes.py b/galleries/examples/subplots_axes_and_figures/invert_axes.py index 15ec55d430bd..31f4d75680ce 100644 --- a/galleries/examples/subplots_axes_and_figures/invert_axes.py +++ b/galleries/examples/subplots_axes_and_figures/invert_axes.py @@ -1,25 +1,35 @@ """ -=========== -Invert Axes -=========== +============= +Inverted axis +============= -You can use decreasing axes by flipping the normal order of the axis -limits +This example demonstrates two ways to invert the direction of an axis: + +- If you want to set *explicit axis limits* anyway, e.g. via `~.Axes.set_xlim`, you + can swap the limit values: ``set_xlim(4, 0)`` instead of ``set_xlim(0, 4)``. +- Use `.Axis.set_inverted` if you only want to invert the axis *without modifying + the limits*, i.e. keep existing limits or existing autoscaling behavior. """ import matplotlib.pyplot as plt import numpy as np -t = np.arange(0.01, 5.0, 0.01) -s = np.exp(-t) +x = np.arange(0.01, 4.0, 0.01) +y = np.exp(-x) + +fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(6.4, 4), layout="constrained") +fig.suptitle('Inverted axis with ...') -fig, ax = plt.subplots() +ax1.plot(x, y) +ax1.set_xlim(4, 0) # inverted fixed limits +ax1.set_title('fixed limits: set_xlim(4, 0)') +ax1.set_xlabel('decreasing x ⟶') +ax1.grid(True) -ax.plot(t, s) -ax.set_xlim(5, 0) # decreasing time -ax.set_xlabel('decreasing time (s)') -ax.set_ylabel('voltage (mV)') -ax.set_title('Should be growing...') -ax.grid(True) +ax2.plot(x, y) +ax2.xaxis.set_inverted(True) # inverted axis with autoscaling +ax2.set_title('autoscaling: set_inverted(True)') +ax2.set_xlabel('decreasing x ⟶') +ax2.grid(True) plt.show() diff --git a/galleries/examples/subplots_axes_and_figures/multiple_figs_demo.py b/galleries/examples/subplots_axes_and_figures/multiple_figs_demo.py index 9bb9962c8e28..d6b6a5ed48c6 100644 --- a/galleries/examples/subplots_axes_and_figures/multiple_figs_demo.py +++ b/galleries/examples/subplots_axes_and_figures/multiple_figs_demo.py @@ -3,7 +3,7 @@ Managing multiple figures in pyplot =================================== -`matplotlib.pyplot` uses the concept of a *current figure* and *current axes*. +`matplotlib.pyplot` uses the concept of a *current figure* and *current Axes*. Figures are identified via a figure number that is passed to `~.pyplot.figure`. The figure with the given number is set as *current figure*. Additionally, if no figure with the number exists, a new one is created. @@ -49,3 +49,6 @@ ax.set_xticklabels([]) plt.show() + +# %% +# .. tags:: component: figure, plot-type: line diff --git a/galleries/examples/subplots_axes_and_figures/secondary_axis.py b/galleries/examples/subplots_axes_and_figures/secondary_axis.py index 27c64247a56f..ab42d3a6c182 100644 --- a/galleries/examples/subplots_axes_and_figures/secondary_axis.py +++ b/galleries/examples/subplots_axes_and_figures/secondary_axis.py @@ -40,6 +40,25 @@ def rad2deg(x): secax.set_xlabel('angle [rad]') plt.show() +# %% +# By default, the secondary axis is drawn in the Axes coordinate space. +# We can also provide a custom transform to place it in a different +# coordinate space. Here we put the axis at Y = 0 in data coordinates. + +fig, ax = plt.subplots(layout='constrained') +x = np.arange(0, 10) +np.random.seed(19680801) +y = np.random.randn(len(x)) +ax.plot(x, y) +ax.set_xlabel('X') +ax.set_ylabel('Y') +ax.set_title('Random data') + +# Pass ax.transData as a transform to place the axis relative to our data +secax = ax.secondary_xaxis(0, transform=ax.transData) +secax.set_xlabel('Axis at Y = 0') +plt.show() + # %% # Here is the case of converting from wavenumber to wavelength in a # log-log scale. @@ -135,7 +154,7 @@ def inverse(x): ax.plot(dates, temperature) ax.set_ylabel(r'$T\ [^oC]$') -plt.xticks(rotation=70) +ax.xaxis.set_tick_params(rotation=70) def date2yday(x): diff --git a/galleries/examples/subplots_axes_and_figures/share_axis_lims_views.py b/galleries/examples/subplots_axes_and_figures/share_axis_lims_views.py index 751f7effc6a1..234a15660f2d 100644 --- a/galleries/examples/subplots_axes_and_figures/share_axis_lims_views.py +++ b/galleries/examples/subplots_axes_and_figures/share_axis_lims_views.py @@ -6,7 +6,7 @@ with time as a common axis. When you pan and zoom around on one, you want the other to move around with you. To facilitate this, matplotlib Axes support a ``sharex`` and ``sharey`` attribute. When you create a `~.pyplot.subplot` or -`~.pyplot.axes`, you can pass in a keyword indicating what axes you want to +`~.pyplot.axes`, you can pass in a keyword indicating what Axes you want to share with. """ diff --git a/galleries/examples/subplots_axes_and_figures/shared_axis_demo.py b/galleries/examples/subplots_axes_and_figures/shared_axis_demo.py index cfe2d68701f0..6b3b3839a437 100644 --- a/galleries/examples/subplots_axes_and_figures/shared_axis_demo.py +++ b/galleries/examples/subplots_axes_and_figures/shared_axis_demo.py @@ -6,7 +6,7 @@ You can share the x- or y-axis limits for one axis with another by passing an `~.axes.Axes` instance as a *sharex* or *sharey* keyword argument. -Changing the axis limits on one axes will be reflected automatically +Changing the axis limits on one Axes will be reflected automatically in the other, and vice-versa, so when you navigate with the toolbar the Axes will follow each other on their shared axis. Ditto for changes in the axis scaling (e.g., log vs. linear). However, it is diff --git a/galleries/examples/subplots_axes_and_figures/subfigures.py b/galleries/examples/subplots_axes_and_figures/subfigures.py index 9141ab61abef..6272de975c4d 100644 --- a/galleries/examples/subplots_axes_and_figures/subfigures.py +++ b/galleries/examples/subplots_axes_and_figures/subfigures.py @@ -69,7 +69,7 @@ def example_plot(ax, fontsize=12, hide_labels=False): for a in axs[:, 0]: a.remove() -# plot data in remaining axes: +# plot data in remaining Axes: for a in axs[:, 1:].flat: a.plot(np.arange(10)) diff --git a/galleries/examples/subplots_axes_and_figures/subplots_demo.py b/galleries/examples/subplots_axes_and_figures/subplots_demo.py index 2e2dc3681cde..229ecd34cc9f 100644 --- a/galleries/examples/subplots_axes_and_figures/subplots_demo.py +++ b/galleries/examples/subplots_axes_and_figures/subplots_demo.py @@ -181,7 +181,7 @@ # %% # If you want a more complex sharing structure, you can first create the -# grid of axes with no sharing, and then call `.axes.Axes.sharex` or +# grid of Axes with no sharing, and then call `.axes.Axes.sharex` or # `.axes.Axes.sharey` to add sharing info a posteriori. fig, axs = plt.subplots(2, 2) @@ -197,7 +197,7 @@ fig.tight_layout() # %% -# Polar axes +# Polar Axes # """""""""" # # The parameter *subplot_kw* of `.pyplot.subplots` controls the subplot diff --git a/galleries/examples/subplots_axes_and_figures/two_scales.py b/galleries/examples/subplots_axes_and_figures/two_scales.py index 857c22d5094d..249a65fd64fe 100644 --- a/galleries/examples/subplots_axes_and_figures/two_scales.py +++ b/galleries/examples/subplots_axes_and_figures/two_scales.py @@ -3,14 +3,14 @@ Plots with different scales =========================== -Two plots on the same axes with different left and right scales. +Two plots on the same Axes with different left and right scales. -The trick is to use *two different axes* that share the same *x* axis. +The trick is to use *two different Axes* that share the same *x* axis. You can use separate `matplotlib.ticker` formatters and locators as -desired since the two axes are independent. +desired since the two Axes are independent. -Such axes are generated by calling the `.Axes.twinx` method. Likewise, -`.Axes.twiny` is available to generate axes that share a *y* axis but +Such Axes are generated by calling the `.Axes.twinx` method. Likewise, +`.Axes.twiny` is available to generate Axes that share a *y* axis but have different top and bottom scales. """ import matplotlib.pyplot as plt @@ -29,7 +29,7 @@ ax1.plot(t, data1, color=color) ax1.tick_params(axis='y', labelcolor=color) -ax2 = ax1.twinx() # instantiate a second axes that shares the same x-axis +ax2 = ax1.twinx() # instantiate a second Axes that shares the same x-axis color = 'tab:blue' ax2.set_ylabel('sin', color=color) # we already handled the x-label with ax1 diff --git a/galleries/examples/subplots_axes_and_figures/zoom_inset_axes.py b/galleries/examples/subplots_axes_and_figures/zoom_inset_axes.py index 0d03db15b434..4cbd9875e4bc 100644 --- a/galleries/examples/subplots_axes_and_figures/zoom_inset_axes.py +++ b/galleries/examples/subplots_axes_and_figures/zoom_inset_axes.py @@ -1,9 +1,9 @@ """ ====================== -Zoom region inset axes +Zoom region inset Axes ====================== -Example of an inset axes and a rectangle showing where the zoom is located. +Example of an inset Axes and a rectangle showing where the zoom is located. """ import numpy as np @@ -22,7 +22,7 @@ ax.imshow(Z2, extent=extent, origin="lower") -# inset axes.... +# inset Axes.... x1, x2, y1, y2 = -1.5, -0.9, -2.5, -1.9 # subregion of the original image axins = ax.inset_axes( [0.5, 0.5, 0.47, 0.47], diff --git a/galleries/examples/text_labels_and_annotations/align_ylabels.py b/galleries/examples/text_labels_and_annotations/align_ylabels.py index 7e6e4152da7f..168f5f6d8c19 100644 --- a/galleries/examples/text_labels_and_annotations/align_ylabels.py +++ b/galleries/examples/text_labels_and_annotations/align_ylabels.py @@ -43,7 +43,7 @@ def make_plot(axs): fig.subplots_adjust(left=0.2, wspace=0.6) make_plot(axs) -# just align the last column of axes: +# just align the last column of Axes: fig.align_ylabels(axs[:, 1]) plt.show() diff --git a/galleries/examples/text_labels_and_annotations/annotation_demo.py b/galleries/examples/text_labels_and_annotations/annotation_demo.py index 26f3b80bf203..5358bfaac60a 100644 --- a/galleries/examples/text_labels_and_annotations/annotation_demo.py +++ b/galleries/examples/text_labels_and_annotations/annotation_demo.py @@ -29,15 +29,15 @@ # 'figure points' : points from the lower left corner of the figure # 'figure pixels' : pixels from the lower left corner of the figure # 'figure fraction' : (0, 0) is lower left of figure and (1, 1) is upper right -# 'axes points' : points from lower left corner of axes -# 'axes pixels' : pixels from lower left corner of axes -# 'axes fraction' : (0, 0) is lower left of axes and (1, 1) is upper right +# 'axes points' : points from lower left corner of the Axes +# 'axes pixels' : pixels from lower left corner of the Axes +# 'axes fraction' : (0, 0) is lower left of Axes and (1, 1) is upper right # 'offset points' : Specify an offset (in points) from the xy value # 'offset pixels' : Specify an offset (in pixels) from the xy value -# 'data' : use the axes data coordinate system +# 'data' : use the Axes data coordinate system # # Note: for physical coordinate systems (points or pixels) the origin is the -# (bottom, left) of the figure or axes. +# (bottom, left) of the figure or Axes. # # Optionally, you can specify arrow properties which draws and arrow # from the text to the annotated point by giving a dictionary of arrow @@ -85,7 +85,7 @@ horizontalalignment='right', verticalalignment='top') # You may also use negative points or pixels to specify from (right, top). -# E.g., (-10, 10) is 10 points to the left of the right side of the axes and 10 +# E.g., (-10, 10) is 10 points to the left of the right side of the Axes and 10 # points above the bottom ax.annotate('pixel offset from axes fraction', @@ -103,10 +103,10 @@ # # You can specify the *xypoint* and the *xytext* in different positions and # coordinate systems, and optionally turn on a connecting line and mark the -# point with a marker. Annotations work on polar axes too. +# point with a marker. Annotations work on polar Axes too. # # In the example below, the *xy* point is in native coordinates (*xycoords* -# defaults to 'data'). For a polar axes, this is in (theta, radius) space. +# defaults to 'data'). For a polar Axes, this is in (theta, radius) space. # The text in the example is placed in the fractional figure coordinate system. # Text keyword arguments like horizontal and vertical alignment are respected. @@ -127,7 +127,7 @@ verticalalignment='bottom') # %% -# You can also use polar notation on a cartesian axes. Here the native +# You can also use polar notation on a cartesian Axes. Here the native # coordinate system ('data') is cartesian, so you need to specify the # xycoords and textcoords as 'polar' if you want to use (theta, radius). @@ -144,7 +144,7 @@ arrowprops=dict(facecolor='black', shrink=0.05), horizontalalignment='left', verticalalignment='bottom', - clip_on=True) # clip to the axes bounding box + clip_on=True) # clip to the Axes bounding box ax.set(xlim=[-20, 20], ylim=[-20, 20]) diff --git a/galleries/examples/text_labels_and_annotations/arrow_demo.py b/galleries/examples/text_labels_and_annotations/arrow_demo.py index 9607818181dc..11c6c3ec0e5d 100644 --- a/galleries/examples/text_labels_and_annotations/arrow_demo.py +++ b/galleries/examples/text_labels_and_annotations/arrow_demo.py @@ -23,7 +23,7 @@ def make_arrow_graph(ax, data, size=4, display='length', shape='right', Parameters ---------- ax - The axes where the graph is drawn. + The Axes where the graph is drawn. data Dict with probabilities for the bases and pair transitions. size diff --git a/galleries/examples/text_labels_and_annotations/custom_legends.py b/galleries/examples/text_labels_and_annotations/custom_legends.py index f1714a21a481..18ace0513228 100644 --- a/galleries/examples/text_labels_and_annotations/custom_legends.py +++ b/galleries/examples/text_labels_and_annotations/custom_legends.py @@ -28,6 +28,7 @@ # Fixing random state for reproducibility np.random.seed(19680801) +# %% N = 10 data = (np.geomspace(1, 10, 100) + np.random.randn(N, 100)).T cmap = plt.cm.coolwarm @@ -35,10 +36,10 @@ fig, ax = plt.subplots() lines = ax.plot(data) -ax.legend() # %% -# Note that no legend entries were created. +# Since the data does not have any labels, creating a legend requires +# us to define the icons and labels. # In this case, we can compose a legend using Matplotlib objects that aren't # explicitly tied to the data that was plotted. For example: diff --git a/galleries/examples/text_labels_and_annotations/demo_text_rotation_mode.py b/galleries/examples/text_labels_and_annotations/demo_text_rotation_mode.py index a571a07edf49..f8f3a108629c 100644 --- a/galleries/examples/text_labels_and_annotations/demo_text_rotation_mode.py +++ b/galleries/examples/text_labels_and_annotations/demo_text_rotation_mode.py @@ -7,7 +7,7 @@ of rotated text. Rotated `.Text`\s are created by passing the parameter ``rotation`` to -the constructor or the axes' method `~.axes.Axes.text`. +the constructor or the Axes' method `~.axes.Axes.text`. The actual positioning depends on the additional parameters ``horizontalalignment``, ``verticalalignment`` and ``rotation_mode``. @@ -45,11 +45,11 @@ def test_rotation_mode(fig, mode): texts = {} - # use a different text alignment in each axes + # use a different text alignment in each Axes for i, va in enumerate(va_list): for j, ha in enumerate(ha_list): ax = axs[i, j] - # prepare axes layout + # prepare Axes layout ax.set(xticks=[], yticks=[]) ax.axvline(0.5, color="skyblue", zorder=0) ax.axhline(0.5, color="skyblue", zorder=0) diff --git a/galleries/examples/text_labels_and_annotations/figlegend_demo.py b/galleries/examples/text_labels_and_annotations/figlegend_demo.py index 9c19418ae261..f6f74b837c10 100644 --- a/galleries/examples/text_labels_and_annotations/figlegend_demo.py +++ b/galleries/examples/text_labels_and_annotations/figlegend_demo.py @@ -30,7 +30,7 @@ plt.show() # %% -# Sometimes we do not want the legend to overlap the axes. If you use +# Sometimes we do not want the legend to overlap the Axes. If you use # *constrained layout* you can specify "outside right upper", and # *constrained layout* will make room for the legend. diff --git a/galleries/examples/text_labels_and_annotations/label_subplots.py b/galleries/examples/text_labels_and_annotations/label_subplots.py index fd66d46a5b52..aa7b94cb74fe 100644 --- a/galleries/examples/text_labels_and_annotations/label_subplots.py +++ b/galleries/examples/text_labels_and_annotations/label_subplots.py @@ -3,46 +3,51 @@ Labelling subplots ================== -Labelling subplots is relatively straightforward, and varies, -so Matplotlib does not have a general method for doing this. +Labelling subplots is relatively straightforward, and varies, so Matplotlib +does not have a general method for doing this. -Simplest is putting the label inside the axes. Note, here -we use `.pyplot.subplot_mosaic`, and use the subplot labels -as keys for the subplots, which is a nice convenience. However, -the same method works with `.pyplot.subplots` or keys that are -different than what you want to label the subplot with. +We showcase two methods to position text at a given physical offset (in +fontsize units or in points) away from a corner of the Axes: one using +`~.Axes.annotate`, and one using `.ScaledTranslation`. + +For convenience, this example uses `.pyplot.subplot_mosaic` and subplot +labels as keys for the subplots. However, the approach also works with +`.pyplot.subplots` or keys that are different from what you want to label the +subplot with. """ import matplotlib.pyplot as plt -import matplotlib.transforms as mtransforms +from matplotlib.transforms import ScaledTranslation +# %% fig, axs = plt.subplot_mosaic([['a)', 'c)'], ['b)', 'c)'], ['d)', 'd)']], layout='constrained') - for label, ax in axs.items(): - # label physical distance in and down: - trans = mtransforms.ScaledTranslation(10/72, -5/72, fig.dpi_scale_trans) - ax.text(0.0, 1.0, label, transform=ax.transAxes + trans, - fontsize='medium', verticalalignment='top', fontfamily='serif', - bbox=dict(facecolor='0.7', edgecolor='none', pad=3.0)) - -plt.show() + # Use Axes.annotate to put the label + # - at the top left corner (axes fraction (0, 1)), + # - offset half-a-fontsize right and half-a-fontsize down + # (offset fontsize (+0.5, -0.5)), + # i.e. just inside the axes. + ax.annotate( + label, + xy=(0, 1), xycoords='axes fraction', + xytext=(+0.5, -0.5), textcoords='offset fontsize', + fontsize='medium', verticalalignment='top', fontfamily='serif', + bbox=dict(facecolor='0.7', edgecolor='none', pad=3.0)) # %% -# We may prefer the labels outside the axes, but still aligned -# with each other, in which case we use a slightly different transform: - fig, axs = plt.subplot_mosaic([['a)', 'c)'], ['b)', 'c)'], ['d)', 'd)']], layout='constrained') - for label, ax in axs.items(): - # label physical distance to the left and up: - trans = mtransforms.ScaledTranslation(-20/72, 7/72, fig.dpi_scale_trans) - ax.text(0.0, 1.0, label, transform=ax.transAxes + trans, - fontsize='medium', va='bottom', fontfamily='serif') - -plt.show() + # Use ScaledTranslation to put the label + # - at the top left corner (axes fraction (0, 1)), + # - offset 20 pixels left and 7 pixels up (offset points (-20, +7)), + # i.e. just outside the axes. + ax.text( + 0.0, 1.0, label, transform=( + ax.transAxes + ScaledTranslation(-20/72, +7/72, fig.dpi_scale_trans)), + fontsize='medium', va='bottom', fontfamily='serif') # %% # If we want it aligned with the title, either incorporate in the title or @@ -50,7 +55,6 @@ fig, axs = plt.subplot_mosaic([['a)', 'c)'], ['b)', 'c)'], ['d)', 'd)']], layout='constrained') - for label, ax in axs.items(): ax.set_title('Normal Title', fontstyle='italic') ax.set_title(label, fontfamily='serif', loc='left', fontsize='medium') @@ -67,5 +71,4 @@ # - `matplotlib.figure.Figure.subplot_mosaic` / # `matplotlib.pyplot.subplot_mosaic` # - `matplotlib.axes.Axes.set_title` -# - `matplotlib.axes.Axes.text` -# - `matplotlib.transforms.ScaledTranslation` +# - `matplotlib.axes.Axes.annotate` diff --git a/galleries/examples/text_labels_and_annotations/line_with_text.py b/galleries/examples/text_labels_and_annotations/line_with_text.py index 389554bd5ae1..22f5580b33ba 100644 --- a/galleries/examples/text_labels_and_annotations/line_with_text.py +++ b/galleries/examples/text_labels_and_annotations/line_with_text.py @@ -28,7 +28,7 @@ def set_figure(self, figure): self.text.set_figure(figure) super().set_figure(figure) - # Override the axes property setter to set Axes on our children as well. + # Override the Axes property setter to set Axes on our children as well. @lines.Line2D.axes.setter def axes(self, new_axes): self.text.axes = new_axes diff --git a/galleries/examples/text_labels_and_annotations/placing_text_boxes.py b/galleries/examples/text_labels_and_annotations/placing_text_boxes.py index f3b07a25e135..7cf49bf6e0bb 100644 --- a/galleries/examples/text_labels_and_annotations/placing_text_boxes.py +++ b/galleries/examples/text_labels_and_annotations/placing_text_boxes.py @@ -2,7 +2,7 @@ Placing text boxes ================== -When decorating axes with text boxes, two useful tricks are to place the text +When decorating Axes with text boxes, two useful tricks are to place the text in axes coordinates (see :ref:`transforms_tutorial`), so the text doesn't move around with changes in x or y limits. You can also use the ``bbox`` property of text to surround the text with a diff --git a/galleries/examples/text_labels_and_annotations/titles_demo.py b/galleries/examples/text_labels_and_annotations/titles_demo.py index 7202ac9115ff..6fc0e350fdb2 100644 --- a/galleries/examples/text_labels_and_annotations/titles_demo.py +++ b/galleries/examples/text_labels_and_annotations/titles_demo.py @@ -4,7 +4,7 @@ ================= Matplotlib can display plot titles centered, flush with the left side of -a set of axes, and flush with the right side of a set of axes. +a set of Axes, and flush with the right side of a set of Axes. """ import matplotlib.pyplot as plt diff --git a/galleries/examples/ticks/colorbar_tick_labelling_demo.py b/galleries/examples/ticks/colorbar_tick_labelling_demo.py index 8a1e5b28043d..6436748a46ec 100644 --- a/galleries/examples/ticks/colorbar_tick_labelling_demo.py +++ b/galleries/examples/ticks/colorbar_tick_labelling_demo.py @@ -6,8 +6,8 @@ Vertical colorbars have ticks, tick labels, and labels visible on the *y* axis, horizontal colorbars on the *x* axis. The ``ticks`` parameter can be used to set the ticks and the ``format`` parameter can be used to format the tick labels -of the visible colorbar axes. For further adjustments, the ``yaxis`` or -``xaxis`` axes of the colorbar can be retrieved using its ``ax`` property. +of the visible colorbar Axes. For further adjustments, the ``yaxis`` or +``xaxis`` Axes of the colorbar can be retrieved using its ``ax`` property. """ import matplotlib.pyplot as plt import numpy as np diff --git a/galleries/examples/ticks/date_concise_formatter.py b/galleries/examples/ticks/date_concise_formatter.py index f757fc4fbe09..540ebf0e56c1 100644 --- a/galleries/examples/ticks/date_concise_formatter.py +++ b/galleries/examples/ticks/date_concise_formatter.py @@ -1,4 +1,6 @@ """ +.. _date_concise_formatter: + ================================================ Formatting date ticks using ConciseDateFormatter ================================================ diff --git a/galleries/examples/ticks/date_formatters_locators.py b/galleries/examples/ticks/date_formatters_locators.py index e500f2e90abc..39492168242f 100644 --- a/galleries/examples/ticks/date_formatters_locators.py +++ b/galleries/examples/ticks/date_formatters_locators.py @@ -1,4 +1,6 @@ """ +.. _date_formatters_locators: + ================================= Date tick locators and formatters ================================= diff --git a/galleries/examples/ticks/dollar_ticks.py b/galleries/examples/ticks/dollar_ticks.py index 4186e8c369e9..7abf967c053b 100644 --- a/galleries/examples/ticks/dollar_ticks.py +++ b/galleries/examples/ticks/dollar_ticks.py @@ -3,10 +3,11 @@ Dollar ticks ============ -Use a `~.ticker.FormatStrFormatter` to prepend dollar signs on y-axis labels. +Use a format string to prepend dollar signs on y-axis labels. .. redirect-from:: /gallery/pyplots/dollar_ticks """ + import matplotlib.pyplot as plt import numpy as np diff --git a/galleries/examples/ticks/ticklabels_rotation.py b/galleries/examples/ticks/ticklabels_rotation.py index 711a680def62..94924d0440f5 100644 --- a/galleries/examples/ticks/ticklabels_rotation.py +++ b/galleries/examples/ticks/ticklabels_rotation.py @@ -5,17 +5,16 @@ Demo of custom tick-labels with user-defined rotation. """ + import matplotlib.pyplot as plt x = [1, 2, 3, 4] y = [1, 4, 9, 6] labels = ['Frogs', 'Hogs', 'Bogs', 'Slogs'] -plt.plot(x, y) +fig, ax = plt.subplots() +ax.plot(x, y) # You can specify a rotation for the tick labels in degrees or with keywords. -plt.xticks(x, labels, rotation='vertical') -# Pad margins so that markers don't get clipped by the axes -plt.margins(0.2) -# Tweak spacing to prevent clipping of tick-labels -plt.subplots_adjust(bottom=0.15) +ax.set_xticks(x, labels, rotation='vertical') + plt.show() diff --git a/galleries/examples/units/basic_units.py b/galleries/examples/units/basic_units.py index e9b5d18ef340..f9a94bcf6e37 100644 --- a/galleries/examples/units/basic_units.py +++ b/galleries/examples/units/basic_units.py @@ -1,4 +1,6 @@ """ +.. _basic_units: + =========== Basic Units =========== @@ -144,10 +146,10 @@ def __getattribute__(self, name): return getattr(variable, name) return object.__getattribute__(self, name) - def __array__(self, dtype=object): + def __array__(self, dtype=object, copy=False): return np.asarray(self.value, dtype) - def __array_wrap__(self, array, context): + def __array_wrap__(self, array, context=None, return_scalar=False): return TaggedValue(array, self.unit) def __repr__(self): @@ -220,10 +222,10 @@ def __mul__(self, rhs): def __rmul__(self, lhs): return self*lhs - def __array_wrap__(self, array, context): + def __array_wrap__(self, array, context=None, return_scalar=False): return TaggedValue(array, self) - def __array__(self, t=None, context=None): + def __array__(self, t=None, context=None, copy=False): ret = np.array(1) if t is not None: return ret.astype(t) diff --git a/galleries/examples/user_interfaces/embedding_in_wx4_sgskip.py b/galleries/examples/user_interfaces/embedding_in_wx4_sgskip.py index 56cb5f423980..b9504ff25dee 100644 --- a/galleries/examples/user_interfaces/embedding_in_wx4_sgskip.py +++ b/galleries/examples/user_interfaces/embedding_in_wx4_sgskip.py @@ -28,7 +28,7 @@ def __init__(self, canvas): self.Bind(wx.EVT_TOOL, self._on_custom, id=tool.GetId()) def _on_custom(self, event): - # add some text to the axes in a random location in axes coords with a + # add some text to the Axes in a random location in axes coords with a # random color ax = self.canvas.figure.axes[0] x, y = np.random.rand(2) # generate a random location diff --git a/galleries/examples/user_interfaces/fourier_demo_wx_sgskip.py b/galleries/examples/user_interfaces/fourier_demo_wx_sgskip.py index 56c62ae1ba77..f51917fda6b9 100644 --- a/galleries/examples/user_interfaces/fourier_demo_wx_sgskip.py +++ b/galleries/examples/user_interfaces/fourier_demo_wx_sgskip.py @@ -164,7 +164,7 @@ def mouseMotion(self, event): if self.state == '': return x, y = event.xdata, event.ydata - if x is None: # outside the axes + if x is None: # outside the Axes return x0, y0, f0Init, AInit = self.mouseInfo self.A.set(AInit + (AInit * (y - y0) / y0), self) diff --git a/galleries/examples/user_interfaces/svg_histogram_sgskip.py b/galleries/examples/user_interfaces/svg_histogram_sgskip.py index 38546b4cb4c3..7a484d998e69 100644 --- a/galleries/examples/user_interfaces/svg_histogram_sgskip.py +++ b/galleries/examples/user_interfaces/svg_histogram_sgskip.py @@ -9,7 +9,7 @@ The interactivity is encoded in ecmascript (javascript) and inserted in the SVG code in a post-processing step. To render the image, open it in a web browser. SVG is supported in most web browsers used by Linux and -OSX users. Windows IE9 supports SVG, but earlier versions do not. +macOS users. Windows IE9 supports SVG, but earlier versions do not. Notes ----- diff --git a/galleries/examples/widgets/menu.py b/galleries/examples/widgets/menu.py index 8d3db3d1b9c3..e948d5e00863 100644 --- a/galleries/examples/widgets/menu.py +++ b/galleries/examples/widgets/menu.py @@ -5,19 +5,22 @@ Using texts to construct a simple menu. """ + +from dataclasses import dataclass + import matplotlib.pyplot as plt import matplotlib.artist as artist import matplotlib.patches as patches +from matplotlib.typing import ColorType +@dataclass class ItemProperties: - def __init__(self, fontsize=14, labelcolor='black', bgcolor='yellow', - alpha=1.0): - self.fontsize = fontsize - self.labelcolor = labelcolor - self.bgcolor = bgcolor - self.alpha = alpha + fontsize: float = 14 + labelcolor: ColorType = 'black' + bgcolor: ColorType = 'yellow' + alpha: float = 1.0 class MenuItem(artist.Artist): @@ -130,7 +133,7 @@ def on_move(self, event): menuitems = [] for label in ('open', 'close', 'save', 'save as', 'quit'): def on_select(item): - print('you selected %s' % item.labelstr) + print(f'you selected {item.labelstr}') item = MenuItem(fig, label, props=props, hoverprops=hoverprops, on_select=on_select) menuitems.append(item) diff --git a/galleries/examples/widgets/multicursor.py b/galleries/examples/widgets/multicursor.py index 3a80b9ef8230..bc0d58b6c749 100644 --- a/galleries/examples/widgets/multicursor.py +++ b/galleries/examples/widgets/multicursor.py @@ -5,9 +5,9 @@ Showing a cursor on multiple plots simultaneously. -This example generates three axes split over two different figures. On +This example generates three Axes split over two different figures. On hovering the cursor over data in one subplot, the values of that datapoint are -shown in all axes. +shown in all Axes. """ import matplotlib.pyplot as plt diff --git a/galleries/plot_types/3D/bar3d_simple.py b/galleries/plot_types/3D/bar3d_simple.py new file mode 100644 index 000000000000..aa75560de8f2 --- /dev/null +++ b/galleries/plot_types/3D/bar3d_simple.py @@ -0,0 +1,29 @@ +""" +========================== +bar3d(x, y, z, dx, dy, dz) +========================== + +See `~mpl_toolkits.mplot3d.axes3d.Axes3D.bar3d`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# Make data +x = [1, 1, 2, 2] +y = [1, 2, 1, 2] +z = [0, 0, 0, 0] +dx = np.ones_like(x)*0.5 +dy = np.ones_like(x)*0.5 +dz = [2, 3, 1, 4] + +# Plot +fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) +ax.bar3d(x, y, z, dx, dy, dz) + +ax.set(xticklabels=[], + yticklabels=[], + zticklabels=[]) + +plt.show() diff --git a/galleries/plot_types/3D/plot3d_simple.py b/galleries/plot_types/3D/plot3d_simple.py new file mode 100644 index 000000000000..108dbecfbd87 --- /dev/null +++ b/galleries/plot_types/3D/plot3d_simple.py @@ -0,0 +1,27 @@ +""" +================ +plot(xs, ys, zs) +================ + +See `~mpl_toolkits.mplot3d.axes3d.Axes3D.plot`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# Make data +n = 100 +xs = np.linspace(0, 1, n) +ys = np.sin(xs * 6 * np.pi) +zs = np.cos(xs * 6 * np.pi) + +# Plot +fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) +ax.plot(xs, ys, zs) + +ax.set(xticklabels=[], + yticklabels=[], + zticklabels=[]) + +plt.show() diff --git a/galleries/plot_types/3D/quiver3d_simple.py b/galleries/plot_types/3D/quiver3d_simple.py new file mode 100644 index 000000000000..6f4aaa9cad90 --- /dev/null +++ b/galleries/plot_types/3D/quiver3d_simple.py @@ -0,0 +1,32 @@ +""" +======================== +quiver(X, Y, Z, U, V, W) +======================== + +See `~mpl_toolkits.mplot3d.axes3d.Axes3D.quiver`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# Make data +n = 4 +x = np.linspace(-1, 1, n) +y = np.linspace(-1, 1, n) +z = np.linspace(-1, 1, n) +X, Y, Z = np.meshgrid(x, y, z) +U = (X + Y)/5 +V = (Y - X)/5 +W = Z*0 + + +# Plot +fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) +ax.quiver(X, Y, Z, U, V, W) + +ax.set(xticklabels=[], + yticklabels=[], + zticklabels=[]) + +plt.show() diff --git a/galleries/plot_types/3D/stem3d.py b/galleries/plot_types/3D/stem3d.py new file mode 100644 index 000000000000..50aa80146bdc --- /dev/null +++ b/galleries/plot_types/3D/stem3d.py @@ -0,0 +1,27 @@ +""" +============= +stem(x, y, z) +============= + +See `~mpl_toolkits.mplot3d.axes3d.Axes3D.stem`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# Make data +n = 20 +x = np.sin(np.linspace(0, 2*np.pi, n)) +y = np.cos(np.linspace(0, 2*np.pi, n)) +z = np.linspace(0, 1, n) + +# Plot +fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) +ax.stem(x, y, z) + +ax.set(xticklabels=[], + yticklabels=[], + zticklabels=[]) + +plt.show() diff --git a/galleries/plot_types/arrays/README.rst b/galleries/plot_types/arrays/README.rst index d9dbfd10ead7..aba457a69940 100644 --- a/galleries/plot_types/arrays/README.rst +++ b/galleries/plot_types/arrays/README.rst @@ -1,7 +1,7 @@ .. _array_plots: -Gridded data: -------------- +Gridded data +------------ Plots of arrays and images :math:`Z_{i, j}` and fields :math:`U_{i, j}, V_{i, j}` on `regular grids `_ and diff --git a/galleries/plot_types/arrays/barbs.py b/galleries/plot_types/arrays/barbs.py index 63e492869039..b007d9b875b8 100644 --- a/galleries/plot_types/arrays/barbs.py +++ b/galleries/plot_types/arrays/barbs.py @@ -2,6 +2,7 @@ ================= barbs(X, Y, U, V) ================= +Plot a 2D field of wind barbs. See `~matplotlib.axes.Axes.barbs`. """ diff --git a/galleries/plot_types/arrays/contour.py b/galleries/plot_types/arrays/contour.py index fe79c18d2b58..1bf8d71d482b 100644 --- a/galleries/plot_types/arrays/contour.py +++ b/galleries/plot_types/arrays/contour.py @@ -2,6 +2,7 @@ ================ contour(X, Y, Z) ================ +Plot contour lines. See `~matplotlib.axes.Axes.contour`. """ diff --git a/galleries/plot_types/arrays/contourf.py b/galleries/plot_types/arrays/contourf.py index bde2f984fc0f..c25afe0bfa77 100644 --- a/galleries/plot_types/arrays/contourf.py +++ b/galleries/plot_types/arrays/contourf.py @@ -2,6 +2,7 @@ ================= contourf(X, Y, Z) ================= +Plot filled contours. See `~matplotlib.axes.Axes.contourf`. """ diff --git a/galleries/plot_types/arrays/imshow.py b/galleries/plot_types/arrays/imshow.py index be647d1f2924..b2920e7fd80c 100644 --- a/galleries/plot_types/arrays/imshow.py +++ b/galleries/plot_types/arrays/imshow.py @@ -2,6 +2,7 @@ ========= imshow(Z) ========= +Display data as an image, i.e., on a 2D regular raster. See `~matplotlib.axes.Axes.imshow`. """ @@ -18,6 +19,6 @@ # plot fig, ax = plt.subplots() -ax.imshow(Z) +ax.imshow(Z, origin='lower') plt.show() diff --git a/galleries/plot_types/arrays/pcolormesh.py b/galleries/plot_types/arrays/pcolormesh.py index b490dcb99d3f..4f0913f62521 100644 --- a/galleries/plot_types/arrays/pcolormesh.py +++ b/galleries/plot_types/arrays/pcolormesh.py @@ -2,6 +2,7 @@ =================== pcolormesh(X, Y, Z) =================== +Create a pseudocolor plot with a non-regular rectangular grid. `~.axes.Axes.pcolormesh` is more flexible than `~.axes.Axes.imshow` in that the x and y vectors need not be equally spaced (indeed they can be skewed). diff --git a/galleries/plot_types/arrays/quiver.py b/galleries/plot_types/arrays/quiver.py index 5d6dc808c518..4b1cbd03759c 100644 --- a/galleries/plot_types/arrays/quiver.py +++ b/galleries/plot_types/arrays/quiver.py @@ -2,6 +2,7 @@ ================== quiver(X, Y, U, V) ================== +Plot a 2D field of arrows. See `~matplotlib.axes.Axes.quiver`. """ diff --git a/galleries/plot_types/arrays/streamplot.py b/galleries/plot_types/arrays/streamplot.py index 3f1e2ef4e1cc..670773d2cfd3 100644 --- a/galleries/plot_types/arrays/streamplot.py +++ b/galleries/plot_types/arrays/streamplot.py @@ -2,6 +2,7 @@ ====================== streamplot(X, Y, U, V) ====================== +Draw streamlines of a vector flow. See `~matplotlib.axes.Axes.streamplot`. """ diff --git a/galleries/plot_types/basic/fill_between.py b/galleries/plot_types/basic/fill_between.py index a454c3c30772..feca3c658d3e 100644 --- a/galleries/plot_types/basic/fill_between.py +++ b/galleries/plot_types/basic/fill_between.py @@ -2,6 +2,7 @@ ======================= fill_between(x, y1, y2) ======================= +Fill the area between two horizontal curves. See `~matplotlib.axes.Axes.fill_between`. """ diff --git a/galleries/plot_types/basic/plot.py b/galleries/plot_types/basic/plot.py index 3808137e52fd..34cf500599bb 100644 --- a/galleries/plot_types/basic/plot.py +++ b/galleries/plot_types/basic/plot.py @@ -2,6 +2,7 @@ ========== plot(x, y) ========== +Plot y versus x as lines and/or markers. See `~matplotlib.axes.Axes.plot`. """ @@ -13,12 +14,16 @@ # make data x = np.linspace(0, 10, 100) -y = 4 + 2 * np.sin(2 * x) +y = 4 + 1 * np.sin(2 * x) +x2 = np.linspace(0, 10, 25) +y2 = 4 + 1 * np.sin(2 * x2) # plot fig, ax = plt.subplots() +ax.plot(x2, y2 + 2.5, 'x', markeredgewidth=2) ax.plot(x, y, linewidth=2.0) +ax.plot(x2, y2 - 2.5, 'o-', linewidth=2) ax.set(xlim=(0, 8), xticks=np.arange(1, 8), ylim=(0, 8), yticks=np.arange(1, 8)) diff --git a/galleries/plot_types/basic/scatter_plot.py b/galleries/plot_types/basic/scatter_plot.py index 792016c0e79c..07fa943b724f 100644 --- a/galleries/plot_types/basic/scatter_plot.py +++ b/galleries/plot_types/basic/scatter_plot.py @@ -2,6 +2,7 @@ ============= scatter(x, y) ============= +A scatter plot of y vs. x with varying marker size and/or color. See `~matplotlib.axes.Axes.scatter`. """ diff --git a/galleries/plot_types/basic/stackplot.py b/galleries/plot_types/basic/stackplot.py index 5b23b52775b3..275fa5cb67ba 100644 --- a/galleries/plot_types/basic/stackplot.py +++ b/galleries/plot_types/basic/stackplot.py @@ -2,6 +2,8 @@ =============== stackplot(x, y) =============== +Draw a stacked area plot or a streamgraph. + See `~matplotlib.axes.Axes.stackplot` """ import matplotlib.pyplot as plt diff --git a/galleries/plot_types/basic/stairs.py b/galleries/plot_types/basic/stairs.py index de5761e3a7a5..732ded998241 100644 --- a/galleries/plot_types/basic/stairs.py +++ b/galleries/plot_types/basic/stairs.py @@ -2,6 +2,7 @@ ============== stairs(values) ============== +Draw a stepwise constant function as a line or a filled plot. See `~matplotlib.axes.Axes.stairs` when plotting :math:`y` between :math:`(x_i, x_{i+1})`. For plotting :math:`y` at :math:`x`, see diff --git a/galleries/plot_types/basic/stem.py b/galleries/plot_types/basic/stem.py index a606482ecf9b..afd10ca1c9df 100644 --- a/galleries/plot_types/basic/stem.py +++ b/galleries/plot_types/basic/stem.py @@ -2,6 +2,7 @@ ========== stem(x, y) ========== +Create a stem plot. See `~matplotlib.axes.Axes.stem`. """ diff --git a/galleries/plot_types/stats/boxplot_plot.py b/galleries/plot_types/stats/boxplot_plot.py index cdad3c52320f..996b97a2aef4 100644 --- a/galleries/plot_types/stats/boxplot_plot.py +++ b/galleries/plot_types/stats/boxplot_plot.py @@ -2,6 +2,7 @@ ========== boxplot(X) ========== +Draw a box and whisker plot. See `~matplotlib.axes.Axes.boxplot`. """ diff --git a/galleries/plot_types/stats/ecdf.py b/galleries/plot_types/stats/ecdf.py index bd5f9fa9e5b2..1a8566b3d2eb 100644 --- a/galleries/plot_types/stats/ecdf.py +++ b/galleries/plot_types/stats/ecdf.py @@ -2,6 +2,7 @@ ======= ecdf(x) ======= +Compute and plot the empirical cumulative distribution function of x. See `~matplotlib.axes.Axes.ecdf`. """ diff --git a/galleries/plot_types/stats/errorbar_plot.py b/galleries/plot_types/stats/errorbar_plot.py index 0e226e11b315..c96a08e111c1 100644 --- a/galleries/plot_types/stats/errorbar_plot.py +++ b/galleries/plot_types/stats/errorbar_plot.py @@ -2,6 +2,7 @@ ========================== errorbar(x, y, yerr, xerr) ========================== +Plot y versus x as lines and/or markers with attached errorbars. See `~matplotlib.axes.Axes.errorbar`. """ diff --git a/galleries/plot_types/stats/eventplot.py b/galleries/plot_types/stats/eventplot.py index da8c33c28425..babdbe6d1ca1 100644 --- a/galleries/plot_types/stats/eventplot.py +++ b/galleries/plot_types/stats/eventplot.py @@ -2,6 +2,7 @@ ============ eventplot(D) ============ +Plot identical parallel lines at the given positions. See `~matplotlib.axes.Axes.eventplot`. """ diff --git a/galleries/plot_types/stats/hexbin.py b/galleries/plot_types/stats/hexbin.py index 91e771308afd..9d3a2a071346 100644 --- a/galleries/plot_types/stats/hexbin.py +++ b/galleries/plot_types/stats/hexbin.py @@ -2,6 +2,7 @@ =============== hexbin(x, y, C) =============== +Make a 2D hexagonal binning plot of points x, y. See `~matplotlib.axes.Axes.hexbin`. """ diff --git a/galleries/plot_types/stats/hist2d.py b/galleries/plot_types/stats/hist2d.py index 3e43f7ee8ace..d95b67539b33 100644 --- a/galleries/plot_types/stats/hist2d.py +++ b/galleries/plot_types/stats/hist2d.py @@ -2,6 +2,7 @@ ============ hist2d(x, y) ============ +Make a 2D histogram plot. See `~matplotlib.axes.Axes.hist2d`. """ diff --git a/galleries/plot_types/stats/hist_plot.py b/galleries/plot_types/stats/hist_plot.py index 6c86a0aca216..6328fe9d07c6 100644 --- a/galleries/plot_types/stats/hist_plot.py +++ b/galleries/plot_types/stats/hist_plot.py @@ -2,6 +2,7 @@ ======= hist(x) ======= +Compute and plot a histogram. See `~matplotlib.axes.Axes.hist`. """ diff --git a/galleries/plot_types/stats/pie.py b/galleries/plot_types/stats/pie.py index 80484a0eb932..bd8d555f0040 100644 --- a/galleries/plot_types/stats/pie.py +++ b/galleries/plot_types/stats/pie.py @@ -2,6 +2,7 @@ ====== pie(x) ====== +Plot a pie chart. See `~matplotlib.axes.Axes.pie`. """ diff --git a/galleries/plot_types/stats/violin.py b/galleries/plot_types/stats/violin.py index c8a987a690dd..2ea2161ad91c 100644 --- a/galleries/plot_types/stats/violin.py +++ b/galleries/plot_types/stats/violin.py @@ -2,6 +2,7 @@ ============= violinplot(D) ============= +Make a violin plot. See `~matplotlib.axes.Axes.violinplot`. """ diff --git a/galleries/plot_types/unstructured/tricontour.py b/galleries/plot_types/unstructured/tricontour.py index 83b0a212fd83..292ff551fe36 100644 --- a/galleries/plot_types/unstructured/tricontour.py +++ b/galleries/plot_types/unstructured/tricontour.py @@ -2,6 +2,7 @@ =================== tricontour(x, y, z) =================== +Draw contour lines on an unstructured triangular grid. See `~matplotlib.axes.Axes.tricontour`. """ diff --git a/galleries/plot_types/unstructured/tricontourf.py b/galleries/plot_types/unstructured/tricontourf.py index da909c02f0b2..aab748e73024 100644 --- a/galleries/plot_types/unstructured/tricontourf.py +++ b/galleries/plot_types/unstructured/tricontourf.py @@ -2,6 +2,7 @@ ==================== tricontourf(x, y, z) ==================== +Draw contour regions on an unstructured triangular grid. See `~matplotlib.axes.Axes.tricontourf`. """ diff --git a/galleries/plot_types/unstructured/tripcolor.py b/galleries/plot_types/unstructured/tripcolor.py index e2619a68444e..398877653db8 100644 --- a/galleries/plot_types/unstructured/tripcolor.py +++ b/galleries/plot_types/unstructured/tripcolor.py @@ -2,6 +2,7 @@ ================== tripcolor(x, y, z) ================== +Create a pseudocolor plot of an unstructured triangular grid. See `~matplotlib.axes.Axes.tripcolor`. """ diff --git a/galleries/plot_types/unstructured/triplot.py b/galleries/plot_types/unstructured/triplot.py index 78cf8e32a318..d726c46e1e47 100644 --- a/galleries/plot_types/unstructured/triplot.py +++ b/galleries/plot_types/unstructured/triplot.py @@ -2,6 +2,7 @@ ============= triplot(x, y) ============= +Draw an unstructured triangular grid as lines and/or markers. See `~matplotlib.axes.Axes.triplot`. """ diff --git a/galleries/tutorials/artists.py b/galleries/tutorials/artists.py index 46303372d7ae..f5e4589e8a52 100644 --- a/galleries/tutorials/artists.py +++ b/galleries/tutorials/artists.py @@ -286,7 +286,7 @@ class in the Matplotlib API, and the one you will be working with most # :class:`~matplotlib.patches.Rectangle` which is stored in # :attr:`Figure.patch `. As # you add subplots (:meth:`~matplotlib.figure.Figure.add_subplot`) and -# axes (:meth:`~matplotlib.figure.Figure.add_axes`) to the figure +# Axes (:meth:`~matplotlib.figure.Figure.add_axes`) to the figure # these will be appended to the :attr:`Figure.axes # `. These are also returned by the # methods that create them: @@ -443,7 +443,7 @@ class in the Matplotlib API, and the one you will be working with most # # create a rectangle instance # In [263]: rect = matplotlib.patches.Rectangle((1, 1), width=5, height=12) # -# # by default the axes instance is None +# # by default the Axes instance is None # In [264]: print(rect.axes) # None # @@ -454,7 +454,7 @@ class in the Matplotlib API, and the one you will be working with most # # now we add the Rectangle to the Axes # In [266]: ax.add_patch(rect) # -# # and notice that the ax.add_patch method has set the axes +# # and notice that the ax.add_patch method has set the Axes # # instance # In [267]: print(rect.axes) # Axes(0.125,0.1;0.775x0.8) @@ -485,7 +485,7 @@ class in the Matplotlib API, and the one you will be working with most # [ 0. 100. 0.] # [ 0. 0. 1.]]))))))) # -# # the default axes transformation is ax.transData +# # the default Axes transformation is ax.transData # In [269]: print(ax.transData) # CompositeGenericTransform( # TransformWrapper( diff --git a/galleries/tutorials/lifecycle.py b/galleries/tutorials/lifecycle.py index 9df59a5d229f..4aae4d6c1dbc 100644 --- a/galleries/tutorials/lifecycle.py +++ b/galleries/tutorials/lifecycle.py @@ -86,7 +86,7 @@ # # .. note:: # -# Figures can have multiple axes on them. For information on how to do this, +# Figures can have multiple Axes on them. For information on how to do this, # see the :ref:`Tight Layout tutorial # `. @@ -212,7 +212,7 @@ def currency(x, pos): # %% # We can then apply this function to the labels on our plot. To do this, -# we use the ``xaxis`` attribute of our axes. This lets you perform +# we use the ``xaxis`` attribute of our Axes. This lets you perform # actions on a specific axis on our plot. fig, ax = plt.subplots(figsize=(6, 8)) @@ -230,7 +230,7 @@ def currency(x, pos): # # It is possible to draw multiple plot elements on the same instance of # :class:`axes.Axes`. To do this we simply need to call another one of -# the plot methods on that axes object. +# the plot methods on that Axes object. fig, ax = plt.subplots(figsize=(8, 8)) ax.barh(group_names, group_data) diff --git a/galleries/tutorials/pyplot.py b/galleries/tutorials/pyplot.py index 3c9f65a68c57..8062e5aa3793 100644 --- a/galleries/tutorials/pyplot.py +++ b/galleries/tutorials/pyplot.py @@ -26,10 +26,10 @@ # In :mod:`matplotlib.pyplot` various states are preserved # across function calls, so that it keeps track of things like # the current figure and plotting area, and the plotting -# functions are directed to the current axes (please note that "axes" here -# and in most places in the documentation refers to the *axes* +# functions are directed to the current Axes (please note that we use uppercase +# Axes to refer to the `~.axes.Axes` concept, which is a central # :ref:`part of a figure ` -# and not the strict mathematical term for more than one axis). +# and not only the plural of *axis*). # # .. note:: # @@ -81,7 +81,7 @@ # list of line styles and format strings. The # `~.pyplot.axis` function in the example above takes a # list of ``[xmin, xmax, ymin, ymax]`` and specifies the viewport of the -# axes. +# Axes. # # If matplotlib were limited to working with lists, it would be fairly # useless for numeric processing. Generally, you will use `numpy @@ -241,12 +241,12 @@ # .. _multiple-figs-axes: # # -# Working with multiple figures and axes +# Working with multiple figures and Axes # ====================================== # # MATLAB, and :mod:`.pyplot`, have the concept of the current figure -# and the current axes. All plotting functions apply to the current -# axes. The function `~.pyplot.gca` returns the current axes (a +# and the current Axes. All plotting functions apply to the current +# Axes. The function `~.pyplot.gca` returns the current Axes (a # `matplotlib.axes.Axes` instance), and `~.pyplot.gcf` returns the current # figure (a `matplotlib.figure.Figure` instance). Normally, you don't have to # worry about this, because it is all taken care of behind the scenes. Below @@ -278,17 +278,17 @@ def f(t): # to ``subplot(2, 1, 1)``. # # You can create an arbitrary number of subplots -# and axes. If you want to place an Axes manually, i.e., not on a +# and Axes. If you want to place an Axes manually, i.e., not on a # rectangular grid, use `~.pyplot.axes`, # which allows you to specify the location as ``axes([left, bottom, # width, height])`` where all values are in fractional (0 to 1) # coordinates. See :doc:`/gallery/subplots_axes_and_figures/axes_demo` for an example of -# placing axes manually and :doc:`/gallery/subplots_axes_and_figures/subplot` for an +# placing Axes manually and :doc:`/gallery/subplots_axes_and_figures/subplot` for an # example with lots of subplots. # # You can create multiple figures by using multiple # `~.pyplot.figure` calls with an increasing figure -# number. Of course, each figure can contain as many axes and subplots +# number. Of course, each figure can contain as many Axes and subplots # as your heart desires:: # # import matplotlib.pyplot as plt @@ -309,8 +309,8 @@ def f(t): # plt.title('Easy as 1, 2, 3') # subplot 211 title # # You can clear the current figure with `~.pyplot.clf` -# and the current axes with `~.pyplot.cla`. If you find -# it annoying that states (specifically the current image, figure and axes) +# and the current Axes with `~.pyplot.cla`. If you find +# it annoying that states (specifically the current image, figure and Axes) # are being maintained for you behind the scenes, don't despair: this is just a thin # stateful wrapper around an object-oriented API, which you can use # instead (see :ref:`artists_tutorial`) diff --git a/galleries/users_explain/animations/animations.py b/galleries/users_explain/animations/animations.py index fb8564f8318e..2711663196f2 100644 --- a/galleries/users_explain/animations/animations.py +++ b/galleries/users_explain/animations/animations.py @@ -11,7 +11,7 @@ generate animations using the `~matplotlib.animation` module. An animation is a sequence of frames where each frame corresponds to a plot on a `~matplotlib.figure.Figure`. This tutorial covers a general guideline on -how to create such animations and the different options available. +how to create such animations and the different options available. More information is available in the API description: `~matplotlib.animation` """ import matplotlib.pyplot as plt @@ -49,28 +49,33 @@ # *func* that modifies the data plotted on the figure. It uses the *frames* # parameter to determine the length of the animation. The *interval* parameter # is used to determine time in milliseconds between drawing of two frames. -# Animating using `.FuncAnimation` would usually follow the following -# structure: -# -# - Plot the initial figure, including all the required artists. Save all the -# artists in variables so that they can be updated later on during the -# animation. -# - Create an animation function that updates the data in each artist to -# generate the new frame at each function call. -# - Create a `.FuncAnimation` object with the `.Figure` and the animation -# function, along with the keyword arguments that determine the animation -# properties. -# - Use `.animation.Animation.save` or `.pyplot.show` to save or show the -# animation. -# -# The update function uses the ``set_*`` function for different artists to -# modify the data. The following table shows a few plotting methods, the artist -# types they return and some methods that can be used to update them. +# Animating using `.FuncAnimation` typically requires these steps: +# +# 1) Plot the initial figure as you would in a static plot. Save all the created +# artists, which are returned by the plot functions, in variables so that you can +# access and modify them later in the animation function. +# 2) Create an animation function that updates the artists for a given frame. +# Typically, this calls ``set_*`` methods of the artists. +# 3) Create a `.FuncAnimation`, passing the `.Figure` and the animation function. +# 4) Save or show the animation using one of the following methods: +# +# - `.pyplot.show` to show the animation in a window +# - `.Animation.to_html5_video` to create a HTML ``