diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 5d1a3ac1539..e0c7dfc4e85 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -36,7 +36,7 @@ jobs: # renovate: datasource=pypi depName=bandit BANDIT_VERSION: "1.7.10" # renovate: datasource=pypi depName=ruff - RUFF_VERSION: "0.7.4" + RUFF_VERSION: "0.8.0" runs-on: ${{ matrix.os }} permissions: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 449a9a61f04..a486f2ee0da 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,13 +37,13 @@ repos: ) - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.7.4 + rev: v0.8.0 hooks: # Run the linter. - id: ruff args: [--fix, --preview] - repo: https://github.com/igorshubovych/markdownlint-cli - rev: v0.42.0 + rev: v0.43.0 hooks: - id: markdownlint-fix # Using this mirror lets us use mypyc-compiled black, which is about 2x faster diff --git a/display/d.text/test.py b/display/d.text/test.py index 1089096d2ef..398cfecf428 100755 --- a/display/d.text/test.py +++ b/display/d.text/test.py @@ -73,8 +73,8 @@ def text(in_text): print( ".L 0\n" + re.sub( - '(".*?")', + r'(".*?")', "\n.C red\n,\\g<0>\n.C gray\n", - re.sub("\n", "\n.L 1\n.L 0\n", re.sub("(?m)^#.*\n?", "", src)), + re.sub(r"\n", "\n.L 1\n.L 0\n", re.sub(r"(?m)^#.*\n?", "", src)), ) ) diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index f321d2a206f..2a4673f511a 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -2672,7 +2672,7 @@ def _getParamName(self, parameter_name, item): @staticmethod def _getModuleNickname(item): return "{module_name}{module_id}".format( - module_name=re.sub("[^a-zA-Z]+", "", item.GetLabel()), + module_name=re.sub(r"[^a-zA-Z]+", "", item.GetLabel()), module_id=item.GetId(), ) diff --git a/gui/wxpython/photo2image/ip2i_manager.py b/gui/wxpython/photo2image/ip2i_manager.py index 37985dad5e4..e3832c681b4 100644 --- a/gui/wxpython/photo2image/ip2i_manager.py +++ b/gui/wxpython/photo2image/ip2i_manager.py @@ -433,7 +433,7 @@ def __init__( fc_count = 0 for line in fc: fc_count += 1 - if re.search("NUM", line): + if re.search(r"NUM", line): storeLine = fc_count numberOfFiducial = int(line.split()[-1]) dataFiducialX = [] diff --git a/lib/init/grass.py b/lib/init/grass.py index 505d48dadae..6e9dcbf7a58 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -1015,7 +1015,7 @@ def load_env(grass_env_file): # Regular expression for lines starting with "export var=val" (^export # lines below). Environment variables should start with a-zA-Z or _. # \1 and \2 are a variable name and its value, respectively. - export_re = re.compile("^export[ \t]+([a-zA-Z_]+[a-zA-Z0-9_]*)=(.*?)[ \t]*$") + export_re = re.compile(r"^export[ \t]+([a-zA-Z_]+[a-zA-Z0-9_]*)=(.*?)[ \t]*$") for line in readfile(grass_env_file).splitlines(): # match ^export lines diff --git a/pyproject.toml b/pyproject.toml index c9f621a679f..c550cf71faa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ extend-exclude = ''' ''' [tool.ruff] -required-version = ">=0.6.0" +required-version = ">=0.8.0" builtins = ["_"] @@ -183,7 +183,6 @@ ignore = [ "PLW1641", # eq-without-hash "PLW2901", # redefined-loop-name "PLW3201", # bad-dunder-method-name - "PT004", # pytest-missing-fixture-name-underscore # deprecated, so doesn't appear with --preview "PTH100", # os-path-abspath "PTH101", # os-chmod "PTH102", # os-mkdir diff --git a/python/grass/gunittest/case.py b/python/grass/gunittest/case.py index 4e0ee3ab24d..49a378d6133 100644 --- a/python/grass/gunittest/case.py +++ b/python/grass/gunittest/case.py @@ -1320,10 +1320,10 @@ def runModule(cls, module, expecting_stdout=False, **kwargs): # TODO: standardized error code would be handy here import re - if re.search("Raster map.*not found", errors, flags=re.DOTALL): + if re.search(r"Raster map.*not found", errors, flags=re.DOTALL): errors += "\nSee available raster maps:\n" errors += call_module("g.list", type="raster") - if re.search("Vector map.*not found", errors, flags=re.DOTALL): + if re.search(r"Vector map.*not found", errors, flags=re.DOTALL): errors += "\nSee available vector maps:\n" errors += call_module("g.list", type="vector") # TODO: message format, parameters diff --git a/python/grass/script/utils.py b/python/grass/script/utils.py index 0a32a9e7b4e..e4bfc9cc484 100644 --- a/python/grass/script/utils.py +++ b/python/grass/script/utils.py @@ -345,7 +345,7 @@ def convert(text): def alphanum_key(actual_key): sort_key = key(actual_key) if key else actual_key - return [convert(c) for c in re.split("([0-9]+)", sort_key)] + return [convert(c) for c in re.split(r"([0-9]+)", sort_key)] items.sort(key=alphanum_key) @@ -501,15 +501,15 @@ def legalize_vector_name(name, fallback_prefix="x"): # The implementation is based on Vect_legal_filename(). if not name: raise ValueError("name cannot be empty") - if fallback_prefix and re.match("[^A-Za-z]", fallback_prefix[0]): + if fallback_prefix and re.match(r"[^A-Za-z]", fallback_prefix[0]): raise ValueError("fallback_prefix must start with an ASCII letter") - if fallback_prefix and re.match("[^A-Za-z]", name[0], flags=re.ASCII): + if fallback_prefix and re.match(r"[^A-Za-z]", name[0], flags=re.ASCII): # We prefix here rather than just replace, because in cases of unique # identifiers, e.g., columns or node names, replacing the first # character by the same replacement character increases chances of # conflict (e.g. column names 10, 20, 30). name = "{fallback_prefix}{name}".format(**locals()) - name = re.sub("[^A-Za-z0-9_]", "_", name, flags=re.ASCII) + name = re.sub(r"[^A-Za-z0-9_]", "_", name, flags=re.ASCII) keywords = ["and", "or", "not"] if name in keywords: name = "{name}_".format(**locals()) diff --git a/raster/r.topidx/arc_to_gridatb.py b/raster/r.topidx/arc_to_gridatb.py index 3eba7114430..d47d399618d 100755 --- a/raster/r.topidx/arc_to_gridatb.py +++ b/raster/r.topidx/arc_to_gridatb.py @@ -13,7 +13,7 @@ def match(pattern, string): return False -if len(sys.argv) != 3 or re.match("^-*help", sys.argv[1]): +if len(sys.argv) != 3 or re.match(r"^-*help", sys.argv[1]): print("Usage: arc.to.gridatb.py arc_file gridatb_file") sys.exit() diff --git a/raster/r.topidx/gridatb_to_arc.py b/raster/r.topidx/gridatb_to_arc.py index 41111f3d6ba..d80f6797a65 100755 --- a/raster/r.topidx/gridatb_to_arc.py +++ b/raster/r.topidx/gridatb_to_arc.py @@ -7,7 +7,7 @@ len(sys.argv) == 1 or len(sys.argv) == 4 or len(sys.argv) > 5 - or re.match("^-*help", sys.argv[1]) + or re.match(r"^-*help", sys.argv[1]) ): print("Usage: gridatb.to.arc.py gridatb_file arc_file [xllcorner yllcorner]") sys.exit() @@ -33,7 +33,7 @@ title = inf.readline() inline = inf.readline() -m = re.match("^[ \t]*([0-9.]+)[ \t]+([0-9.]+)[ \t]+([0-9.]+)[ \t]*$", inline) +m = re.match(r"^[ \t]*([0-9.]+)[ \t]+([0-9.]+)[ \t]+([0-9.]+)[ \t]*$", inline) if not m: print(f"{infname}: Invalid input file format") inf.close() diff --git a/utils/g.html2man/g.html2man.py b/utils/g.html2man/g.html2man.py index 58ffb2f7bca..46a749ae5d3 100755 --- a/utils/g.html2man/g.html2man.py +++ b/utils/g.html2man/g.html2man.py @@ -47,7 +47,7 @@ def main(): sf.close() # strip excess whitespace - blank_re = re.compile("[ \t\n]*\n([ \t]*\n)*") + blank_re = re.compile(r"[ \t\n]*\n([ \t]*\n)*") s = blank_re.sub("\n", s) s = s.lstrip() diff --git a/utils/g.html2man/ggroff.py b/utils/g.html2man/ggroff.py index a0458dd0334..c96fec4eab7 100644 --- a/utils/g.html2man/ggroff.py +++ b/utils/g.html2man/ggroff.py @@ -69,7 +69,7 @@ def __init__(self, filename, stream=sys.stdout): "index": [], } self.stack = [] - self.strip_re = re.compile("^[ \t]+") + self.strip_re = re.compile(r"^[ \t]+") self.filename = filename self.at_bol = True diff --git a/utils/gitlog2changelog.py b/utils/gitlog2changelog.py index 5ad459aa2b1..f878041ca83 100755 --- a/utils/gitlog2changelog.py +++ b/utils/gitlog2changelog.py @@ -61,7 +61,7 @@ # Match the author line and extract the part we want # (Don't use startswith to allow Author override inside commit message.) elif "Author:" in line: - authorList = re.split(": ", line, 1) + authorList = re.split(r": ", line, 1) try: author = authorList[1] author = author[0 : len(author) - 1] @@ -71,7 +71,7 @@ # Match the date line elif line.startswith("Date:"): - dateList = re.split(": ", line, 1) + dateList = re.split(r": ", line, 1) try: date = dateList[1] date = date[0 : len(date) - 1] @@ -100,7 +100,7 @@ else: message = message + " " + line.strip() # If this line is hit all of the files have been stored for this commit - elif re.search("files? changed", line): + elif re.search(r"files? changed", line): filesFound = True continue # Collect the files for this commit. FIXME: Still need to add +/- to files diff --git a/utils/mkhtml.py b/utils/mkhtml.py index 363c513a7ed..c01ab0534cb 100644 --- a/utils/mkhtml.py +++ b/utils/mkhtml.py @@ -234,7 +234,7 @@ def handle_data(self, data): def escape_href(label): # remove html tags - label = re.sub("<[^<]+?>", "", label) + label = re.sub(r"<[^<]+?>", "", label) # fix   label = label.replace(" ", "") # fix " @@ -351,16 +351,16 @@ def update_toc(data): # process header src_data = read_file(src_file) -name = re.search("()", src_data, re.IGNORECASE) +name = re.search(r"()", src_data, re.IGNORECASE) pgm_desc = "GRASS GIS Reference Manual" if name: pgm = name.group(2).strip().split("-", 1)[0].strip() name_desc = re.search( - "()", src_data, re.IGNORECASE + r"()", src_data, re.IGNORECASE ) if name_desc: pgm_desc = name_desc.group(2).strip() -desc = re.search("()", src_data, re.IGNORECASE) +desc = re.search(r"()", src_data, re.IGNORECASE) if desc: pgm = desc.group(2).strip() header_tmpl = string.Template(header_base + header_nopgm) @@ -369,7 +369,7 @@ def update_toc(data): else: header_tmpl = string.Template(header_base + header_pgm_desc) -if not re.search("", src_data, re.IGNORECASE): +if not re.search(r"", src_data, re.IGNORECASE): tmp_data = read_file(tmp_file) """ Adjusting keywords html pages paths if add-on html man page @@ -395,7 +395,7 @@ def update_toc(data): orig_keywords_paths.group(1), ",".join(new_keywords_paths), ) - if not re.search("", tmp_data, re.IGNORECASE): + if not re.search(r"", tmp_data, re.IGNORECASE): sys.stdout.write(header_tmpl.substitute(PGM=pgm, PGM_DESC=pgm_desc)) if tmp_data: @@ -403,7 +403,7 @@ def update_toc(data): for line in tmp_data.splitlines(True): # The cleanup happens on Makefile level too. if not re.search( - "|| ", line, re.IGNORECASE + r"|| ", line, re.IGNORECASE ): if header_logo_img_el in line: sys.stdout.write(line) @@ -420,7 +420,7 @@ def update_toc(data): # if is found, suppose a complete html is provided. # otherwise, generate module class reference: -if re.search("", src_data, re.IGNORECASE): +if re.search(r"", src_data, re.IGNORECASE): sys.exit() index_names = { @@ -453,7 +453,7 @@ def to_title(name): index_titles[key] = to_title(name) # process footer -index = re.search("()", src_data, re.IGNORECASE) +index = re.search(r"()", src_data, re.IGNORECASE) if index: index_name = index.group(2).strip() if "|" in index_name: diff --git a/utils/mkrest.py b/utils/mkrest.py index 8d156e0bcc7..6270b78c6b2 100755 --- a/utils/mkrest.py +++ b/utils/mkrest.py @@ -66,7 +66,7 @@ def read_file(name): src_data = read_file(src_file) -title = re.search("()", src_data, re.IGNORECASE) +title = re.search(r"()", src_data, re.IGNORECASE) if title: title_name = title.group(2).strip() @@ -108,7 +108,7 @@ def read_file(name): "v": "vector", } -index = re.search("()", src_data, re.IGNORECASE) +index = re.search(r"()", src_data, re.IGNORECASE) if index: index_name = index.group(2).strip()