Skip to content

DOC: add section about how to write good error messages #25292

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

tacaswell
Copy link
Member

PR Summary

After leaving a comment to make error messages verbose I felt inspired to write it up for the docs...

Adding generated API docs for the private API might be a bit controversial, but I think on net it is the right thing to do so that we if we do remove them we can be sure that the docs do not still advocate for their use and saying "there are some check_* functions" felt too vague).

@tacaswell tacaswell added this to the v3.8.0 milestone Feb 22, 2023
@tacaswell tacaswell mentioned this pull request Feb 22, 2023
6 tasks
Comment on lines 570 to 572
In cases like this it may be possible to get into a debugging and from reading
the stack trace sort out right local variable to check is to start to sort out
what went wrong.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure what is meant by "to get into a debugging", and not sure it's made better in context by saying "to get into a debugger", which was my first guess.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did mean "debugger".

While this does tell the user what variable and value Matplotlib rejected, it still
does not tell them why.

An example of a **best** error message ::
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throwing your advice back to you, what about starting with the recommended best practice so that it won't get lost in skimming/fatigue? Then if it's still necessary, discuss unpack why the bad is bad - but honestly I'm not sure it's necessary given I think the paragraph under here does a good job of explaining why folks should do it this way.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with this - the first draft is pretty verbose....

Comment on lines 590 to 591
Which tells the user what input in broken, what was passed in, and the reason
why the value was invalid. All of this together gives the user the best chance
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a personal preference, but I like this sort of info broken out into a list:

We recommend writing an error message that states:

  1. which parameter is raising the error
  2. what value was passed
  3. why the value is incorrect

Comment on lines 570 to 572
In cases like this it may be possible to get into an interactive debugger and
from reading the stack trace sort out right local variable to check is to start
to sort out what went wrong. However, without access to the running process the user
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the and, this sentence kind of gets a bit muddled.

Internal helpers
~~~~~~~~~~~~~~~~

Matplotlib has number of helper functions in the ``matplotlib._api`` module
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Matplotlib has number of helper functions in the ``matplotlib._api`` module
Matplotlib has a number of helper functions in the ``matplotlib._api`` module

~~~~~~~~~~~~~~~~

Matplotlib has number of helper functions in the ``matplotlib._api`` module
named as ``check_*`` which provide nice error messages for very common checks
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
named as ``check_*`` which provide nice error messages for very common checks
named as ``check_*`` which provide nice error messages for common checks

very seems superfluous?


This message tells the user what (local) variable Matplotlib checked was and
what value was passed in which can help users rapidly find bugs in their own
code. For example if they expected to have passed in an integer but the error
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
code. For example if they expected to have passed in an integer but the error
code. For example, if they expected to have passed in an integer but the error

This message tells the user what (local) variable Matplotlib checked was and
what value was passed in which can help users rapidly find bugs in their own
code. For example if they expected to have passed in an integer but the error
messages reports the string ``'aardvark'``, they may know where in their code
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
messages reports the string ``'aardvark'``, they may know where in their code
message reports the string ``'aardvark'``, they may know where in their code

@oscargus
Copy link
Member

I'm missing info about dropping punctuation characters and if an error message should be in Sentence case or nor.

@anntzer
Copy link
Contributor

anntzer commented Feb 23, 2023

Highly nitpicky, but my personal(?) view is to do what cpython does:

>>> object()()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'object' object is not callable
>>> object().bar
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute 'bar'
>>> getattr(object(), 42)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: attribute name must be string, not 'int'

i.e. no leading capital and no trailing punctuation.
However, if the error message spans multiple sentences, then all sentences should be capitalized and punctuated correctly. I try to avoid such cases, e.g. I would reword the error message of check_shape (currently 'arg' must be 2D with shape (M, 2). Your input has shape (3, 3).) to 'arg' must be 2D with shape (M, 2); your input has shape (3, 3)

@anntzer
Copy link
Contributor

anntzer commented Feb 23, 2023

I agree with others that this seems pretty verbose (though a good idea to document); a first try at editing (while keeping most of your wording) gives me

Error messages should err on the side of being verbose, descriptive, and
specific about what went wrong.  They should be informative enough so that a
typical user (not an expert of our API) can understand, debug, and fix their
problem based solely on the message, without the need to consult the online
documentation.

For example, input validation errors should include, where possible,
information about which input is invalid, what value was passed in, and why the
value is invalid, e.g. ::

    raise ValueError(f"{value=!r} is invalid, value must be a positive integer")

This message helps the user to quickly diagnose and fix their problem.

Remember that many other libraries are implemented on top of Matplotlib, and
therefore ``value`` may not even have been directly passed by the user, but
rather generated in some intermediate layer.  In such cases, this kind of
highly explicit messages can be particularly useful, compared to shorter
messages such as ::

    raise ValueError("invalid value")

Co-authored-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>
Co-authored-by: Antony Lee <anntzer.lee@gmail.com>
Co-authored-by: Oscar Gustafsson <oscar.gustafsson@gmail.com>
@tacaswell
Copy link
Member Author

I took @anntzer 's wording whole sale and applied the other changes.

Comment on lines +556 to +559
--------------


Error messages should err on the side of being verbose, descriptive, and
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
--------------
Error messages should err on the side of being verbose, descriptive, and
--------------
Error messages should err on the side of being verbose, descriptive, and

Comment on lines +594 to +605
.. currentmodule:: matplotlib


.. autosummary::
:toctree: _as_gen
:template: autosummary.rst
:nosignatures:

_api.check_isinstance
_api.check_in_list
_api.check_shape
_api.check_getitem
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These appear to already have been documented somewhere since the build is failing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants