Coding guidelines#

We appreciate these guidelines being followed because it improves the readability,consistency, and maintainability of the code base.

API guidelines

If adding new features, changing behavior or function signatures, or removingpublic interfaces, please consult theAPI guidelines.

PEP8, as enforced by ruff#

Formatting should follow the recommendations ofPEP8, as enforced byruff.Matplotlib modifies PEP8 to extend the maximum line length to 88characters. You can check PEP8 compliance from the command line with

python-mpipinstallruffruffcheck/path/to/module.py

or your editor may provide integration with it. To check all files,and fix any errors in-place (where possible) run

ruffcheck--fix

Matplotlib intentionally does not use theblack auto-formatter (1),in particular due to its inability to understand the semantics ofmathematical expressions (2,3).

Package imports#

Import the following modules using the standard scipy conventions:

importnumpyasnpimportnumpy.maasmaimportmatplotlibasmplimportmatplotlib.pyplotaspltimportmatplotlib.cbookascbookimportmatplotlib.patchesasmpatches

In general, Matplotlib modules shouldnot importrcParams usingfrommatplotlibimportrcParams, but rather access it asmpl.rcParams. Thisis because some modules are imported very early, before thercParamssingleton is constructed.

Variable names#

When feasible, please use our internal variable naming convention for objectsof a given class and objects of any child class:

base class

variable

multiples

FigureBase

fig

Axes

ax

Transform

trans

trans_<source>_<target>

trans_<source> when target is screen

Generally, denote more than one instance of the same class by adding suffixes tothe variable names. If a format isn't specified in the table, use numbers orletters as appropriate.

Type hints#

If you add new public API or change public API, update or add thecorrespondingmypy type hints.We generally usestub files(*.pyi) to store the type information; for examplecolors.pyi containsthe type information forcolors.py. A notable exception ispyplot.py,which is type hinted inline.

Type hints can be validated by thestubtest tool, which can be runlocally usingtox-estubtest and is a part of theAutomated testssuite. Type hints for existing functions are also checked by the mypypre-commit hook.

New modules and files: installation#

  • If you have added new files or directories, or reorganized existing ones, make sure thenew files are included in themeson.build in the corresponding directories.

  • New modulesmay 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'taddress 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 namedFOO_wrap.cpp orFOO_wrapper.cpp.

  • Header file documentation (aka docstrings) should be in Numpydocformat. We don't plan on using automated tools for thesedocstrings, and the Numpydoc format is well understood in thescientific Python community.

  • C/C++ code in theextern/ directory is vendored, and should be keptclose to upstream whenever possible. It can be modified to fix bugs orimplement new features only if the required changes cannot be made elsewherein the codebase. In particular, avoid making style fixes to it.

Keyword argument processing#

Matplotlib makes extensive use of**kwargs for pass-through customizationsfrom one function to another. A typical example istext. The definition ofmatplotlib.pyplot.text is asimple pass-through tomatplotlib.axes.Axes.text:

# in pyplot.pydeftext(x,y,s,fontdict=None,**kwargs):returngca().text(x,y,s,fontdict=fontdict,**kwargs)

matplotlib.axes.Axes.text (simplified for illustration) justpasses allargs andkwargs on tomatplotlib.text.Text.__init__:

# in axes/_axes.pydeftext(self,x,y,s,fontdict=None,**kwargs):t=Text(x=x,y=y,text=s,**kwargs)

andmatplotlib.text.Text.__init__ (again, simplified)just passes them on to thematplotlib.artist.Artist.update method:

# in text.pydef__init__(self,x=0,y=0,text='',**kwargs):super().__init__()self.update(kwargs)

update does the work looking for methods named likeset_property ifproperty is a keyword argument. i.e., no onelooks at the keywords, they just get passed through the API to theartist constructor which looks for suitably named methods and callsthem with the value.

As a general rule, the use of**kwargs should be reserved forpass-through keyword arguments, as in the example above. If all thekeyword args are to be used in the function, and not passedon, use the key/value keyword args in the function definition ratherthan the**kwargs idiom.

In some cases, you may want to consume some keys in the localfunction, and let others pass through. Instead of popping arguments touse off**kwargs, specify them as keyword-only arguments to the localfunction. This makes it obvious at a glance which arguments will beconsumed in the function. For example, inplot(),scalex andscaley arelocal arguments and the rest are passed on asLine2D() keyword arguments:

# in axes/_axes.pydefplot(self,*args,scalex=True,scaley=True,**kwargs):lines=[]forlineinself._get_lines(*args,**kwargs):self.add_line(line)lines.append(line)

Using logging for debug messages#

Matplotlib uses the standard Pythonlogging library to write verbosewarnings, information, and debug messages. Please use it! In all those placesyou writeprint calls to do your debugging, try usinglogging.debuginstead!

To includelogging in your module, at the top of the module, you need toimportlogging. 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 namedmatplotlib.yourmodulename.

If an end-user of Matplotlib sets uplogging to display at levels moreverbose thanlogging.WARNING in their code with the Matplotlib-providedhelper:

plt.set_loglevel("debug")

or manually with

importlogginglogging.basicConfig(level=logging.DEBUG)importmatplotlib.pyplotasplt

Then they will receive messages like

DEBUG:matplotlib.backends:backend MacOSX version unknownDEBUG:matplotlib.yourmodulename:Here is some informationDEBUG:matplotlib.yourmodulename:Here is some more detailed information

Avoid using pre-computed strings (f-strings,str.format,etc.) for logging becauseof security and performance issues, and because they interfere with style handlers. Forexample, 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 andlogging.error are really only there for errors thatwill 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 theprogram behaves oddly. They are not displayed by default. For instance, ifan object isn't drawn because its position isNaN, that can usuallybe ignored, but a mystified user could calllogging.basicConfig(level=logging.INFO) and get an error message thatsays why.

  • logging.debug is the least likely to be displayed, and hence can be themost verbose. "Expected" code paths (e.g., reporting normal intermediatesteps of layouting or rendering) should only log at this level.

By default,logging displays all log messages at levels higher thanlogging.WARNING tosys.stderr.

Thelogging tutorial suggests that the difference betweenlogging.warningand_api.warn_external (which useswarnings.warn) is that_api.warn_external should be used for things the user must change to stopthe warning (typically in the source), whereaslogging.warning can be morepersistent. Moreover, note that_api.warn_external will by default onlyemit a given warningonce for each line of user code, whereaslogging.warning will display the message every time it is called.

By default,warnings.warn displays the line of code that has thewarncall. This usually isn't more informative than the warning message itself.Therefore, Matplotlib uses_api.warn_external which useswarnings.warn,but goes up the stack and displays the first line of code outside ofMatplotlib. For example, for the module:

# in my_matplotlib_module.pyimportwarningsdefset_range(bottom,top):ifbottom==top:warnings.warn('Attempting to set identical bottom==top')

running the script:

frommatplotlibimportmy_matplotlib_modulemy_matplotlib_module.set_range(0,0)# set range

will display

UserWarning: Attempting to set identical bottom==topwarnings.warn('Attempting to set identical bottom==top')

Modifying the module to use_api.warn_external:

frommatplotlibimport_apidefset_range(bottom,top):ifbottom==top:_api.warn_external('Attempting to set identical bottom==top')

and running the same script will display

UserWarning: Attempting to set identical bottom==topmy_matplotlib_module.set_range(0, 0)  # set range

Licenses for contributed code#

Matplotlib only uses BSD compatible code. If you bring in code fromanother project make sure it has a PSF, BSD, MIT or compatible license(see the Open Source Initiativelicenses page for details on individuallicenses). If it doesn't, you may consider contacting the author andasking them to relicense it. GPL and LGPL code are not acceptable inthe main code base, though we are considering an alternative way ofdistributing L/GPL code through an separate channel, possibly atoolkit. If you include code, make sure you include a copy of thatcode's license in the license directory if the code's license requiresyou to distribute the license with it. Non-BSD compatible licensesare acceptable in Matplotlib toolkits (e.g., basemap), but make sure youclearly state the licenses you are using.

Why BSD compatible?#

The two dominant license variants in the wild are GPL-style andBSD-style. There are countless other licenses that place specificrestrictions on code reuse, but there is an important difference to beconsidered in the GPL and BSD variants. The best known and perhapsmost widely used license is the GPL, which in addition to granting youfull rights to the source code including redistribution, carries withit an extra obligation. If you use GPL code in your own code, or linkwith it, your product must be released under a GPL compatiblelicense. i.e., you are required to give the source code to otherpeople and give them the right to redistribute it as well. Many of themost famous and widely used open source projects are released underthe GPL, including linux, gcc, emacs and sage.

The second major class are the BSD-style licenses (which includes MITand the python PSF license). These basically allow you to do whateveryou want with the code: ignore it, include it in your own open sourceproject, include it in your proprietary product, sell it,whatever. python itself is released under a BSD compatible license, inthe sense that, quoting from the PSF license page:

ThereisnoGPL-like"copyleft"restriction.Distributingbinary-onlyversionsofPython,modifiedornot,isallowed.Thereisnorequirementtoreleaseanyofyoursourcecode.YoucanalsowriteextensionmodulesforPythonandprovidethemonlyinbinaryform.

Famous projects released under a BSD-style license in the permissivesense of the last paragraph are the BSD operating system, python andTeX.

There are several reasons why early Matplotlib developers selected aBSD compatible license. Matplotlib is a python extension, and wechoose a license that was based on the python license (BSDcompatible). Also, we wanted to attract as many users and developersas possible, and many software companies will not use GPL code insoftware they plan to distribute, even those that are highly committedto open source development, such asenthought, out of legitimate concern that use of theGPL will "infect" their code base by its viral nature. In effect, theywant to retain the right to release some proprietary code. Companiesand institutions who use Matplotlib often make significantcontributions, because they have the resources to get a job done, evena boring one. Two of the Matplotlib backends (FLTK and WX) werecontributed by private companies. The final reason behind thelicensing choice is compatibility with the other python extensions forscientific computing: ipython, numpy, scipy, the enthought tool suiteand python itself are all distributed under BSD compatible licenses.