Repository: matplotlib/matplotlib
Pull request: Add earlier color validation
## PR Summary

This PR fixes #17865 by raising an error when an invalid `color` argument is passed.

```
>>> plt.plot([1, 2], c="foobar")
ValueError: 'foobar' is not a valid value for color
```


I used the `_check_in_list` method of `cbook` but had to add an additional parameter to avoid printing all the color values and flood the terminal.

## PR Checklist

- [x] Code is [Flake 8](http://flake8.pycqa.org/en/latest/) compliant



<!--
Thank you so much for your PR!  To help us review your contribution, please
consider the following points:

- A development guide is available at https://matplotlib.org/devdocs/devel/index.html.

- Help with git and github is available at
  https://matplotlib.org/devel/gitwash/development_workflow.html.

- Do not create the PR out of master, but out of a separate branch.

- The PR title should summarize the changes, for example "Raise ValueError on
  non-numeric input to set_xlim".  Avoid non-descriptive titles such as
  "Addresses issue #8576".

- The summary should provide at least 1-2 sentences describing the pull request
  in detail (Why is this change required?  What problem does it solve?) and
  link to any relevant issues.

- If you are contributing fixes to docstrings, please pay attention to
  http://matplotlib.org/devel/documenting_mpl.html#formatting.  In particular,
  note the difference between using single backquotes, double backquotes, and
  asterisks in the markup.

We understand that PRs can sometimes be overwhelming, especially as the
reviews start coming in.  Please let us know if the reviews are unclear or
the recommended next step seems overly demanding, if you would like help in
addressing a reviewer's comments, or if you have been waiting too long to hear
back on your PR.
-->


Problem statement:
Earlier validation of color inputs
<!--To help us understand and resolve your issue, please fill out the form to the best of your ability.-->
<!--You can feel free to delete the sections that do not apply.-->

### Bug report

**Bug summary**

Currently Line2D only validates `color` at draw time, which can lead to somewhat confusing errors when working interactively.  (Likely the same argument applies to facecolor, edgecolor, markerfacecolor, markeredgecolor, other artists, etc.)

**Code for reproduction**

At an IPython interactive console.
```
plt.plot([1, 2], c="foobar")
```

**Actual outcome**

The bad color causes an asynchronous traceback to be shown at draw time, and the figure is stuck in a bad state, partially drawn, because the error occurs in the middle of the draw loop; further attempts at drawing on the same figure fail in the same way because the "bad" Line2D artist is still among the children.  In theory one could probably fix that with something like `plt.gca().lines[-1].remove()` to remove the "bad" artist, but that requires rather involved knowledge of the architecture, especially when the figure window is not actually showing anything.

**Expected outcome**

An immediate exception and traceback, as one would get e.g. with `plt.plot([1, 2], lw="foobar")`.

Note that we can't *store* the validated color immediately (because of currently awkward semantics about when Cn colors get resolved), but whether a certain input is a valid color does not depend on the current color cycle.

Labeling as good first issue because I *think* it's just a matter of adding validation calls in a few places, but I didn't actually try :)

**Matplotlib version**
<!--Please specify your platform and versions of the relevant libraries you are using:-->
  * Operating system: fedora
  * Matplotlib version: master
  * Matplotlib backend (`print(matplotlib.get_backend())`): qt5agg
  * Python version: 38
  * Jupyter version (if applicable):
  * Other libraries: 

<!--Please tell us how you installed matplotlib and python e.g., from source, pip, conda-->
<!--If you installed from conda, please specify which channel you used if not the default-->

Hints from issue discussion:

Commit message: Add earlier color validation

Patch to review:
diff --git a/lib/matplotlib/cbook/__init__.py b/lib/matplotlib/cbook/__init__.py
index dd8b6aed9b8a..cf29b569eb1d 100644
--- a/lib/matplotlib/cbook/__init__.py
+++ b/lib/matplotlib/cbook/__init__.py
@@ -2242,7 +2242,7 @@ def type_name(tp):
                     type_name(type(v))))
 
 
-def _check_in_list(_values, **kwargs):
+def _check_in_list(_values, _print_supported_values=True, **kwargs):
     """
     For each *key, value* pair in *kwargs*, check that *value* is in *_values*;
     if not, raise an appropriate ValueError.
@@ -2254,9 +2254,13 @@ def _check_in_list(_values, **kwargs):
     values = _values
     for k, v in kwargs.items():
         if v not in values:
-            raise ValueError(
-                "{!r} is not a valid value for {}; supported values are {}"
-                .format(v, k, ', '.join(map(repr, values))))
+            if _print_supported_values:
+                raise ValueError(
+                    "{!r} is not a valid value for {}; supported values are {}"
+                    .format(v, k, ', '.join(map(repr, values))))
+            else:
+                raise ValueError(
+                    "{!r} is not a valid value for {}".format(v, k))
 
 
 def _check_shape(_shape, **kwargs):
diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py
index 6efb42d0e8ef..ae7d271d1f3a 100644
--- a/lib/matplotlib/lines.py
+++ b/lib/matplotlib/lines.py
@@ -14,6 +14,7 @@
 from .artist import Artist, allow_rasterization
 from .cbook import (
     _to_unmasked_float_array, ls_mapper, ls_mapper_r, STEP_LOOKUP_MAP)
+from .colors import is_color_like, get_named_colors_mapping
 from .markers import MarkerStyle
 from .path import Path
 from .transforms import (
@@ -1039,6 +1040,9 @@ def set_color(self, color):
         ----------
         color : color
         """
+        if not is_color_like(color):
+            cbook._check_in_list(get_named_colors_mapping(),
+                                 _print_supported_values=False, color=color)
         self._color = color
         self.stale = True
 


Code review comments:
diff_hunk: @@ -2242,7 +2242,7 @@ def type_name(tp):
                     type_name(type(v))))
 
 
-def _check_in_list(_values, **kwargs):
+def _check_in_list(_values, _print_supported_values=True, **kwargs):
path: lib/matplotlib/cbook/__init__.py
text: @user1:
can you make this a keyword only argument?

@author:
It's done. Thanks for the suggestion!