From 5d471d69cdab2db123e26bf3cf15ca3960552fc2 Mon Sep 17 00:00:00 2001 From: Kyle Sunden Date: Fri, 3 Feb 2023 13:29:43 -0600 Subject: [PATCH 1/4] Add ruff config to pyproject.toml for devs who are interested The change to legend_handler was a numpydoc docstring with an errant black line after the Parameters section header. It was flagged by ruff (but not flake8), but I agree that it should be changed after reviewing numpydoc standards. Applying the `ruff --fix .` removed the blank line, and that was the only flagged error after converting the flake8 config to ruff config. While I was looking at the config, I realized that some of the E502 (line length) ignores were out of date since we bumped the line length to 88, so I removed those ignores from both configs. I did not change any files to adhere to the new limit, only removed ignores which failed the 79 limit but pass at 88. One note is that the flake8 config selects `C90` (mccabe complexity) but does not set a `max-complexity` value, meaning that the check is ignored. For ruff, a default complexity (10) is selected, and we have many methods which fail (our worst is 73 in sankey, but ~180 above 10). As such I removed it from the ruff config, rather than adding ignores, etc. At this time, I'm not enabling ruff on CI because it is missing 9 checks selected by flake8. Once those checks are implemented (or at least the more commonly applicable ones, not too worried about "semicolon at end of line", if I'm honest), may look to swap over because it is faster. (It already takes only ~1 minute for the linter CI to run most of the time, but if it it can get down to basically the startup/queue time, that would be just that little bit faster that you may see it before you context switch) All of the missing checks are from pycodestyle, but running pycodestyle outside of flake8 runs single threaded, so the wall clock time savings don't add up. Combined with the increased complexity of running more than one tool, not worth it to implement CI yet. --- .flake8 | 5 - lib/matplotlib/legend_handler.py | 1 - pyproject.toml | 162 ++++++++++++++++++++++++++++++- 3 files changed, 161 insertions(+), 7 deletions(-) diff --git a/.flake8 b/.flake8 index 3fba9604fab8..ad197ac8a1bf 100644 --- a/.flake8 +++ b/.flake8 @@ -42,7 +42,6 @@ exclude = per-file-ignores = setup.py: E402 - lib/matplotlib/__init__.py: E402, F401 lib/matplotlib/_animation_data.py: E501 lib/matplotlib/_api/__init__.py: F401 @@ -81,9 +80,6 @@ per-file-ignores = tutorials/text/mathtext.py: E501 tutorials/text/text_intro.py: E402 tutorials/text/text_props.py: E501 - tutorials/text/usetex.py: E501 - tutorials/toolkits/axes_grid.py: E501 - tutorials/toolkits/axisartist.py: E501 examples/animation/frame_grabbing_sgskip.py: E402 examples/images_contours_and_fields/tricontour_demo.py: E201 @@ -93,7 +89,6 @@ per-file-ignores = examples/misc/print_stdout_sgskip.py: E402 examples/misc/table_demo.py: E201 examples/style_sheets/bmh.py: E501 - examples/style_sheets/plot_solarizedlight2.py: E501 examples/subplots_axes_and_figures/demo_constrained_layout.py: E402 examples/text_labels_and_annotations/custom_legends.py: E402 examples/ticks/date_concise_formatter.py: E402 diff --git a/lib/matplotlib/legend_handler.py b/lib/matplotlib/legend_handler.py index 849644145856..da118ed0b978 100644 --- a/lib/matplotlib/legend_handler.py +++ b/lib/matplotlib/legend_handler.py @@ -63,7 +63,6 @@ def __init__(self, xpad=0., ypad=0., update_func=None): """ Parameters ---------- - xpad : float, optional Padding in x-direction. ypad : float, optional diff --git a/pyproject.toml b/pyproject.toml index 8c51b1556a3b..4f7b0459ad46 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,6 @@ requires = [ "setuptools_scm>=7", ] - [tool.isort] known_mpltoolkits = "mpl_toolkits" known_pydata = "numpy, matplotlib.pyplot" @@ -15,3 +14,164 @@ known_firstparty = "matplotlib" sections = "FUTURE,STDLIB,THIRDPARTY,PYDATA,FIRSTPARTY,MPLTOOLKITS,LOCALFOLDER" no_lines_before = "MPLTOOLKITS" force_sort_within_sections = true + +[tool.ruff] +exclude = [ + ".git", + "build", + "doc/gallery", + "doc/tutorials", + "tools/gh_api.py", + ".tox", + ".eggs", +] +ignore = [ + "D100", + "D101", + "D102", + "D103", + "D104", + "D105", + "D106", + "D107", + "D200", + "D202", + "D203", + "D204", + "D205", + "D207", + "D212", + "D301", + "D400", + "D401", + "D402", + "D403", + "D404", + "D413", + "E722", + "E741", + "F841", + "N801", + "N802", + "N803", + "N806", + "N812", +] +line-length = 88 +select = [ + "D100", + "D101", + "D102", + "D103", + "D104", + "D105", + "D106", + "D200", + "D201", + "D202", + "D204", + "D205", + "D206", + "D207", + "D208", + "D209", + "D210", + "D211", + "D213", + "D214", + "D215", + "D300", + "D301", + "D400", + "D401", + "D403", + "D404", + "D405", + "D406", + "D407", + "D408", + "D409", + "D410", + "D411", + "D412", + "D414", + "E", + "F", + "W", +] + +# The following error codes are not supported by ruff v0.0.240 +# They are planned and should be selected once implemented +# even if they are deselected by default. +# These are primarily whitespace/corrected by autoformatters (which we don't use). +# See https://github.com/charliermarsh/ruff/issues/2402 for status on implementation +external = [ + "E122", + "E201", + "E202", + "E203", + "E221", + "E251", + "E261", + "E272", + "E302", + "E703", +] + +target-version = "py39" + +[tool.ruff.pydocstyle] +convention = "numpy" + +[tool.ruff.per-file-ignores] +"setup.py" = ["E402"] + +"doc/conf.py" = ["E402"] +"examples/animation/frame_grabbing_sgskip.py" = ["E402"] +"examples/lines_bars_and_markers/marker_reference.py" = ["E402"] +"examples/misc/print_stdout_sgskip.py" = ["E402"] +"examples/style_sheets/bmh.py" = ["E501"] +"examples/subplots_axes_and_figures/demo_constrained_layout.py" = ["E402"] +"examples/text_labels_and_annotations/custom_legends.py" = ["E402"] +"examples/ticks/date_concise_formatter.py" = ["E402"] +"examples/ticks/date_formatters_locators.py" = ["F401"] +"examples/user_interfaces/embedding_in_gtk3_panzoom_sgskip.py" = ["E402"] +"examples/user_interfaces/embedding_in_gtk3_sgskip.py" = ["E402"] +"examples/user_interfaces/embedding_in_gtk4_panzoom_sgskip.py" = ["E402"] +"examples/user_interfaces/embedding_in_gtk4_sgskip.py" = ["E402"] +"examples/user_interfaces/gtk3_spreadsheet_sgskip.py" = ["E402"] +"examples/user_interfaces/gtk4_spreadsheet_sgskip.py" = ["E402"] +"examples/user_interfaces/mpl_with_glade3_sgskip.py" = ["E402"] +"examples/user_interfaces/pylab_with_gtk3_sgskip.py" = ["E402"] +"examples/user_interfaces/pylab_with_gtk4_sgskip.py" = ["E402"] +"examples/userdemo/pgf_preamble_sgskip.py" = ["E402"] + +"lib/matplotlib/__init__.py" = ["E402", "F401"] +"lib/matplotlib/_animation_data.py" = ["E501"] +"lib/matplotlib/_api/__init__.py" = ["F401"] +"lib/matplotlib/axes/__init__.py" = ["F401", "F403"] +"lib/matplotlib/backends/backend_template.py" = ["F401"] +"lib/matplotlib/font_manager.py" = ["E501"] +"lib/matplotlib/image.py" = ["F401", "F403"] +"lib/matplotlib/pylab.py" = ["F401", "F403"] +"lib/matplotlib/pyplot.py" = ["F401", "F811"] +"lib/matplotlib/tests/test_mathtext.py" = ["E501"] +"lib/mpl_toolkits/axisartist/__init__.py" = ["F401"] +"lib/pylab.py" = ["F401", "F403"] + +"tutorials/advanced/path_tutorial.py" = ["E402"] +"tutorials/advanced/patheffects_guide.py" = ["E402"] +"tutorials/advanced/transforms_tutorial.py" = ["E402", "E501"] +"tutorials/colors/colormaps.py" = ["E501"] +"tutorials/colors/colors.py" = ["E402"] +"tutorials/intermediate/artists.py" = ["E402"] +"tutorials/intermediate/constrainedlayout_guide.py" = ["E402"] +"tutorials/intermediate/legend_guide.py" = ["E402"] +"tutorials/intermediate/tight_layout_guide.py" = ["E402"] +"tutorials/introductory/animation_tutorial.py" = ["E501"] +"tutorials/introductory/images.py" = ["E501"] +"tutorials/introductory/pyplot.py" = ["E402", "E501"] +"tutorials/text/annotations.py" = ["E402", "E501"] +"tutorials/text/mathtext.py" = ["E501"] +"tutorials/text/text_intro.py" = ["E402"] +"tutorials/text/text_props.py" = ["E501"] From a49b9d0c36ae3c49d8b31bd1d93334c9d5670da2 Mon Sep 17 00:00:00 2001 From: Kyle Sunden Date: Mon, 6 Feb 2023 23:22:58 -0600 Subject: [PATCH 2/4] Clean up ruff config for rules both selected and ignored I already did configure it to use numpy style docstrings, which automatically disables several of the ignored rules (without having to specify) Thus selecting all "D" rules and only ignoring has the same effect, and cleans up the config quite a bit. Additionally, the following rules from the ignore list had only a single occurrence, so I may as well fix that occurrence and enforce the rule: - D207: Docstring is under-indented - just a single missing space in the closing `"""` for one docstring - D403: First word of the first line should be properly capitalized - No occurrences, if no objection to enforcing this going forward, may as well enable - E722: do not use bare except, specify exception instead - single occurence in tests, added a noqa comment (could alternatively add to per file ignore if we wish) Additionally, selecting all pydocstyle (`D`) enabled the previously unselected D419: Docstring is empty. There was a single occurrence of an empty docstring, in scale.py:LinearScale, which has a comment stating that the method is there explicitly to prevent inherited docstring. - noqa comment added, enforcing the rule otherwise The N error codes (enforcing naming conventions) that were included in the ignore list were also never selected (and selecting "N" was rather noisy even with the ignores). These have been removed --- lib/matplotlib/scale.py | 2 +- lib/matplotlib/tests/test_font_manager.py | 2 +- lib/mpl_toolkits/mplot3d/art3d.py | 2 +- pyproject.toml | 50 +---------------------- 4 files changed, 4 insertions(+), 52 deletions(-) diff --git a/lib/matplotlib/scale.py b/lib/matplotlib/scale.py index e278c7464cc0..e57a9735db80 100644 --- a/lib/matplotlib/scale.py +++ b/lib/matplotlib/scale.py @@ -98,7 +98,7 @@ def __init__(self, axis): # constructor docstring, which would otherwise end up interpolated into # the docstring of Axis.set_scale. """ - """ + """ # noqa: D419 def set_default_locators_and_formatters(self, axis): # docstring inherited diff --git a/lib/matplotlib/tests/test_font_manager.py b/lib/matplotlib/tests/test_font_manager.py index 3724db1e1b43..64db62a665da 100644 --- a/lib/matplotlib/tests/test_font_manager.py +++ b/lib/matplotlib/tests/test_font_manager.py @@ -317,7 +317,7 @@ def test_get_font_names(): font = ft2font.FT2Font(path) prop = ttfFontProperty(font) ttf_fonts.append(prop.name) - except: + except: # noqa: E722 pass available_fonts = sorted(list(set(ttf_fonts))) mpl_font_names = sorted(fontManager.get_font_names()) diff --git a/lib/mpl_toolkits/mplot3d/art3d.py b/lib/mpl_toolkits/mplot3d/art3d.py index 71cdf3d1ccc5..3afd8f216475 100644 --- a/lib/mpl_toolkits/mplot3d/art3d.py +++ b/lib/mpl_toolkits/mplot3d/art3d.py @@ -91,7 +91,7 @@ class Text3D(mtext.Text): ---------------- **kwargs All other parameters are passed on to `~matplotlib.text.Text`. - """ + """ def __init__(self, x=0, y=0, z=0, text='', zdir='z', **kwargs): mtext.Text.__init__(self, x, y, text, **kwargs) diff --git a/pyproject.toml b/pyproject.toml index 4f7b0459ad46..34a28f99f4f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,68 +33,20 @@ ignore = [ "D104", "D105", "D106", - "D107", "D200", "D202", - "D203", "D204", "D205", - "D207", - "D212", "D301", "D400", "D401", - "D402", - "D403", "D404", - "D413", - "E722", "E741", "F841", - "N801", - "N802", - "N803", - "N806", - "N812", ] line-length = 88 select = [ - "D100", - "D101", - "D102", - "D103", - "D104", - "D105", - "D106", - "D200", - "D201", - "D202", - "D204", - "D205", - "D206", - "D207", - "D208", - "D209", - "D210", - "D211", - "D213", - "D214", - "D215", - "D300", - "D301", - "D400", - "D401", - "D403", - "D404", - "D405", - "D406", - "D407", - "D408", - "D409", - "D410", - "D411", - "D412", - "D414", + "D", "E", "F", "W", From cff05d93bbbfd86dceccd22e3199c5caed203998 Mon Sep 17 00:00:00 2001 From: Kyle Sunden Date: Tue, 7 Feb 2023 18:56:38 -0600 Subject: [PATCH 3/4] backport config changes to flake8 D403 re-ignored because flake8 flagged things ruff did not flake8 flagged two additional bare excepts (E722). ruff ignores if the exception is re-raised. --- .flake8 | 23 +++++++---------------- lib/matplotlib/tests/test_backend_qt.py | 4 ++-- pyproject.toml | 1 + 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/.flake8 b/.flake8 index ad197ac8a1bf..ec8be001a430 100644 --- a/.flake8 +++ b/.flake8 @@ -2,16 +2,7 @@ max-line-length = 88 select = # flake8 default - C90, E, F, W, - # docstring-convention=numpy - D100, D101, D102, D103, D104, D105, D106, - D200, D201, D202, D204, D205, D206, D207, D208, - D209, D210, D211, D214, D215, - D300, D301, D302, - D400, D401, D403, D404, D405, D406, D407, D408, - D409, D410, D411, D412, D414, - # matplotlib-specific extra pydocstyle errors - D213, + D, E, F, W, ignore = # flake8 default E121,E123,E126,E226,E24,E704,W503,W504, @@ -19,15 +10,15 @@ ignore = E127, E131, E266, E305, E306, - E722, E741, + E741, F841, - # Some new flake8 ignores: - N801, N802, N803, N806, N812, # pydocstyle - D100, D101, D102, D103, D104, D105, D106, D107, - D200, D202, D203, D204, D205, D207, D212, + D100, D101, D102, D103, D104, D105, D106, + D200, D202, D204, D205, D301, - D400, D401, D402, D403, D404, D413, + D400, D401, D403, D404 + # ignored by pydocstyle numpy docstring convention + D107, D203, D212, D213, D402, D413, D415, D416, D417, exclude = .git diff --git a/lib/matplotlib/tests/test_backend_qt.py b/lib/matplotlib/tests/test_backend_qt.py index 64d59b853fb0..85a3c36ddb21 100644 --- a/lib/matplotlib/tests/test_backend_qt.py +++ b/lib/matplotlib/tests/test_backend_qt.py @@ -128,7 +128,7 @@ def test_sigint(target, kwargs): try: proc.wait_for('DRAW') stdout, _ = proc.communicate(timeout=_test_timeout) - except: + except: # noqa: E722 proc.kill() stdout, _ = proc.communicate() raise @@ -182,7 +182,7 @@ def test_other_signal_before_sigint(target, kwargs): proc.wait_for('SIGUSR1') os.kill(proc.pid, signal.SIGINT) stdout, _ = proc.communicate(timeout=_test_timeout) - except: + except: # noqa: E722 proc.kill() stdout, _ = proc.communicate() raise diff --git a/pyproject.toml b/pyproject.toml index 34a28f99f4f3..c8b76f1e61de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,7 @@ ignore = [ "D301", "D400", "D401", + "D403", "D404", "E741", "F841", From f19b57c216709a50ed486fc932e7f8a1f7987aa5 Mon Sep 17 00:00:00 2001 From: Kyle Sunden Date: Wed, 8 Feb 2023 17:20:13 -0600 Subject: [PATCH 4/4] Use except Exception in place of bare except --- lib/matplotlib/tests/test_backend_qt.py | 4 ++-- lib/matplotlib/tests/test_font_manager.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/tests/test_backend_qt.py b/lib/matplotlib/tests/test_backend_qt.py index 85a3c36ddb21..cf82b900bf39 100644 --- a/lib/matplotlib/tests/test_backend_qt.py +++ b/lib/matplotlib/tests/test_backend_qt.py @@ -128,7 +128,7 @@ def test_sigint(target, kwargs): try: proc.wait_for('DRAW') stdout, _ = proc.communicate(timeout=_test_timeout) - except: # noqa: E722 + except Exception: proc.kill() stdout, _ = proc.communicate() raise @@ -182,7 +182,7 @@ def test_other_signal_before_sigint(target, kwargs): proc.wait_for('SIGUSR1') os.kill(proc.pid, signal.SIGINT) stdout, _ = proc.communicate(timeout=_test_timeout) - except: # noqa: E722 + except Exception: proc.kill() stdout, _ = proc.communicate() raise diff --git a/lib/matplotlib/tests/test_font_manager.py b/lib/matplotlib/tests/test_font_manager.py index 64db62a665da..966539088e42 100644 --- a/lib/matplotlib/tests/test_font_manager.py +++ b/lib/matplotlib/tests/test_font_manager.py @@ -317,7 +317,7 @@ def test_get_font_names(): font = ft2font.FT2Font(path) prop = ttfFontProperty(font) ttf_fonts.append(prop.name) - except: # noqa: E722 + except Exception: pass available_fonts = sorted(list(set(ttf_fonts))) mpl_font_names = sorted(fontManager.get_font_names())