diff --git a/pyproject.toml b/pyproject.toml index fad3910f680..1df103b1bad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -324,7 +324,6 @@ ignore = [ "python/grass/gunittest/multireport.py" = ["PYI024"] "python/grass/gunittest/testsu*/d*/s*/s*/subsub*/t*/test_segfaut.py" = ["B018"] "python/grass/gunittest/testsuite/test_assertions_rast3d.py" = ["FLY002"] -"python/grass/imaging/images2*.py" = ["SIM115"] "python/grass/imaging/images2ims.py" = ["PTH208"] "python/grass/jupyter/testsuite/interactivemap_test.py" = ["PGH004"] "python/grass/jupyter/testsuite/map_test.py" = ["PGH004"] @@ -343,9 +342,7 @@ ignore = [ "python/grass/pygrass/vector/testsuite/test_table.py" = ["PLW0108"] "python/grass/script/array.py" = ["A005"] "python/grass/script/core.py" = ["PTH208"] -"python/grass/script/db.py" = ["SIM115"] -"python/grass/script/raster.py" = ["SIM115"] -"python/grass/script/utils.py" = ["FURB189", "SIM115"] +"python/grass/script/utils.py" = ["FURB189"] "python/grass/temporal/aggregation.py" = ["SIM115"] "python/grass/temporal/register.py" = ["SIM115"] "python/grass/temporal/stds_export.py" = ["SIM115"] diff --git a/python/grass/gunittest/multirunner.py b/python/grass/gunittest/multirunner.py index ebb9e80049a..dd5411d66fc 100644 --- a/python/grass/gunittest/multirunner.py +++ b/python/grass/gunittest/multirunner.py @@ -111,16 +111,16 @@ def main(): # we assume that the start script is available and in the PATH # the shell=True is here because of MS Windows? (code taken from wiki) startcmd = grass_executable + " --config path" - p = subprocess.Popen( + with subprocess.Popen( startcmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE - ) - out, err = p.communicate() - if p.returncode != 0: - print( - "ERROR: Cannot find GRASS GIS start script (%s):\n%s" % (startcmd, err), - file=sys.stderr, - ) - return 1 + ) as p: + out, err = p.communicate() + if p.returncode != 0: + print( + "ERROR: Cannot find GRASS GIS start script (%s):\n%s" % (startcmd, err), + file=sys.stderr, + ) + return 1 gisbase = decode(out.strip()) # set GISBASE environment variable @@ -151,7 +151,7 @@ def main(): # including also type to make it unique and preserve it for sure report = "report_for_" + location + "_" + location_type absreport = os.path.abspath(report) - p = subprocess.Popen( + with subprocess.Popen( [ sys.executable, "-tt", @@ -167,23 +167,24 @@ def main(): absreport, ], cwd=grasssrc, - ) - returncode = p.wait() - reports.append(report) + ) as p2: + returncode = p2.wait() + reports.append(report) if main_report: # TODO: solve the path to source code (work now only for grass source code) arguments = [ sys.executable, - grasssrc + "/python/grass/gunittest/" + "multireport.py", - "--timestapms", + grasssrc + "/python/grass/gunittest/multireport.py", + "--timestamps", + *reports, ] - arguments.extend(reports) - p = subprocess.Popen(arguments) - returncode = p.wait() - if returncode != 0: - print("ERROR: Creation of main report failed.", file=sys.stderr) - return 1 + + with subprocess.Popen(arguments) as p3: + returncode = p3.wait() + if returncode != 0: + print("ERROR: Creation of main report failed.", file=sys.stderr) + return 1 return 0 diff --git a/python/grass/gunittest/reporters.py b/python/grass/gunittest/reporters.py index 5b468ac1d91..767678416d1 100644 --- a/python/grass/gunittest/reporters.py +++ b/python/grass/gunittest/reporters.py @@ -160,19 +160,19 @@ def get_svn_revision(): """ # TODO: here should be starting directory # but now we are using current as starting - p = subprocess.Popen( + with subprocess.Popen( ["svnversion", "."], stdout=subprocess.PIPE, stderr=subprocess.PIPE - ) - stdout, stderr = p.communicate() - rc = p.poll() - if not rc: + ) as p: + stdout, stderr = p.communicate() + rc = p.poll() + if rc: + return None stdout = stdout.strip() stdout = stdout.removesuffix("M") if ":" in stdout: # the first one is the one of source code stdout = stdout.split(":")[0] return stdout - return None def get_svn_info(): diff --git a/python/grass/imaging/images2avi.py b/python/grass/imaging/images2avi.py index 2dda32e142d..27bdd2c15ca 100644 --- a/python/grass/imaging/images2avi.py +++ b/python/grass/imaging/images2avi.py @@ -180,22 +180,22 @@ def readAvi(filename, asNumpy=True): # Run ffmpeg command = "ffmpeg -i input.avi im%d.jpg" - S = subprocess.Popen( - command, shell=True, cwd=tempDir, stdout=subprocess.PIPE, stderr=subprocess.PIPE - ) - - # Show what mencodec has to say - outPut = S.stdout.read() - - if S.wait(): - # An error occurred, show - print(outPut) - print(S.stderr.read()) - # Clean up - _cleanDir(tempDir) - msg = "Could not read avi." - raise RuntimeError(msg) - + with subprocess.Popen( + command, + cwd=tempDir, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) as S: + # Show what mencodec has to say + outPut = S.stdout.read() + if S.wait(): + # An error occurred, show + print(outPut) + print(S.stderr.read()) + # Clean up + _cleanDir(tempDir) + msg = "Could not read avi." + raise RuntimeError(msg) # Read images images = images2ims.readIms(os.path.join(tempDir, "im*.jpg"), asNumpy) # Clean up diff --git a/python/grass/imaging/images2gif.py b/python/grass/imaging/images2gif.py index f44bad13122..63435297927 100644 --- a/python/grass/imaging/images2gif.py +++ b/python/grass/imaging/images2gif.py @@ -612,11 +612,8 @@ def writeGifVisvis( images = gifWriter.convertImagesToPIL(images, dither, nq) # Write - fp = open(filename, "wb") - try: + with open(filename, "wb") as fp: gifWriter.writeGifToFile(fp, images, duration, loops, xy, dispose) - finally: - fp.close() def readGif(filename, asNumpy=True): diff --git a/python/grass/imaging/images2swf.py b/python/grass/imaging/images2swf.py index 1ea1a4e8413..9fcd2da6447 100644 --- a/python/grass/imaging/images2swf.py +++ b/python/grass/imaging/images2swf.py @@ -69,6 +69,7 @@ import os import zlib +from pathlib import Path try: import numpy as np @@ -838,11 +839,8 @@ def writeSwf(filename, images, duration=0.1, repeat=True): taglist.append(DoActionTag("stop")) # Build file - fp = open(filename, "wb") - try: + with open(filename, "wb") as fp: buildFile(fp, taglist, nframes=nframes, framesize=wh, fps=fps) - finally: - fp.close() def _readPixels(bb, i, tagType, L1): @@ -923,67 +921,60 @@ def readSwf(filename, asNumpy=True): images = [] # Open file and read all - fp = open(filename, "rb") - bb = fp.read() - - try: - # Check opening tag - tmp = bb[0:3].decode("ascii", "ignore") - if tmp.upper() == "FWS": - pass # ok - elif tmp.upper() == "CWS": - # Decompress movie - bb = bb[:8] + zlib.decompress(bb[8:]) + bb = Path(filename).read_bytes() + # Check opening tag + tmp = bb[0:3].decode("ascii", "ignore") + if tmp.upper() == "FWS": + pass # ok + elif tmp.upper() == "CWS": + # Decompress movie + bb = bb[:8] + zlib.decompress(bb[8:]) + else: + raise OSError("Not a valid SWF file: " + str(filename)) + + # Set filepointer at first tag (skipping framesize RECT and two uin16's + i = 8 + nbits = bitsToInt(bb[i : i + 1], 5) # skip FrameSize + nbits = 5 + nbits * 4 + Lrect = nbits / 8.0 + if Lrect % 1: + Lrect += 1 + Lrect = int(Lrect) + i += Lrect + 4 + + # Iterate over the tags + counter = 0 + while True: + counter += 1 + + # Get tag header + head = bb[i : i + 6] + if not head: + break # Done (we missed end tag) + + # Determine type and length + T, L1, L2 = getTypeAndLen(head) + if not L2: + print("Invalid tag length, could not proceed") + break + # print(T, L2) + + # Read image if we can + if T in {20, 36}: + im = _readPixels(bb, i + 6, T, L1) + if im is not None: + images.append(im) + elif T in {6, 21, 35, 90}: + print("Ignoring JPEG image: cannot read JPEG.") else: - raise OSError("Not a valid SWF file: " + str(filename)) - - # Set filepointer at first tag (skipping framesize RECT and two uin16's - i = 8 - nbits = bitsToInt(bb[i : i + 1], 5) # skip FrameSize - nbits = 5 + nbits * 4 - Lrect = nbits / 8.0 - if Lrect % 1: - Lrect += 1 - Lrect = int(Lrect) - i += Lrect + 4 - - # Iterate over the tags - counter = 0 - while True: - counter += 1 - - # Get tag header - head = bb[i : i + 6] - if not head: - break # Done (we missed end tag) - - # Determine type and length - T, L1, L2 = getTypeAndLen(head) - if not L2: - print("Invalid tag length, could not proceed") - break - # print(T, L2) - - # Read image if we can - if T in [20, 36]: - im = _readPixels(bb, i + 6, T, L1) - if im is not None: - images.append(im) - elif T in [6, 21, 35, 90]: - print("Ignoring JPEG image: cannot read JPEG.") - else: - pass # Not an image tag - - # Detect end tag - if T == 0: - break - - # Next tag! - i += L2 + pass # Not an image tag - finally: - fp.close() + # Detect end tag + if T == 0: + break + # Next tag! + i += L2 # Convert to normal PIL images if needed if not asNumpy: images2 = images diff --git a/python/grass/script/core.py b/python/grass/script/core.py index 2a07219b461..0c9d3726697 100644 --- a/python/grass/script/core.py +++ b/python/grass/script/core.py @@ -933,15 +933,14 @@ def parser() -> tuple[dict[str, str], dict[str, bool]]: argv[0] = os.path.join(sys.path[0], name) prog = "g.parser.exe" if sys.platform == "win32" else "g.parser" - p = subprocess.Popen([prog, "-n"] + argv, stdout=subprocess.PIPE) - s = p.communicate()[0] - lines = s.split(b"\0") - - if not lines or lines[0] != b"@ARGS_PARSED@": - stdout = os.fdopen(sys.stdout.fileno(), "wb") - stdout.write(s) - sys.exit(p.returncode) - return _parse_opts(lines[1:]) + with subprocess.Popen([prog, "-n"] + argv, stdout=subprocess.PIPE) as p: + s = p.communicate()[0] + lines = s.split(b"\0") + if not lines or lines[0] != b"@ARGS_PARSED@": + stdout = os.fdopen(sys.stdout.fileno(), "wb") + stdout.write(s) + sys.exit(p.returncode) + return _parse_opts(lines[1:]) # interface to g.tempfile diff --git a/python/grass/script/db.py b/python/grass/script/db.py index 0ae4fe6b53f..46b4753bf42 100644 --- a/python/grass/script/db.py +++ b/python/grass/script/db.py @@ -94,23 +94,20 @@ def db_table_exist(table, env=None, **args): :return: True for success, False otherwise """ - nuldev = open(os.devnull, "w+") ok = True - try: - run_command( - "db.describe", - flags="c", - table=table, - stdout=nuldev, - stderr=nuldev, - env=env, - **args, - ) - except CalledModuleError: - ok = False - finally: - nuldev.close() - + with open(os.devnull, "w+") as nuldev: + try: + run_command( + "db.describe", + flags="c", + table=table, + stdout=nuldev, + stderr=nuldev, + env=env, + **args, + ) + except CalledModuleError: + ok = False return ok @@ -190,9 +187,8 @@ def db_select(sql=None, filename=None, table=None, env=None, **args): except CalledModuleError: fatal(_("Fetching data failed"), env=env) - ofile = open(fname) - result = [tuple(x.rstrip(os.linesep).split(args["sep"])) for x in ofile] - ofile.close() + with open(fname) as ofile: + result = [tuple(x.rstrip(os.linesep).split(args["sep"])) for x in ofile] try_remove(fname) return tuple(result) @@ -217,16 +213,16 @@ def db_table_in_vector(table, mapset=".", env=None): """ from .vector import vector_db - nuldev = open(os.devnull, "w") used = [] vects = list_strings("vector", mapset=mapset, env=env) - for vect in vects: - for f in vector_db(vect, stderr=nuldev, env=env).values(): - if not f: - continue - if f["table"] == table: - used.append(vect) - break + with open(os.devnull, "w") as nuldev: + for vect in vects: + for f in vector_db(vect, stderr=nuldev, env=env).values(): + if not f: + continue + if f["table"] == table: + used.append(vect) + break if len(used) > 0: return used return None diff --git a/python/grass/script/raster.py b/python/grass/script/raster.py index c097f632b48..73c555a67f0 100644 --- a/python/grass/script/raster.py +++ b/python/grass/script/raster.py @@ -21,6 +21,8 @@ import os import string import time +from pathlib import Path + from .core import ( gisenv, @@ -49,27 +51,25 @@ def raster_history(map, overwrite=False, env=None): """ current_mapset = gisenv(env)["MAPSET"] - if find_file(name=map, env=env)["mapset"] == current_mapset: - if overwrite is True: - historyfile = tempfile(env=env) - f = open(historyfile, "w") - f.write(os.environ["CMDLINE"]) - f.close() - run_command("r.support", map=map, loadhistory=historyfile, env=env) - try_remove(historyfile) - else: - run_command("r.support", map=map, history=os.environ["CMDLINE"], env=env) - return True - - warning( - _( - "Unable to write history for <%(map)s>. " - "Raster map <%(map)s> not found in current mapset." + if find_file(name=map, env=env)["mapset"] != current_mapset: + warning( + _( + "Unable to write history for <%(map)s>. " + "Raster map <%(map)s> not found in current mapset." + ) + % {"map": map}, + env=env, ) - % {"map": map}, - env=env, - ) - return False + return False + + if overwrite is True: + historyfile = tempfile(env=env) + Path(historyfile).write_text(os.environ["CMDLINE"]) + run_command("r.support", map=map, loadhistory=historyfile, env=env) + try_remove(historyfile) + else: + run_command("r.support", map=map, history=os.environ["CMDLINE"], env=env) + return True def raster_info(map, env=None): diff --git a/python/grass/script/utils.py b/python/grass/script/utils.py index 64677731835..3684091ba73 100644 --- a/python/grass/script/utils.py +++ b/python/grass/script/utils.py @@ -105,9 +105,8 @@ def diff_files( import difflib differ = difflib.Differ() - fh_a = open(filename_a) - fh_b = open(filename_b) - return list(differ.compare(fh_a.readlines(), fh_b.readlines())) + with open(filename_a) as fh_a, open(filename_b) as fh_b: + return list(differ.compare(fh_a.readlines(), fh_b.readlines())) def try_remove(path: StrOrBytesPath) -> None: diff --git a/python/grass/temporal/stds_export.py b/python/grass/temporal/stds_export.py index c5f546aa3a4..c5a68bb9d88 100644 --- a/python/grass/temporal/stds_export.py +++ b/python/grass/temporal/stds_export.py @@ -373,36 +373,31 @@ def export_stds( # Open the tar archive to add the files tar = tarfile.open(tmp_tar_file_name, flag) - list_file = open(list_file_name, "w") - fs = "|" - - if rows: - if type_ == "strds": - if format_ in {"GTiff", "AAIGrid"}: - _export_raster_maps_as_gdal( - rows, tar, list_file, new_cwd, fs, format_, datatype, **kwargs - ) - else: - _export_raster_maps(rows, tar, list_file, new_cwd, fs) - elif type_ == "stvds": - if format_ == "GML": - _export_vector_maps_as_gml(rows, tar, list_file, new_cwd, fs) - elif format_ == "GPKG": - _export_vector_maps_as_gpkg(rows, tar, list_file, new_cwd, fs) - else: - _export_vector_maps(rows, tar, list_file, new_cwd, fs) - elif type_ == "str3ds": - _export_raster3d_maps(rows, tar, list_file, new_cwd, fs) - - list_file.close() + with open(list_file_name, "w") as list_file: + if rows: + if type_ == "strds": + if format_ in {"GTiff", "AAIGrid"}: + _export_raster_maps_as_gdal( + rows, tar, list_file, new_cwd, fs, format_, datatype, **kwargs + ) + else: + _export_raster_maps(rows, tar, list_file, new_cwd, fs) + elif type_ == "stvds": + if format_ == "GML": + _export_vector_maps_as_gml(rows, tar, list_file, new_cwd, fs) + elif format_ == "GPKG": + _export_vector_maps_as_gpkg(rows, tar, list_file, new_cwd, fs) + else: + _export_vector_maps(rows, tar, list_file, new_cwd, fs) + elif type_ == "str3ds": + _export_raster3d_maps(rows, tar, list_file, new_cwd, fs) # Write projection and metadata proj = gs.read_command("g.proj", flags="j") Path(proj_file_name).write_text(proj) - init_file = open(init_file_name, "w") # Create the init string string = "" # This is optional, if not present strds will be assumed for backward @@ -423,62 +418,66 @@ def export_stds( string += "%s=%s\n" % ("south", south) string += "%s=%s\n" % ("east", east) string += "%s=%s\n" % ("west", west) - init_file.write(string) - init_file.close() + Path(init_file_name).write_text(string) metadata = gs.read_command("t.info", type=type_, input=sp.get_id()) Path(metadata_file_name).write_text(metadata) - read_file = open(read_file_name, "w") - if type_ == "strds": + with open(read_file_name, "w") as read_file: + if type_ == "strds": + read_file.write( + "This space time raster dataset was exported with " + "t.rast.export of GRASS GIS 8\n" + ) + elif type_ == "stvds": + read_file.write( + "This space time vector dataset was exported with " + "t.vect.export of GRASS GIS 8\n" + ) + elif type_ == "str3ds": + read_file.write( + "This space time 3D raster dataset was exported " + "with t.rast3d.export of GRASS GIS 8\n" + ) + read_file.write("\n") + read_file.write("Files:\n") + if type_ == "strds": + if format_ == "GTiff": + # 123456789012345678901234567890 + read_file.write(" *.tif -- GeoTIFF raster files\n") + read_file.write(" *.color -- GRASS GIS raster color rules\n") + elif format_ == "pack": + read_file.write( + " *.pack -- GRASS raster files packed with r.pack\n" + ) + elif type_ == "stvds": + # 123456789012345678901234567890 + if format_ == "GML": + read_file.write(" *.xml -- Vector GML files\n") + else: + read_file.write( + " *.pack -- GRASS vector files packed with v.pack\n" + ) + elif type_ == "str3ds": + read_file.write( + " *.pack -- GRASS 3D raster files packed with r3.pack\n" + ) read_file.write( - "This space time raster dataset was exported with " - "t.rast.export of GRASS GIS 8\n" + "%13s -- Projection information in PROJ.4 format\n" % (proj_file_name) ) - elif type_ == "stvds": read_file.write( - "This space time vector dataset was exported with " - "t.vect.export of GRASS GIS 8\n" + "%13s -- GRASS GIS space time %s dataset information\n" + % (init_file_name, sp.get_new_map_instance(None).get_type()) ) - elif type_ == "str3ds": read_file.write( - "This space time 3D raster dataset was exported " - "with t.rast3d.export of GRASS GIS 8\n" + "%13s -- Time series file, lists all maps by name " + "with interval\n" % (list_file_name) ) - read_file.write("\n") - read_file.write("Files:\n") - if type_ == "strds": - if format_ == "GTiff": - # 123456789012345678901234567890 - read_file.write(" *.tif -- GeoTIFF raster files\n") - read_file.write(" *.color -- GRASS GIS raster color rules\n") - elif format_ == "pack": - read_file.write(" *.pack -- GRASS raster files packed with r.pack\n") - elif type_ == "stvds": - # 123456789012345678901234567890 - if format_ == "GML": - read_file.write(" *.xml -- Vector GML files\n") - else: - read_file.write(" *.pack -- GRASS vector files packed with v.pack\n") - elif type_ == "str3ds": - read_file.write(" *.pack -- GRASS 3D raster files packed with r3.pack\n") - read_file.write( - "%13s -- Projection information in PROJ.4 format\n" % (proj_file_name) - ) - read_file.write( - "%13s -- GRASS GIS space time %s dataset information\n" - % (init_file_name, sp.get_new_map_instance(None).get_type()) - ) - read_file.write( - "%13s -- Time series file, lists all maps by name " - "with interval\n" % (list_file_name) - ) - read_file.write( - " time stamps in ISO-Format. Field separator is |\n" - ) - read_file.write("%13s -- The output of t.info\n" % (metadata_file_name)) - read_file.write("%13s -- This file\n" % (read_file_name)) - read_file.close() + read_file.write( + " time stamps in ISO-Format. Field separator is |\n" + ) + read_file.write("%13s -- The output of t.info\n" % (metadata_file_name)) + read_file.write("%13s -- This file\n" % (read_file_name)) # Append the file list tar.add(list_file_name) diff --git a/python/grass/temporal/stds_import.py b/python/grass/temporal/stds_import.py index a96c23fab88..d401aceea55 100644 --- a/python/grass/temporal/stds_import.py +++ b/python/grass/temporal/stds_import.py @@ -299,7 +299,6 @@ def import_stds( # Check projection information if not location: temp_name = gs.tempfile() - temp_file = open(temp_name, "w") proj_name = os.path.abspath(proj_file_name) # We need to convert projection strings generated @@ -313,9 +312,9 @@ def import_stds( proj_name_tmp = f"{temp_name}_in_projection" Path(proj_name_tmp).write_text(proj_content) - p = gs.start_command("g.proj", flags="j", stdout=temp_file) - p.communicate() - temp_file.close() + with open(temp_name, "w") as temp_file: + p = gs.start_command("g.proj", flags="j", stdout=temp_file) + p.communicate() if not gs.compare_key_value_text_files(temp_name, proj_name_tmp, sep="="): if overr: