diff --git a/doc/devel/api_changes.rst b/doc/devel/api_changes.rst new file mode 100644 index 000000000000..b0904077ee0c --- /dev/null +++ b/doc/devel/api_changes.rst @@ -0,0 +1,211 @@ +.. _api_changes: + +API changes +=========== + +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 + +.. _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: = ...``). + +.. _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. + +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. diff --git a/doc/devel/coding_guide.rst b/doc/devel/coding_guide.rst index 22873020f103..6564de906979 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. +API changes +=========== -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 you are adding new features, changing behavior or function signatures, or +removing classes, functions, methods, or properties, please see the :ref:`api_changes` +guide. +PEP8, as enforced by flake8 +=========================== -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. - -When reviewing, please ensure that the pull request satisfies the following -requirements before merging it: - -Content topics: - -.. 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. - +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 :: -Organizational 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__). -* 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. +.. _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 -.. _pr-guidelines-details: -Detailed guidelines -=================== +Package imports +=============== -.. _pr-documentation: +Import the following modules using the standard scipy conventions:: -Documentation -------------- + 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 -* 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. +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. -* 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. +Variable names +============== -* Build the docs and make sure all formatting warnings are addressed. +When feasible, please use our internal variable naming convention for objects +of a given class and objects of any child class: -* 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 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. ++------------------------------------+---------------+------------------------------------------+ +| 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 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 :file:`meson.build` in the corresponding directories. +* 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. + +* 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 a7d81cbc06d9..9eacb2032dfd 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! @@ -90,6 +90,8 @@ 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. +.. _contributor_incubator: + Contributor incubator --------------------- @@ -104,6 +106,9 @@ 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 Meeting ------------------------ @@ -206,18 +211,108 @@ Please post feature requests to the 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 `. +users to then also :ref:`participate in the implementation `. -.. _contributing-code: +.. _contribute_code: Contribute 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-contribute` If you get stuck, please reach out on the +:ref:`contributor_incubator` + +.. _contribute_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 ` + +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 + + .. grid-item:: using the library + + * :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/users/installing/` + * :file:`doc/users/project/` + * :file:`doc/users/resources/` + * :file:`doc/users/faq.rst` + * :file:`doc/devel/` + +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-contribute`. 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: + +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! + +Matplotlib's community is built by its members, if you would like to help out +see our :ref:`communications-guidelines`. + +If Matplotlib contributes to a project that leads to a scientific publication, +please follow the :doc:`/users/project/citing` guidelines. + +If you have developed an extension to Matplotlib, please consider adding it to our +`third party package `_ list. .. _how-to-contribute: -How to contribute ------------------ +How to contribute via pull request +================================== The preferred way to contribute to Matplotlib is to fork the `main repository `__ on GitHub, @@ -228,7 +323,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. @@ -292,17 +387,6 @@ 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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -327,539 +411,16 @@ GitHub Codespaces workflows 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 have trouble getting started, you may instead open an `issue`_ -describing the intended improvement. - -.. _issue: https://github.com/matplotlib/matplotlib/issues - -.. 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: = ...``). - -.. _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. - -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 :file:`meson.build` in the corresponding directories. -* 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. - -* 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/index.rst b/doc/devel/index.rst index e6e16266968b..3ddbcba198a0 100644 --- a/doc/devel/index.rst +++ b/doc/devel/index.rst @@ -20,6 +20,10 @@ and implementing new features, helping the community... New contributors ================ +.. toctree:: + :hidden: + + contribute .. grid:: 1 1 2 2 :class-row: sd-align-minor-center @@ -58,14 +62,14 @@ New contributors :octicon:`bug;1em;sd-text-info` Submit bug report .. grid-item-card:: - :link: contributing-code + :link: contribute_code :link-type: ref :shadow: none :octicon:`code;1em;sd-text-info` Contribute code .. grid-item-card:: - :link: documenting-matplotlib + :link: contribute_documentation :link-type: ref :shadow: none @@ -130,12 +134,11 @@ Policies and guidelines **Code** ^^^ - | :ref:`coding_guidelines` - .. toctree:: :maxdepth: 1 coding_guide + api_changes testing .. grid-item-card:: @@ -154,12 +157,14 @@ Policies and 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 @@ -174,11 +179,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/pr_guide.rst b/doc/devel/pr_guide.rst new file mode 100644 index 000000000000..257e218d8e1f --- /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 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. + + +.. _branches_and_backports: + +Branches and backports +====================== + +Current branches +---------------- +The current active branches are + +*main* + The current development version. Future minor releases (*v3.N.0*) will be + branched from this. + +*v3.N.x* + Maintenance branch for Matplotlib 3.N. Future patch releases will be + branched from this. + +*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. + + +.. _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 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: + +- critical bug fixes (segfault, failure to import, things that the + user cannot work around) +- fixes for regressions introduced in the last two minor 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 minor 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/triage.rst b/doc/devel/triage.rst index 94b6874af228..1b2849738c9c 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 @@ -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 .