From d6a461255a11b9874bd7bd6c8a6fb82e96e03e94 Mon Sep 17 00:00:00 2001 From: Wei Ji <23487320+weiji14@users.noreply.github.com> Date: Wed, 25 Oct 2023 17:31:14 +1300 Subject: [PATCH 1/6] CI: Use ruff format to ensure UNIX line endings in python files Automatically convert line endings in Python files to `\n` (UNIX style). Requires using beta version of Ruff formatter in v0.1.2. Also sorting ruff sections in pyproject.toml alphabetically. --- Makefile | 4 ++-- pyproject.toml | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 3fcab6575ad..2049386c324 100644 --- a/Makefile +++ b/Makefile @@ -63,13 +63,13 @@ format: docformatter --in-place $(FORMAT_FILES) black $(FORMAT_FILES) blackdoc $(FORMAT_FILES) - ruff check --fix $(FORMAT_FILES) + ruff format $(FORMAT_FILES) check: docformatter --check $(FORMAT_FILES) black --check $(FORMAT_FILES) blackdoc --check $(FORMAT_FILES) - ruff check $(FORMAT_FILES) + ruff format --check $(FORMAT_FILES) codespell: @codespell diff --git a/pyproject.toml b/pyproject.toml index 054a4d2d395..bb07dfadcad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -97,14 +97,17 @@ select = [ ] ignore = ["E501"] # Avoid enforcing line-length violations +[tool.ruff.isort] +known-third-party = ["pygmt"] + [tool.ruff.pycodestyle] max-doc-length = 79 [tool.ruff.per-file-ignores] "__init__.py" = ["F401"] # Ignore `F401` (unused-import) in all `__init__.py` files -[tool.ruff.isort] -known-third-party = ["pygmt"] +[tool.ruff.format] +line-ending = "lf" # Use UNIX `\n` line endings for all files [tool.pytest.ini_options] minversion = "6.0" From e273441752688d97d0d5ce837d14b88b6245c740 Mon Sep 17 00:00:00 2001 From: Wei Ji <23487320+weiji14@users.noreply.github.com> Date: Wed, 25 Oct 2023 17:44:58 +1300 Subject: [PATCH 2/6] Position some inline comments differently to make ruff linter happy Some deviations between the black and ruff linters on where to place inline comments. --- pygmt/tests/test_basemap.py | 3 ++- pygmt/tests/test_clib_loading.py | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/pygmt/tests/test_basemap.py b/pygmt/tests/test_basemap.py index 340d19c2647..a78d909cd40 100644 --- a/pygmt/tests/test_basemap.py +++ b/pygmt/tests/test_basemap.py @@ -79,7 +79,8 @@ def test_basemap_utm_projection(projection): works. """ projection = projection.replace( - "EPSG_", "EPSG:" # workaround Windows not allowing colons in filenames + "EPSG_", + "EPSG:", # workaround Windows not allowing colons in filenames ) fig = Figure() fig.basemap(region=[-52, -50, -12, -11], projection=projection, frame="afg") diff --git a/pygmt/tests/test_clib_loading.py b/pygmt/tests/test_clib_loading.py index 055badd8656..63192946121 100644 --- a/pygmt/tests/test_clib_loading.py +++ b/pygmt/tests/test_clib_loading.py @@ -150,9 +150,9 @@ def test_two_broken_libraries(self, mock_ctypes): # pylint: disable=unused-argu with pytest.raises(GMTCLibNotFoundError, match=msg_regex): load_libgmt(lib_fullnames=lib_fullnames) - def test_load_brokenlib_invalidpath( + def test_load_brokenlib_invalidpath( # pylint: disable=unused-argument self, mock_ctypes - ): # pylint: disable=unused-argument + ): """ Case 2: broken library + invalid path. @@ -170,36 +170,36 @@ def test_load_brokenlib_invalidpath( with pytest.raises(GMTCLibNotFoundError, match=msg_regex): load_libgmt(lib_fullnames=lib_fullnames) - def test_brokenlib_invalidpath_workinglib( + def test_brokenlib_invalidpath_workinglib( # pylint: disable=unused-argument self, mock_ctypes - ): # pylint: disable=unused-argument + ): """ Case 3: broken library + invalid path + working library. """ lib_fullnames = [self.faked_libgmt1, self.invalid_path, self.loaded_libgmt] assert check_libgmt(load_libgmt(lib_fullnames=lib_fullnames)) is None - def test_invalidpath_brokenlib_workinglib( + def test_invalidpath_brokenlib_workinglib( # pylint: disable=unused-argument self, mock_ctypes - ): # pylint: disable=unused-argument + ): """ Case 4: invalid path + broken library + working library. """ lib_fullnames = [self.invalid_path, self.faked_libgmt1, self.loaded_libgmt] assert check_libgmt(load_libgmt(lib_fullnames=lib_fullnames)) is None - def test_workinglib_brokenlib_invalidpath( + def test_workinglib_brokenlib_invalidpath( # pylint: disable=unused-argument self, mock_ctypes - ): # pylint: disable=unused-argument + ): """ Case 5: working library + broken library + invalid path. """ lib_fullnames = [self.loaded_libgmt, self.faked_libgmt1, self.invalid_path] assert check_libgmt(load_libgmt(lib_fullnames=lib_fullnames)) is None - def test_brokenlib_brokenlib_workinglib( + def test_brokenlib_brokenlib_workinglib( # pylint: disable=unused-argument self, mock_ctypes - ): # pylint: disable=unused-argument + ): """ Case 6: repeating broken libraries + working library. """ From c4c7e30e9cb2b506cf806022de9c5f35345253f3 Mon Sep 17 00:00:00 2001 From: Wei Ji <23487320+weiji14@users.noreply.github.com> Date: Wed, 25 Oct 2023 17:50:23 +1300 Subject: [PATCH 3/6] Try changing line endings to CRLF --- pygmt/io.py | 92 ++++++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/pygmt/io.py b/pygmt/io.py index 70bcfbfcdb9..1a41bf1fd14 100644 --- a/pygmt/io.py +++ b/pygmt/io.py @@ -1,46 +1,46 @@ -""" -PyGMT input/output (I/O) utilities. -""" -import xarray as xr - - -def load_dataarray(filename_or_obj, **kwargs): - """ - Open, load into memory, and close a DataArray from a file or file-like - object containing a single data variable. - - This is a thin wrapper around :py:func:`xarray.open_dataarray`. It differs - from :py:func:`xarray.open_dataarray` in that it loads the DataArray into - memory, gets GMT specific metadata about the grid via - :py:meth:`GMTDataArrayAccessor`, closes the file, and returns the - DataArray. In contrast, :py:func:`xarray.open_dataarray` keeps the file - handle open and lazy loads its contents. All parameters are passed directly - to :py:func:`xarray.open_dataarray`. See that documentation for further - details. - - Parameters - ---------- - filename_or_obj : str or pathlib.Path or file-like or DataStore - Strings and Path objects are interpreted as a path to a netCDF file - or an OpenDAP URL and opened with python-netCDF4, unless the filename - ends with .gz, in which case the file is gunzipped and opened with - scipy.io.netcdf (only netCDF3 supported). Byte-strings or file-like - objects are opened by scipy.io.netcdf (netCDF3) or h5py (netCDF4/HDF). - - Returns - ------- - datarray : xarray.DataArray - The newly created DataArray. - - See Also - -------- - xarray.open_dataarray - """ - if "cache" in kwargs: - raise TypeError("cache has no effect in this context") - - with xr.open_dataarray(filename_or_obj, **kwargs) as dataarray: - result = dataarray.load() - _ = result.gmt # load GMTDataArray accessor information - - return result +""" +PyGMT input/output (I/O) utilities. +""" +import xarray as xr + + +def load_dataarray(filename_or_obj, **kwargs): + """ + Open, load into memory, and close a DataArray from a file or file-like + object containing a single data variable. + + This is a thin wrapper around :py:func:`xarray.open_dataarray`. It differs + from :py:func:`xarray.open_dataarray` in that it loads the DataArray into + memory, gets GMT specific metadata about the grid via + :py:meth:`GMTDataArrayAccessor`, closes the file, and returns the + DataArray. In contrast, :py:func:`xarray.open_dataarray` keeps the file + handle open and lazy loads its contents. All parameters are passed directly + to :py:func:`xarray.open_dataarray`. See that documentation for further + details. + + Parameters + ---------- + filename_or_obj : str or pathlib.Path or file-like or DataStore + Strings and Path objects are interpreted as a path to a netCDF file + or an OpenDAP URL and opened with python-netCDF4, unless the filename + ends with .gz, in which case the file is gunzipped and opened with + scipy.io.netcdf (only netCDF3 supported). Byte-strings or file-like + objects are opened by scipy.io.netcdf (netCDF3) or h5py (netCDF4/HDF). + + Returns + ------- + datarray : xarray.DataArray + The newly created DataArray. + + See Also + -------- + xarray.open_dataarray + """ + if "cache" in kwargs: + raise TypeError("cache has no effect in this context") + + with xr.open_dataarray(filename_or_obj, **kwargs) as dataarray: + result = dataarray.load() + _ = result.gmt # load GMTDataArray accessor information + + return result From 22b194ce5fd0d08ae7c3252726ee55bcd870e1f1 Mon Sep 17 00:00:00 2001 From: actions-bot <58130806+actions-bot@users.noreply.github.com> Date: Wed, 25 Oct 2023 04:52:17 +0000 Subject: [PATCH 4/6] [format-command] fixes --- pygmt/io.py | 92 ++++++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/pygmt/io.py b/pygmt/io.py index 1a41bf1fd14..70bcfbfcdb9 100644 --- a/pygmt/io.py +++ b/pygmt/io.py @@ -1,46 +1,46 @@ -""" -PyGMT input/output (I/O) utilities. -""" -import xarray as xr - - -def load_dataarray(filename_or_obj, **kwargs): - """ - Open, load into memory, and close a DataArray from a file or file-like - object containing a single data variable. - - This is a thin wrapper around :py:func:`xarray.open_dataarray`. It differs - from :py:func:`xarray.open_dataarray` in that it loads the DataArray into - memory, gets GMT specific metadata about the grid via - :py:meth:`GMTDataArrayAccessor`, closes the file, and returns the - DataArray. In contrast, :py:func:`xarray.open_dataarray` keeps the file - handle open and lazy loads its contents. All parameters are passed directly - to :py:func:`xarray.open_dataarray`. See that documentation for further - details. - - Parameters - ---------- - filename_or_obj : str or pathlib.Path or file-like or DataStore - Strings and Path objects are interpreted as a path to a netCDF file - or an OpenDAP URL and opened with python-netCDF4, unless the filename - ends with .gz, in which case the file is gunzipped and opened with - scipy.io.netcdf (only netCDF3 supported). Byte-strings or file-like - objects are opened by scipy.io.netcdf (netCDF3) or h5py (netCDF4/HDF). - - Returns - ------- - datarray : xarray.DataArray - The newly created DataArray. - - See Also - -------- - xarray.open_dataarray - """ - if "cache" in kwargs: - raise TypeError("cache has no effect in this context") - - with xr.open_dataarray(filename_or_obj, **kwargs) as dataarray: - result = dataarray.load() - _ = result.gmt # load GMTDataArray accessor information - - return result +""" +PyGMT input/output (I/O) utilities. +""" +import xarray as xr + + +def load_dataarray(filename_or_obj, **kwargs): + """ + Open, load into memory, and close a DataArray from a file or file-like + object containing a single data variable. + + This is a thin wrapper around :py:func:`xarray.open_dataarray`. It differs + from :py:func:`xarray.open_dataarray` in that it loads the DataArray into + memory, gets GMT specific metadata about the grid via + :py:meth:`GMTDataArrayAccessor`, closes the file, and returns the + DataArray. In contrast, :py:func:`xarray.open_dataarray` keeps the file + handle open and lazy loads its contents. All parameters are passed directly + to :py:func:`xarray.open_dataarray`. See that documentation for further + details. + + Parameters + ---------- + filename_or_obj : str or pathlib.Path or file-like or DataStore + Strings and Path objects are interpreted as a path to a netCDF file + or an OpenDAP URL and opened with python-netCDF4, unless the filename + ends with .gz, in which case the file is gunzipped and opened with + scipy.io.netcdf (only netCDF3 supported). Byte-strings or file-like + objects are opened by scipy.io.netcdf (netCDF3) or h5py (netCDF4/HDF). + + Returns + ------- + datarray : xarray.DataArray + The newly created DataArray. + + See Also + -------- + xarray.open_dataarray + """ + if "cache" in kwargs: + raise TypeError("cache has no effect in this context") + + with xr.open_dataarray(filename_or_obj, **kwargs) as dataarray: + result = dataarray.load() + _ = result.gmt # load GMTDataArray accessor information + + return result From 6076a5058526bd8f0e84dd9db1d3689ede241670 Mon Sep 17 00:00:00 2001 From: Wei Ji <23487320+weiji14@users.noreply.github.com> Date: Thu, 16 Nov 2023 16:48:42 +1300 Subject: [PATCH 5/6] Run ruff check, then ruff format Run the linter `ruff check`, then the formatter `ruff format`. --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 2049386c324..9cc8db42795 100644 --- a/Makefile +++ b/Makefile @@ -63,12 +63,14 @@ format: docformatter --in-place $(FORMAT_FILES) black $(FORMAT_FILES) blackdoc $(FORMAT_FILES) + ruff check --fix $(FORMAT_FILES) ruff format $(FORMAT_FILES) check: docformatter --check $(FORMAT_FILES) black --check $(FORMAT_FILES) blackdoc --check $(FORMAT_FILES) + ruff check $(FORMAT_FILES) ruff format --check $(FORMAT_FILES) codespell: From 8c58089627dd64933ed304e9b1ff6c71729c43d1 Mon Sep 17 00:00:00 2001 From: Wei Ji <23487320+weiji14@users.noreply.github.com> Date: Thu, 16 Nov 2023 17:22:41 +1300 Subject: [PATCH 6/6] Put configs under tool.ruff.lint and sort toml alphabetically Most of ruff's lint rules are now under tool.ruff.lint, so rearranging things a bit. --- pyproject.toml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c6dc3a65f55..850664d0db9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -89,6 +89,11 @@ wrap-descriptions = 79 [tool.ruff] line-length = 88 # E501 (line-too-long) show-source = true + +[tool.ruff.format] +line-ending = "lf" # Use UNIX `\n` line endings for all files + +[tool.ruff.lint] select = [ "E", # pycodestyle "F", # pyflakes @@ -99,17 +104,14 @@ select = [ ] ignore = ["E501"] # Avoid enforcing line-length violations -[tool.ruff.isort] +[tool.ruff.lint.isort] known-third-party = ["pygmt"] -[tool.ruff.pycodestyle] -max-doc-length = 79 - -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] "__init__.py" = ["F401"] # Ignore `F401` (unused-import) in all `__init__.py` files -[tool.ruff.format] -line-ending = "lf" # Use UNIX `\n` line endings for all files +[tool.ruff.lint.pycodestyle] +max-doc-length = 79 [tool.pytest.ini_options] minversion = "6.0"