Skip to content

Commit 09f9235

Browse files
authored
style: Use ruff format instead of black . for improved user experience (#5023)
* style: Disable Flake8 line length checks as handled by our formatting tools * Apply `ruff format` to potentially unexpected tuples in gui/wxpython/rlisetup/wizard.py * style: Apply `ruff format`-specific formatting for 2025 style * style: Disable Flake8 line length checks as handled by our formatting tools * checks: Replace black with ruff format in pre-commit * CI: Implement checks and PR suggestions for `ruff format` * python: Address misplace to and from comments for Rast_copy_cats * gui: Remove potentially unexpected and unused tuple for SetSelection in rlisetup.wizard
1 parent d7949c5 commit 09f9235

File tree

32 files changed

+138
-175
lines changed

32 files changed

+138
-175
lines changed

.flake8

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
ignore =
33
# whitespace before ':' (Black)
44
E203,
5+
# E501 line too long
6+
E501,
57
# line break before binary operator (Black)
68
W503,
79

.github/workflows/python-code-quality.yml

+8-17
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ jobs:
2929
# renovate: datasource=python-version depName=python
3030
PYTHON_VERSION: "3.13"
3131
MIN_PYTHON_VERSION: "3.9"
32-
# renovate: datasource=pypi depName=black
33-
BLACK_VERSION: "25.1.0"
3432
# renovate: datasource=pypi depName=flake8
3533
FLAKE8_VERSION: "7.1.1"
3634
# renovate: datasource=pypi depName=pylint
@@ -73,6 +71,14 @@ jobs:
7371
continue-on-error: true
7472
- name: Run Ruff (apply fixes for suggestions)
7573
run: ruff check . --preview --fix --unsafe-fixes
74+
- name: Run `ruff format` showing diff without failing
75+
continue-on-error: true
76+
if: ${{ !cancelled() }}
77+
run: ruff format --diff
78+
- name: Run `ruff format` fixing files
79+
# Run `ruff format` even when `ruff check` fixed files: fixes can require formatting
80+
if: ${{ !cancelled() }}
81+
run: ruff format --diff
7682
- name: Create and uploads code suggestions to apply for Ruff
7783
# Will fail fast here if there are changes required
7884
id: diff-ruff
@@ -84,21 +90,6 @@ jobs:
8490
# To keep repo's file structure in formatted changes artifact
8591
extra-upload-changes: pyproject.toml
8692

87-
- name: Install Black only
88-
run: pip install black[jupyter]==${{ env.BLACK_VERSION }}
89-
90-
- name: Run Black
91-
run: black .
92-
93-
- name: Create and uploads code suggestions to apply for Black
94-
# Will fail fast here if there are changes required
95-
id: diff-black
96-
uses: ./.github/actions/create-upload-suggestions
97-
with:
98-
tool-name: black
99-
# To keep repo's file structure in formatted changes artifact
100-
extra-upload-changes: .clang-format
101-
10293
- name: Install non-Python dependencies
10394
run: |
10495
sudo apt-get update -y

.pre-commit-config.yaml

+2-9
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,12 @@ repos:
4242
# Run the linter.
4343
- id: ruff
4444
args: [--fix, --preview]
45+
# Run the formatter.
46+
- id: ruff-format
4547
- repo: https://github.com/igorshubovych/markdownlint-cli
4648
rev: v0.44.0
4749
hooks:
4850
- id: markdownlint-fix
49-
# Using this mirror lets us use mypyc-compiled black, which is about 2x faster
50-
- repo: https://github.com/psf/black-pre-commit-mirror
51-
rev: 25.1.0
52-
hooks:
53-
- id: black-jupyter
54-
exclude: |
55-
(?x)^(
56-
python/libgrass_interface_generator/
57-
)
5851
- repo: https://github.com/pycqa/flake8
5952
rev: 7.1.1
6053
hooks:

general/g.version/tests/g_version_test.py

+21-21
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,36 @@
44
def test_g_version_no_flag(session):
55
"""Test that g.version output contains the word 'GRASS'."""
66
output = gs.read_command("g.version", env=session.env).strip()
7-
assert (
8-
"GRASS" in output
9-
), "Expected 'GRASS' in g.version output, but it was not found."
7+
assert "GRASS" in output, (
8+
"Expected 'GRASS' in g.version output, but it was not found."
9+
)
1010

1111

1212
def test_c_flag(session):
1313
"""Test the output of g.version -c for Copyright and License Statement."""
1414
expected_text = "Copyright and License Statement"
1515
output = gs.read_command("g.version", flags="c", env=session.env).strip()
16-
assert (
17-
expected_text in output
18-
), f"Expected '{expected_text}' in g.version -c output, but got: '{output}'"
16+
assert expected_text in output, (
17+
f"Expected '{expected_text}' in g.version -c output, but got: '{output}'"
18+
)
1919

2020

2121
def test_e_flag(session):
2222
"""Test that g.version -e contains the expected keys."""
2323
expected_keys = ["PROJ:", "GDAL/OGR:", "SQLite:"]
2424
output = gs.read_command("g.version", flags="e", env=session.env).strip()
2525
for key in expected_keys:
26-
assert (
27-
key in output
28-
), f"Expected key '{key}' in g.version -e output, but it was not found."
26+
assert key in output, (
27+
f"Expected key '{key}' in g.version -e output, but it was not found."
28+
)
2929

3030

3131
def test_b_flag(session):
3232
"""Test that g.version -b output contains the word 'GRASS'."""
3333
output = gs.read_command("g.version", flags="b", env=session.env).strip()
34-
assert (
35-
"GRASS" in output
36-
), "Expected 'GRASS' in g.version -b output, but it was not found."
34+
assert "GRASS" in output, (
35+
"Expected 'GRASS' in g.version -b output, but it was not found."
36+
)
3737

3838

3939
def test_g_flag(session):
@@ -48,19 +48,19 @@ def test_g_flag(session):
4848
]
4949
output = gs.parse_command("g.version", flags="g", env=session.env)
5050
for key in expected_keys:
51-
assert (
52-
key in output
53-
), f"Expected key '{key}' in g.version -g output, but it was not found."
51+
assert key in output, (
52+
f"Expected key '{key}' in g.version -g output, but it was not found."
53+
)
5454

5555

5656
def test_r_flag(session):
5757
"""Test that g.version -r contains the expected keys."""
5858
expected_texts = ["libgis revision:", "libgis date:"]
5959
output = gs.read_command("g.version", flags="r", env=session.env).strip()
6060
for text in expected_texts:
61-
assert (
62-
text in output
63-
), f"Expected key '{text}' in g.version -r output, but it was not found."
61+
assert text in output, (
62+
f"Expected key '{text}' in g.version -r output, but it was not found."
63+
)
6464

6565

6666
def test_x_flag(session):
@@ -78,6 +78,6 @@ def curly_brackets_paired(text):
7878
return False
7979
return counter == 0
8080

81-
assert curly_brackets_paired(
82-
output
83-
), "Curly brackets are not properly paired in the g.version -x output."
81+
assert curly_brackets_paired(output), (
82+
"Curly brackets are not properly paired in the g.version -x output."
83+
)

gui/wxpython/core/settings.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -816,9 +816,9 @@ def _internalSettings(self):
816816
)
817817

818818
self.internalSettings["display"]["driver"]["choices"] = ["cairo", "png"]
819-
self.internalSettings["display"]["statusbarMode"][
820-
"choices"
821-
] = None # set during MapFrame init
819+
self.internalSettings["display"]["statusbarMode"]["choices"] = (
820+
None # set during MapFrame init
821+
)
822822
self.internalSettings["display"]["mouseWheelZoom"]["choices"] = (
823823
_("Zoom and recenter"),
824824
_("Zoom to mouse cursor"),

gui/wxpython/dbmgr/base.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -2223,9 +2223,11 @@ def ValidateSelectStatement(self, statement):
22232223

22242224
tablelen = len(self.dbMgrData["mapDBInfo"].layers[self.selLayer]["table"])
22252225

2226-
if statement[index + 1 : index + 6].lower() != "from " or statement[
2227-
index + 6 : index + 6 + tablelen
2228-
] != "%s" % (self.dbMgrData["mapDBInfo"].layers[self.selLayer]["table"]):
2226+
if (
2227+
statement[index + 1 : index + 6].lower() != "from "
2228+
or statement[index + 6 : index + 6 + tablelen]
2229+
!= "%s" % (self.dbMgrData["mapDBInfo"].layers[self.selLayer]["table"])
2230+
):
22292231
return None
22302232

22312233
if len(statement[index + 7 + tablelen :]) > 0:

gui/wxpython/gmodeler/model.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -3307,9 +3307,7 @@ def _writePython(self):
33073307
# %module
33083308
# % description: {description}
33093309
# %end
3310-
""".format(
3311-
description=" ".join(properties["description"].splitlines())
3312-
)
3310+
""".format(description=" ".join(properties["description"].splitlines()))
33133311
)
33143312

33153313
modelItems = self.model.GetItems(ModelAction)

gui/wxpython/gui_core/vselect.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -332,9 +332,7 @@ def OnExportMap(self, event):
332332
GMessage(_("No features selected"))
333333
return
334334
lst = ""
335-
for (
336-
cat
337-
) in (
335+
for cat in (
338336
self.selectedFeatures
339337
): # build text string of categories for v.extract input
340338
lst += str(cat["Category"]) + ","

gui/wxpython/nviz/tools.py

+12-12
Original file line numberDiff line numberDiff line change
@@ -1610,12 +1610,12 @@ def _createVectorPage(self, parent):
16101610
checkThematicWidth = wx.CheckBox(
16111611
parent=panel, id=wx.ID_ANY, label=_("use width for thematic mapping")
16121612
)
1613-
self.win["vector"]["lines"]["thematic"][
1614-
"checkcolor"
1615-
] = checkThematicColor.GetId()
1616-
self.win["vector"]["lines"]["thematic"][
1617-
"checkwidth"
1618-
] = checkThematicWidth.GetId()
1613+
self.win["vector"]["lines"]["thematic"]["checkcolor"] = (
1614+
checkThematicColor.GetId()
1615+
)
1616+
self.win["vector"]["lines"]["thematic"]["checkwidth"] = (
1617+
checkThematicWidth.GetId()
1618+
)
16191619
checkThematicColor.Bind(wx.EVT_CHECKBOX, self.OnCheckThematic)
16201620
checkThematicWidth.Bind(wx.EVT_CHECKBOX, self.OnCheckThematic)
16211621
checkThematicColor.SetValue(False)
@@ -1825,12 +1825,12 @@ def _createVectorPage(self, parent):
18251825
checkThematicSize = wx.CheckBox(
18261826
parent=panel, id=wx.ID_ANY, label=_("use size for thematic mapping")
18271827
)
1828-
self.win["vector"]["points"]["thematic"][
1829-
"checkcolor"
1830-
] = checkThematicColor.GetId()
1831-
self.win["vector"]["points"]["thematic"][
1832-
"checksize"
1833-
] = checkThematicSize.GetId()
1828+
self.win["vector"]["points"]["thematic"]["checkcolor"] = (
1829+
checkThematicColor.GetId()
1830+
)
1831+
self.win["vector"]["points"]["thematic"]["checksize"] = (
1832+
checkThematicSize.GetId()
1833+
)
18341834
checkThematicColor.Bind(wx.EVT_CHECKBOX, self.OnCheckThematic)
18351835
checkThematicSize.Bind(wx.EVT_CHECKBOX, self.OnCheckThematic)
18361836
checkThematicColor.SetValue(False)

gui/wxpython/nviz/workspace.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -385,8 +385,9 @@ def SetDecorDefaultProp(self, type):
385385
# arrow
386386
if type == "arrow":
387387
data["arrow"] = copy.deepcopy(UserSettings.Get(group="nviz", key="arrow"))
388-
data["arrow"]["color"] = "%d:%d:%d" % (
389-
UserSettings.Get(group="nviz", key="arrow", subkey="color")[:3]
388+
data["arrow"]["color"] = (
389+
"%d:%d:%d"
390+
% (UserSettings.Get(group="nviz", key="arrow", subkey="color")[:3])
390391
)
391392
data["arrow"].update(
392393
copy.deepcopy(
@@ -402,8 +403,9 @@ def SetDecorDefaultProp(self, type):
402403
data["scalebar"] = copy.deepcopy(
403404
UserSettings.Get(group="nviz", key="scalebar")
404405
)
405-
data["scalebar"]["color"] = "%d:%d:%d" % (
406-
UserSettings.Get(group="nviz", key="scalebar", subkey="color")[:3]
406+
data["scalebar"]["color"] = (
407+
"%d:%d:%d"
408+
% (UserSettings.Get(group="nviz", key="scalebar", subkey="color")[:3])
407409
)
408410
data["scalebar"].update(
409411
copy.deepcopy(

gui/wxpython/rlisetup/wizard.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1405,7 +1405,7 @@ def __init__(self, wizard, parent):
14051405
def OnEnterPage(self, event: WizardEvent | None = None) -> None:
14061406
"""Function during entering"""
14071407
# This is an hack to force the user to choose Rectangle or Circle
1408-
self.typeBox.SetSelection(2),
1408+
self.typeBox.SetSelection(2)
14091409
self.typeBox.ShowItem(2, False)
14101410
self.panelSizer.Layout()
14111411

@@ -1608,7 +1608,7 @@ def __init__(self, wizard, parent):
16081608
choices=[_("Rectangle"), _("Circle"), ("")],
16091609
)
16101610
# This is an hack to force the user to choose Rectangle or Circle
1611-
self.typeBox.SetSelection(2),
1611+
self.typeBox.SetSelection(2)
16121612
self.typeBox.ShowItem(2, False)
16131613
self.sizer.Add(self.typeBox, flag=wx.ALIGN_LEFT, pos=(0, 0), span=(1, 2))
16141614

gui/wxpython/timeline/frame.py

+2-6
Original file line numberDiff line numberDiff line change
@@ -271,9 +271,7 @@ def _draw3dFigure(self):
271271
self.axes3d.clear()
272272
self.axes3d.grid(False)
273273
# self.axes3d.grid(True)
274-
convert = (
275-
mdates.date2num if self.temporalType == "absolute" else lambda x: x
276-
) # noqa: E731
274+
convert = mdates.date2num if self.temporalType == "absolute" else lambda x: x # noqa: E731
277275

278276
colors = cycle(COLORS)
279277
plots = []
@@ -319,9 +317,7 @@ def _draw2dFigure(self):
319317
"""Draws 2D plot (temporal extents)"""
320318
self.axes2d.clear()
321319
self.axes2d.grid(True)
322-
convert = (
323-
mdates.date2num if self.temporalType == "absolute" else lambda x: x
324-
) # noqa: E731
320+
convert = mdates.date2num if self.temporalType == "absolute" else lambda x: x # noqa: E731
325321

326322
colors = cycle(COLORS)
327323

imagery/i.atcorr/create_iwave.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,9 @@ def interpolate_band(values, step=2.5):
8888

8989
wavelengths = values_clean[:, 0] # 1st column of input array
9090
responses = values_clean[:, 1] # 2nd column
91-
assert len(wavelengths) == len(
92-
responses
93-
), "Number of wavelength slots and spectral responses are not equal!"
91+
assert len(wavelengths) == len(responses), (
92+
"Number of wavelength slots and spectral responses are not equal!"
93+
)
9494

9595
# spectral responses are written out with .4f in pretty_print()
9696
# anything smaller than 0.0001 will become 0.0000 -> discard with ...

lib/init/grass.py

+4-11
Original file line numberDiff line numberDiff line change
@@ -603,13 +603,10 @@ def read_gui(gisrc, default_gui):
603603

604604
def create_initial_gisrc(filename):
605605
# for convenience, define GISDBASE as pwd:
606-
s = (
607-
r"""GISDBASE: %s
606+
s = r"""GISDBASE: %s
608607
LOCATION_NAME: <UNKNOWN>
609608
MAPSET: <UNKNOWN>
610-
"""
611-
% Path.cwd()
612-
)
609+
""" % Path.cwd()
613610
writefile(filename, s)
614611

615612

@@ -1662,9 +1659,7 @@ def sh_like_startup(location, location_name, grass_env_file, sh):
16621659
fc -R
16631660
_grass_old_mapset="$MAPSET_PATH"
16641661
fi
1665-
""".format(
1666-
sh_history=sh_history
1667-
)
1662+
""".format(sh_history=sh_history)
16681663
elif sh == "bash":
16691664
# Append existing history to file ("flush").
16701665
# Clear the (in-memory) history.
@@ -1678,9 +1673,7 @@ def sh_like_startup(location, location_name, grass_env_file, sh):
16781673
history -r
16791674
_grass_old_mapset="$MAPSET_PATH"
16801675
fi
1681-
""".format(
1682-
sh_history=sh_history
1683-
)
1676+
""".format(sh_history=sh_history)
16841677
# Ubuntu sudo creates a file .sudo_as_admin_successful and bash checks
16851678
# for this file in the home directory from /etc/bash.bashrc and prints a
16861679
# message if it's not detected. This can be suppressed with either

python/grass/experimental/tests/conftest.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,7 @@ def unique_id():
4040

4141

4242
@pytest.fixture
43-
def xy_mapset_session(
44-
xy_session_for_module, unique_id
45-
): # pylint: disable=redefined-outer-name
43+
def xy_mapset_session(xy_session_for_module, unique_id): # pylint: disable=redefined-outer-name
4644
"""Active session in a mapset of an XY location
4745
4846
Mapset scope is function, while the location scope is module.

python/grass/experimental/tests/grass_script_mapset_session_test.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,9 @@ def test_create_multiple(xy_session):
155155
assert sorted(collected) == sorted(create_names)
156156
existing_mapsets = get_mapset_names(env=xy_session.env)
157157
assert sorted(existing_mapsets) == sorted(create_names + original_mapsets)
158-
assert (
159-
len(set(top_level_collected)) == 1
160-
), f"Top level mapset changed: {top_level_collected}"
158+
assert len(set(top_level_collected)) == 1, (
159+
f"Top level mapset changed: {top_level_collected}"
160+
)
161161

162162

163163
def test_nested_top_env(xy_session):

python/grass/jupyter/interactivemap.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -504,9 +504,7 @@ class InteractiveRegionController:
504504
changed_region (dict): The dictionary to store the changed region.
505505
"""
506506

507-
def __init__(
508-
self, map_object, ipyleaflet, ipywidgets, **kwargs
509-
): # pylint: disable=unused-argument
507+
def __init__(self, map_object, ipyleaflet, ipywidgets, **kwargs): # pylint: disable=unused-argument
510508
"""Initializes the InteractiveRegionController.
511509
512510
:param map_object: The map object.

0 commit comments

Comments
 (0)