From 3f9d14b2d2f46053bad83fc2fef8af527f1fc67f Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 4 Jan 2024 09:10:35 +0800 Subject: [PATCH] Enable ruff's pydocstyle (D) rules and remove docformatter (#2925) * Enable ruff's pydocstyle (D) rules and set numpy convention * Fix D406: Section name should end with a newline ('Notes') * Fix D405L: Section name should be properly capitalized ('See also') * Fix D104 Missing docstring in public package * Fix D409: Section underline should match the length of its name * Ignore a D410/D411 violation * Fix D103: Missing docstring in public function * Fix D105: Missing docstring in magic method * Fix a formatting issue * Ignore some D rules ``` $ ruff check pygmt doc/conf.py examples --statistics 645 D200 [*] One-line docstring should fit on one line 312 D205 [ ] 1 blank line required between summary line and description 123 D400 [ ] First line should end with a period 30 D401 [ ] First line of docstring should be in imperative mood: "A mock GMT API function that always returns a given value." 30 D412 [*] No blank lines allowed between a section header and its content ("Examples") 12 D202 [*] No blank lines allowed after function docstring (found 1) ``` * Enabel D213 and D410 rules * Fully remove docformatter * Update the contributing guides --------- Co-authored-by: Wei Ji <23487320+weiji14@users.noreply.github.com> --- .github/workflows/format-command.yml | 2 +- .github/workflows/style_checks.yaml | 4 ++-- Makefile | 6 ++--- doc/contributing.md | 16 +++++-------- environment.yml | 1 - examples/gallery/3d_plots/grdview_surface.py | 3 +++ examples/gallery/lines/vector_styles.py | 4 +--- .../tutorials/advanced/working_with_panel.py | 8 +++++-- pygmt/figure.py | 4 +++- pygmt/helpers/decorators.py | 2 +- pygmt/helpers/tempfile.py | 6 +++++ pygmt/helpers/utils.py | 2 +- pygmt/src/config.py | 7 +++++- pygmt/src/grdhisteq.py | 6 ++--- pygmt/src/grdtrack.py | 2 +- pygmt/tests/__init__.py | 3 +++ pygmt/tests/test_clib_loading.py | 3 +++ pyproject.toml | 24 ++++++++++++------- 18 files changed, 64 insertions(+), 39 deletions(-) diff --git a/.github/workflows/format-command.yml b/.github/workflows/format-command.yml index 51f00f8fb1f..0cdad84b468 100644 --- a/.github/workflows/format-command.yml +++ b/.github/workflows/format-command.yml @@ -32,7 +32,7 @@ jobs: # Install formatting tools - name: Install formatting tools run: | - python -m pip install docformatter ruff + python -m pip install ruff python -m pip list sudo apt-get install dos2unix diff --git a/.github/workflows/style_checks.yaml b/.github/workflows/style_checks.yaml index bab24f9c1f5..16b376455f9 100644 --- a/.github/workflows/style_checks.yaml +++ b/.github/workflows/style_checks.yaml @@ -34,11 +34,11 @@ jobs: - name: Install packages run: | - python -m pip install docformatter ruff + python -m pip install ruff python -m pip list sudo apt-get install dos2unix - - name: Formatting check (docformatter, ruff) + - name: Formatting check (ruff) run: make check - name: Ensure files use UNIX line breaks and have 644 permission diff --git a/Makefile b/Makefile index 490797339a5..c3d59ce6848 100644 --- a/Makefile +++ b/Makefile @@ -15,8 +15,8 @@ help: @echo " fulltest run the test suite (including all doctests)" @echo " doctest run the doctests only" @echo " test_no_images run the test suite (including all doctests) but skip image comparisons" - @echo " format run docformatter and ruff to automatically format the code" - @echo " check run code style and quality checks (docformatter and ruff)" + @echo " format run ruff to automatically format the code" + @echo " check run ruff to check code style and quality" @echo " codespell run codespell to check common misspellings" @echo " typecheck run mypy for static type check" @echo " clean clean up build and generated files" @@ -60,12 +60,10 @@ test_no_images: PYTEST_ARGS=-o addopts="--verbose --durations=0 --durations-min= test_no_images: _runtest format: - docformatter --in-place $(FORMAT_FILES) ruff check --fix $(FORMAT_FILES) ruff format $(FORMAT_FILES) check: - docformatter --check $(FORMAT_FILES) ruff check $(FORMAT_FILES) ruff format --check $(FORMAT_FILES) diff --git a/doc/contributing.md b/doc/contributing.md index 5f560cca83e..ee58f8945c3 100644 --- a/doc/contributing.md +++ b/doc/contributing.md @@ -406,7 +406,7 @@ arguments and return values. While the maximum line length for code is automatically set by ruff, docstrings must be formatted manually. To play nicely with Jupyter and IPython, **keep docstrings -limited to 79 characters** per line. +limited to 88 characters** per line. ### Standards for Example Code @@ -471,14 +471,10 @@ code, be sure to follow the general guidelines in the ### Code Style -We use some tools to format the code so we don't have to think about it: - -- [docformatter](https://github.com/myint/docformatter) -- [ruff](https://docs.astral.sh/ruff) - -These tools loosely follow the [PEP8](http://pep8.org) guide but with a few -differences. Regardless, you won't have to worry about formatting the code yourself. -Before committing, run it to automatically format your code: +We use the [ruff](https://docs.astral.sh/ruff) tool to format the code, so we +don't have to think about it. It loosely follow the [PEP8](http://pep8.org) guide +but with a few differences. Regardless, you won't have to worry about formatting +the code yourself. Before committing, run it to automatically format your code: ```bash make format @@ -511,7 +507,7 @@ The [`Makefile`](https://github.com/GenericMappingTools/pygmt/blob/main/Makefile contains rules for running the linter checks: ```bash -make check # Runs docformatter and ruff (in check mode) +make check # Runs ruff in check mode ``` ### Testing your Code diff --git a/environment.yml b/environment.yml index dc40f3cf84e..de6870344bd 100644 --- a/environment.yml +++ b/environment.yml @@ -24,7 +24,6 @@ dependencies: - pip # Dev dependencies (style checks) - codespell - - docformatter>=1.7.2 - ruff>=0.1.9 # Dev dependencies (unit testing) - matplotlib diff --git a/examples/gallery/3d_plots/grdview_surface.py b/examples/gallery/3d_plots/grdview_surface.py index 97af6115e49..beec0eb6c4b 100644 --- a/examples/gallery/3d_plots/grdview_surface.py +++ b/examples/gallery/3d_plots/grdview_surface.py @@ -23,6 +23,9 @@ # Define an interesting function of two variables, see: # https://en.wikipedia.org/wiki/Ackley_function def ackley(x, y): + """ + Ackley function. + """ return ( -20 * np.exp(-0.2 * np.sqrt(0.5 * (x**2 + y**2))) - np.exp(0.5 * (np.cos(2 * np.pi * x) + np.cos(2 * np.pi * y))) diff --git a/examples/gallery/lines/vector_styles.py b/examples/gallery/lines/vector_styles.py index e3a13cdd5ee..726bfabd642 100644 --- a/examples/gallery/lines/vector_styles.py +++ b/examples/gallery/lines/vector_styles.py @@ -4,9 +4,7 @@ The :meth:`pygmt.Figure.plot` method can plot Cartesian, circular, and geographic vectors. The ``style`` parameter controls vector attributes. -See also -:doc:`Vector attributes example `. - +See also :doc:`Vector attributes example `. """ # %% diff --git a/examples/tutorials/advanced/working_with_panel.py b/examples/tutorials/advanced/working_with_panel.py index 83d31e2a355..cf0786c3b5e 100644 --- a/examples/tutorials/advanced/working_with_panel.py +++ b/examples/tutorials/advanced/working_with_panel.py @@ -73,9 +73,11 @@ ) -# Define a function for plotting the single slices @pn.depends(central_lon=slider_lon) def view(central_lon): + """ + Define a function for plotting the single slices. + """ # Create a new instance or object of the pygmt.Figure() class fig = pygmt.Figure() fig.coast( @@ -112,9 +114,11 @@ def view(central_lon): ) -# Define a function for plotting the single slices @pn.depends(central_lon=slider_lon) def view(central_lon): + """ + Define a function for plotting the single slices. + """ # Create a new instance or object of the pygmt.Figure() class fig = pygmt.Figure() # Set up a colormap for the elevation in meters diff --git a/pygmt/figure.py b/pygmt/figure.py index bcb5a84835c..587ce0fa4d9 100644 --- a/pygmt/figure.py +++ b/pygmt/figure.py @@ -88,7 +88,9 @@ def __init__(self): self._activate_figure() def __del__(self): - # Clean up the temporary directory that stores the previews + """ + Clean up the temporary directory that stores the previews. + """ if hasattr(self, "_preview_dir"): self._preview_dir.cleanup() diff --git a/pygmt/helpers/decorators.py b/pygmt/helpers/decorators.py index 2db4784fcca..57bd0f90014 100644 --- a/pygmt/helpers/decorators.py +++ b/pygmt/helpers/decorators.py @@ -445,7 +445,7 @@ def fmt_docstring(module_func): - J = projection - R = region - """ + """ # noqa: D410,D411 filler_text = {} if hasattr(module_func, "aliases"): diff --git a/pygmt/helpers/tempfile.py b/pygmt/helpers/tempfile.py index 7226e1b0d95..5545d2b3695 100644 --- a/pygmt/helpers/tempfile.py +++ b/pygmt/helpers/tempfile.py @@ -62,9 +62,15 @@ def __init__(self, prefix="pygmt-", suffix=".txt"): self.name = tmpfile.name def __enter__(self): + """ + Do nothing but return the object. + """ return self def __exit__(self, *args): + """ + Remove the temporary file. + """ if os.path.exists(self.name): os.remove(self.name) diff --git a/pygmt/helpers/utils.py b/pygmt/helpers/utils.py index c814ce99f99..0e7bb93f71d 100644 --- a/pygmt/helpers/utils.py +++ b/pygmt/helpers/utils.py @@ -529,7 +529,7 @@ def args_in_kwargs(args, kwargs): short-form aliases of the parameters. Returns - -------- + ------- bool If one of the required arguments is in ``kwargs``. diff --git a/pygmt/src/config.py b/pygmt/src/config.py index c684b745e77..dd0854a296b 100644 --- a/pygmt/src/config.py +++ b/pygmt/src/config.py @@ -203,10 +203,15 @@ def __init__(self, **kwargs): lib.call_module(module="set", args=arg_str) def __enter__(self): + """ + Do nothing but return the object. + """ return self def __exit__(self, exc_type, exc_value, traceback): - # revert to initial values + """ + Revert GMT configurations to initial values. + """ arg_str = " ".join( [f'{key}="{value}"' for key, value in self.old_defaults.items()] ) diff --git a/pygmt/src/grdhisteq.py b/pygmt/src/grdhisteq.py index 97d44048264..c804d8f8f28 100644 --- a/pygmt/src/grdhisteq.py +++ b/pygmt/src/grdhisteq.py @@ -102,7 +102,7 @@ def _grdhisteq(grid, output_type, **kwargs): ``outgrid`` or ``outfile``) See Also - ------- + -------- :func:`pygmt.grd2cpt` """ @@ -194,7 +194,7 @@ def equalize_grid( >>> grid = pygmt.grdhisteq.equalize_grid(grid=grid, gaussian=True) See Also - ------- + -------- :func:`pygmt.grd2cpt` Note @@ -306,7 +306,7 @@ def compute_bins( 4 705.0 2275.5 See Also - ------- + -------- :func:`pygmt.grd2cpt` Note diff --git a/pygmt/src/grdtrack.py b/pygmt/src/grdtrack.py index c8d7a2b0d97..1d0daa1b800 100644 --- a/pygmt/src/grdtrack.py +++ b/pygmt/src/grdtrack.py @@ -209,7 +209,7 @@ def grdtrack(grid, points=None, newcolname=None, outfile=None, **kwargs): - **+c**\ *fact* : Compute envelope on stacked profile as ±\ *fact* \*\ *deviation* [Default fact value is 2]. - Notes: + Here are some notes: 1. Deviations depend on *method* and are st.dev (**a**), L1 scale, i.e., 1.4826 \* median absolute deviation (MAD) (for **m** and diff --git a/pygmt/tests/__init__.py b/pygmt/tests/__init__.py index e69de29bb2d..f8ee84678e6 100644 --- a/pygmt/tests/__init__.py +++ b/pygmt/tests/__init__.py @@ -0,0 +1,3 @@ +""" +PyGMT test suite. +""" diff --git a/pygmt/tests/test_clib_loading.py b/pygmt/tests/test_clib_loading.py index 33d1cf2081d..513523917b3 100644 --- a/pygmt/tests/test_clib_loading.py +++ b/pygmt/tests/test_clib_loading.py @@ -24,6 +24,9 @@ def __init__(self, name): self._name = name def __str__(self): + """ + String representation of the object. + """ return self._name diff --git a/pyproject.toml b/pyproject.toml index cf8a3580619..99f50767ae3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,14 +75,6 @@ ignore-words-list = "astroid,oints,reenable,tripel,trough" [tool.coverage.run] omit = ["*/tests/*", "*pygmt/__init__.py"] -[tool.docformatter] -black = true -recursive = true -pre-summary-newline = true -make-summary-multi-line = true -wrap-summaries = 88 -wrap-descriptions = 88 - [tool.mypy] exclude = ["pygmt/tests/"] ignore_missing_imports = true @@ -103,6 +95,7 @@ select = [ "B", # flake8-bugbear "BLE", # flake8-blind-except "C4", # flake8-comprehensions + "D", # pydocstyle "E", # pycodestyle "EXE", # flake8-executable "F", # pyflakes @@ -131,7 +124,17 @@ select = [ "W", # pycodestyle warnings "YTT", # flake8-2020 ] +extend-select = [ + "D213", # Summary lines should be positioned on the second physical line of the docstring. + "D410", # A blank line after section headings. +] ignore = [ + "D200", # One-line docstring should fit on one line + "D202", # No blank lines allowed after function docstring + "D205", # 1 blank line required between summary line and description + "D400", # First line should end with a period + "D401", # First line of docstring should be in imperative mood + "D412", # No blank lines allowed between a section header and its content "E501", # Avoid enforcing line-length violations "ISC001", # Single-line-implicit-string-concatenation, conflict with formatter "PD901", # Allow using the generic variable name `df` for DataFrames @@ -153,6 +156,11 @@ known-third-party = ["pygmt"] [tool.ruff.lint.pycodestyle] max-doc-length = 88 +[tool.ruff.lint.pydocstyle] +# See https://docs.astral.sh/ruff/faq/#does-ruff-support-numpy-or-google-style-docstrings +# for the enabled/disabled rules for the "numpy" convention. +convention = "numpy" + [tool.ruff.lint.pylint] max-args=10