From 5878436e95090903b4115818dc3d5b64975015f2 Mon Sep 17 00:00:00 2001 From: Mike Taves Date: Thu, 28 Nov 2024 10:36:17 +1300 Subject: [PATCH 1/3] refactor: clean-up strings and Path related aspects, add a few Ruff rules --- autotest/ci_setup.py | 7 +- autotest/test_build.py | 16 +--- autotest/test_cli_cmds.py | 42 +++------- autotest/test_gridgen.py | 12 +-- autotest/test_mf6_existing_meson.py | 14 +--- autotest/test_mp6.py | 12 +-- autotest/test_mt3dusgs.py | 5 +- autotest/test_requests.py | 17 ++-- pymake/cmds/createjson.py | 31 ++++---- pymake/cmds/mfpymakecli.py | 25 +++--- pymake/config.py | 12 +-- pymake/pymake.py | 50 ++++-------- pymake/pymake_base.py | 99 ++++++++++-------------- pymake/pymake_build_apps.py | 5 +- pymake/pymake_parser.py | 18 +++-- pymake/utils/_compiler_language_files.py | 4 +- pymake/utils/_compiler_switches.py | 18 ++--- pymake/utils/_dag.py | 4 +- pymake/utils/_meson_build.py | 48 ++++-------- pymake/utils/_usgs_src_update.py | 33 ++++---- pymake/utils/download.py | 25 +++--- pymake/utils/usgsprograms.py | 37 ++++----- pyproject.toml | 12 ++- 23 files changed, 219 insertions(+), 327 deletions(-) diff --git a/autotest/ci_setup.py b/autotest/ci_setup.py index 7eac513..fa59c53 100644 --- a/autotest/ci_setup.py +++ b/autotest/ci_setup.py @@ -1,14 +1,13 @@ import os -import pathlib as pl import shutil +from pathlib import Path from modflow_devtools.misc import get_model_paths import pymake -temp_pth = pl.Path("temp") -if not temp_pth.exists(): - temp_pth.mkdir() +temp_pth = Path("temp") +temp_pth.mkdir(exist_ok=True) mf6_exdir = temp_pth / "mf6examples" if mf6_exdir.is_dir(): shutil.rmtree(mf6_exdir) diff --git a/autotest/test_build.py b/autotest/test_build.py index 11a4e45..1dfca34 100644 --- a/autotest/test_build.py +++ b/autotest/test_build.py @@ -61,13 +61,7 @@ def test_build(function_tmpdir, target: str) -> None: pm.inplace = True fc = os.environ.get("FC", "gfortran") assert ( - pymake.build_apps( - target, - pm, - verbose=True, - clean=False, - ) - == 0 + pymake.build_apps(target, pm, verbose=True, clean=False) == 0 ), f"could not compile {target}" @@ -80,13 +74,7 @@ def test_meson_build(function_tmpdir, target: str) -> None: pymake.linker_update_environment(cc=cc, fc=fc) with set_dir(function_tmpdir): assert ( - pymake.build_apps( - target, - verbose=True, - clean=False, - meson=True, - ) - == 0 + pymake.build_apps(target, verbose=True, clean=False, meson=True) == 0 ), f"could not compile {target}" diff --git a/autotest/test_cli_cmds.py b/autotest/test_cli_cmds.py index 6465f5d..f365559 100644 --- a/autotest/test_cli_cmds.py +++ b/autotest/test_cli_cmds.py @@ -1,7 +1,8 @@ import os -import pathlib as pl import subprocess +from pathlib import Path from platform import system +from textwrap import dedent import pytest from flaky import flaky @@ -47,13 +48,7 @@ def run_cli_cmd(cmd: list) -> None: @pytest.mark.parametrize("target", targets) def test_make_program(function_tmpdir, target: str) -> None: with set_dir(function_tmpdir): - cmd = [ - "make-program", - target, - "--appdir", - ".", - "--verbose", - ] + cmd = ["make-program", target, "--appdir", ".", "--verbose"] run_cli_cmd(cmd) @@ -62,14 +57,7 @@ def test_make_program(function_tmpdir, target: str) -> None: @pytest.mark.base def test_make_program_double(function_tmpdir) -> None: with set_dir(function_tmpdir): - cmd = [ - "make-program", - "mf2005", - "--double", - "--verbose", - "--appdir", - ".", - ] + cmd = ["make-program", "mf2005", "--double", "--verbose", "--appdir", "."] run_cli_cmd(cmd) @@ -77,13 +65,7 @@ def test_make_program_double(function_tmpdir) -> None: @pytest.mark.schedule def test_make_program_all(module_tmpdir) -> None: with set_dir(module_tmpdir): - cmd = [ - "make-program", - ":", - "--appdir", - ".", - "--verbose", - ] + cmd = ["make-program", ":", "--appdir", ".", "--verbose"] run_cli_cmd(cmd) @@ -93,13 +75,13 @@ def test_make_program_all(module_tmpdir) -> None: @pytest.mark.parametrize("meson", meson_parm) def test_mfpymake(function_tmpdir, meson: bool) -> None: with set_dir(function_tmpdir): - src = ( - "program hello\n" - + " ! This is a comment line; it is ignored by the compiler\n" - + " print *, 'Hello, World!'\n" - + "end program hello\n" - ) - src_file = pl.Path("src/hello.f90") + src = dedent("""\ + program hello + ! This is a comment line; it is ignored by the compiler + print *, 'Hello, World!' + end program hello + """) + src_file = Path("src/hello.f90") src_file.parent.mkdir(parents=True, exist_ok=True) with open(src_file, "w") as f: f.write(src) diff --git a/autotest/test_gridgen.py b/autotest/test_gridgen.py index 00a0aec..4b80016 100644 --- a/autotest/test_gridgen.py +++ b/autotest/test_gridgen.py @@ -1,6 +1,6 @@ -import pathlib as pl import subprocess from os import environ +from pathlib import Path from platform import system import pytest @@ -11,7 +11,7 @@ @pytest.fixture(scope="module") -def target(module_tmpdir) -> pl.Path: +def target(module_tmpdir) -> Path: name = TARGET_NAME ext = ".exe" if system() == "Windows" else "" return module_tmpdir / f"{name}{ext}" @@ -23,7 +23,7 @@ def prog_data(target) -> dict: @pytest.fixture(scope="module") -def workspace(module_tmpdir, prog_data) -> pl.Path: +def workspace(module_tmpdir, prog_data) -> Path: return module_tmpdir / prog_data.dirname @@ -42,11 +42,7 @@ def pm(module_tmpdir, target) -> pymake.Pymake: def run_command(args, cwd): p = subprocess.Popen( - args, - shell=False, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - cwd=cwd, + args, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=cwd ) for line in p.stdout.readlines(): print(line.decode().strip()) diff --git a/autotest/test_mf6_existing_meson.py b/autotest/test_mf6_existing_meson.py index 4413c80..13d1fca 100644 --- a/autotest/test_mf6_existing_meson.py +++ b/autotest/test_mf6_existing_meson.py @@ -89,16 +89,10 @@ def test_build_with_existing_meson(pm, module_tmpdir, workspace, targets): linker_update_environment(cc=cc, fc=fc) # make modflow 6 with existing meson.build file - returncode = pymake.meson_build( - workspace, - fc, - cc, - appdir=pm.appdir, - ) - assert returncode == 0, ( - "could not build modflow 6 applications " - + "using existing meson.build file" - ) + returncode = pymake.meson_build(workspace, fc, cc, appdir=pm.appdir) + assert ( + returncode == 0 + ), "could not build modflow 6 applications using existing meson.build file" # check that all of the executables exist for executable in targets: diff --git a/autotest/test_mp6.py b/autotest/test_mp6.py index 0e5ba9d..55d1eba 100644 --- a/autotest/test_mp6.py +++ b/autotest/test_mp6.py @@ -44,20 +44,12 @@ def update_files(fn, workspace): if rf in fn.lower(): fname1 = workspace / f"{rf}.locations" fname2 = workspace / f"{rf}_mod.locations" - print( - "copy {} to {}".format( - os.path.basename(fname1), os.path.basename(fname2) - ) - ) + print(f"copy {os.path.basename(fname1)} to {os.path.basename(fname2)}") shutil.copy(fname1, fname2) print(f"deleting {os.path.basename(fname1)}") os.remove(fname1) fname1 = workspace / f"{rf.upper()}.locations" - print( - "renmae {} to {}".format( - os.path.basename(fname2), os.path.basename(fname1) - ) - ) + print(f"renmae {os.path.basename(fname2)} to {os.path.basename(fname1)}") os.rename(fname2, fname1) diff --git a/autotest/test_mt3dusgs.py b/autotest/test_mt3dusgs.py index 053e83e..07ee199 100644 --- a/autotest/test_mt3dusgs.py +++ b/autotest/test_mt3dusgs.py @@ -158,8 +158,5 @@ def test_mt3dusgs(module_tmpdir, workspace, ws, target): pytest.skip(reason="excluding {ws}") assert run_mt3dusgs( - workspace / "data" / ws, - target, - mfnwt_exe, - module_tmpdir / "mf6", + workspace / "data" / ws, target, mfnwt_exe, module_tmpdir / "mf6" ), f"could not run {ws}" diff --git a/autotest/test_requests.py b/autotest/test_requests.py index 1002373..2203910 100644 --- a/autotest/test_requests.py +++ b/autotest/test_requests.py @@ -77,9 +77,8 @@ def test_latest_version(): version = pymake.repo_latest_version() test_version = "5.0" msg = ( - f"returned version ({version}) " - + "is not greater than or equal to " - + f"defined version ({test_version})" + f"returned version ({version}) is not greater than or equal to " + f"defined version ({test_version})" ) if version is not None: assert float(version) >= float(test_version), msg @@ -125,10 +124,7 @@ def test_previous_assets(): assets = pymake.get_repo_assets( mfexes_repo_name, version=version, error_return=True ) - msg = ( - "failed to get release {} ".format(version) - + f"from the '{mfexes_repo_name}' repo" - ) + msg = f"failed to get release {version} " + f"from the '{mfexes_repo_name}' repo" if allow_failure: if not isinstance(assets, dict): print(msg) @@ -235,10 +231,7 @@ def test_target_keys(): for target in targets: target_dict = pymake.usgs_program_data.get_target(target) test_dict = prog_dict[target] - msg = ( - f"dictionary from {target} " - + "does not match dictionary from .get_target()" - ) + msg = f"dictionary from {target} does not match dictionary from .get_target()" assert target_dict == test_dict, msg @@ -268,7 +261,7 @@ def test_usgsprograms_export_json(module_tmpdir): temp_dict[fill_key] = value[fill_key] msg = ( f"json dictionary for {key} key " - + "is not equal to the .usgs_prog_data dictionary" + "is not equal to the .usgs_prog_data dictionary" ) assert value == temp_dict, msg diff --git a/pymake/cmds/createjson.py b/pymake/cmds/createjson.py index 97d1343..70034a7 100755 --- a/pymake/cmds/createjson.py +++ b/pymake/cmds/createjson.py @@ -50,16 +50,14 @@ def main() -> None: "-f", "--fpth", ), - "help": "Path for the json file to be created. " - + 'Default is "code.json".', + "help": 'Path for the json file to be created. Default is "code.json".', "default": "code.json", "choices": None, "action": None, }, "appdir": { "tag": ("-ad", "--appdir"), - "help": "code.json path that overides FPTH defined path, " - + "Default is None.", + "help": "code.json path that overides FPTH defined path. Default is None.", "default": None, "choices": None, "action": None, @@ -67,8 +65,7 @@ def main() -> None: "prog_data": { "tag": ("--prog_data",), "help": "User-specified program database. If prog_data is None, " - + "it will be created from the USGS program database." - + "Default is None.", + "it will be created from the USGS program database. Default is None.", "default": None, "choices": None, "action": None, @@ -76,8 +73,8 @@ def main() -> None: "current": { "tag": ("--current",), "help": "If False, all USGS program targets are listed. " - + "If True, only USGS program targets that are " - + " defined as current are listed. Default is True.", + "If True, only USGS program targets that are defined as current " + "are listed. Default is True.", "default": True, "choices": None, "action": "store_true", @@ -87,20 +84,19 @@ def main() -> None: "-u", "--update", ), - "help": "If True, existing targets in the user-specified " - + "program database with values in the USGS " - + "program database. If False, existing targets " - + "in the user-specified program database " - + "will not be updated. Default is True.", + "help": "If True, existing targets in the user-specified program database " + "with values in the USGS program database. If False, existing targets " + "in the user-specified program database will not be updated. " + "Default is True.", "default": True, "choices": None, "action": "store_true", }, "write_markdown": { "tag": ("--write_markdown",), - "help": "If True, write markdown file that includes the " - + "target name, version, and the last-modified date of " - + "the download asset (url). Default is True.", + "help": "If True, write markdown file that includes the target name, " + "version, and the last-modified date of the download asset (url). " + "Default is True.", "default": True, "choices": None, "action": "store_true", @@ -108,8 +104,7 @@ def main() -> None: "partial_json": { "tag": ("--partial_json",), "help": "Create a partial code.json based on targets in the " - + "parent path for the code.json file (FPTH). " - + "Default is False.", + "parent path for the code.json file (FPTH). Default is False.", "default": False, "choices": None, "action": "store_true", diff --git a/pymake/cmds/mfpymakecli.py b/pymake/cmds/mfpymakecli.py index bd2150c..8a94e1a 100755 --- a/pymake/cmds/mfpymakecli.py +++ b/pymake/cmds/mfpymakecli.py @@ -7,6 +7,7 @@ import sys from pathlib import Path +from textwrap import dedent from pymake.pymake_base import main as pymake_main from pymake.pymake_parser import parser @@ -25,16 +26,20 @@ def main() -> None: """ # Show meaningful examples at bottom of help prog = Path(sys.argv[0]).stem - examples = ( - "Examples:\n\n" - + "Compile MODFLOW 6 from the root directory containing the \n" - + "source files in subdirectories in the src/ subdirectory:\n\n" - + f"$ {prog} src/ mf6 --subdirs\n\n" - + "Compile MODFLOW 6 in the bin subdirectory using the Intel \n" - + "Fortran compiler from the root directory containing the source \n" - + "files in subdirectories in the the src/ subdirectory:\n\n" - + f"$ {prog} src/ mf6 --subdirs -fc ifort --appdir bin\n" - ) + examples = dedent(f"""\ + Examples: + + Compile MODFLOW 6 from the root directory containing the + source files in subdirectories in the src/ subdirectory: + + $ {prog} src/ mf6 --subdirs + + Compile MODFLOW 6 in the bin subdirectory using the Intel + Fortran compiler from the root directory containing the source + files in subdirectories in the the src/ subdirectory: + + $ {prog} src/ mf6 --subdirs -fc ifort --appdir bin + """) # get the arguments args = parser(examples=examples) diff --git a/pymake/config.py b/pymake/config.py index 83c7efe..05f5f17 100644 --- a/pymake/config.py +++ b/pymake/config.py @@ -4,9 +4,9 @@ __maintainer__ = "Joseph D. Hughes" __email__ = "jdhughes@usgs.gov" __status__ = "Production" -__description__ = ( - "This is the pymake program for compiling fortran, c, and c++ source\n" - + "files, such as the source files that come with MODFLOW. The program\n" - + "works by building a directed acyclic graph of the module dependencies\n" - + "and then compiling the source files in the proper order." -) +__description__ = """\ +This is the pymake program for compiling fortran, c, and c++ source +files, such as the source files that come with MODFLOW. The program +works by building a directed acyclic graph of the module dependencies +and then compiling the source files in the proper order. +""" diff --git a/pymake/pymake.py b/pymake/pymake.py index 268698a..71e9d0e 100644 --- a/pymake/pymake.py +++ b/pymake/pymake.py @@ -54,10 +54,10 @@ import argparse import os -import pathlib as pl import shutil import sys import time +from pathlib import Path from zipfile import ZipFile from .config import __description__ @@ -211,9 +211,7 @@ def _arg_parser(self): """ loc_dict = _get_standard_arg_dict() - parser = argparse.ArgumentParser( - description=__description__, - ) + parser = argparse.ArgumentParser(description=__description__) for _, value in loc_dict.items(): tag = value["tag"][0] # only process optional command line variables @@ -255,22 +253,17 @@ def compress_targets(self): targets.append(target) # add code.json - if pl.Path("code.json").exists(): + if Path("code.json").exists(): if "code.json" not in targets: targets.append("code.json") # delete the zip file if it exists + zip_str = str(Path(zip_pth).resolve()) if os.path.exists(zip_pth): if self.keep: if self.verbose: - print( - "Appending files to existing " - + f"zipfile '{pl.Path(zip_pth).resolve()}'" - ) - with ZipFile( - zip_pth, - mode="r", - ) as zf: + print(f"Appending files to existing zipfile '{zip_str}'") + with ZipFile(zip_pth, mode="r") as zf: available_file_paths = zf.namelist() pop_list = [] for target in targets: @@ -282,27 +275,20 @@ def compress_targets(self): targets.remove(target) else: if self.verbose: - print( - "Deleting existing zipfile " - + f"'{pl.Path(zip_pth).resolve()}'" - ) + print(f"Deleting existing zipfile '{zip_str}'") os.remove(zip_pth) # print a message describing the zip process if self.verbose: print( - f"Compressing files in '{appdir}' " - + f"directory to zip file '{pl.Path(zip_pth).resolve()}'" + f"Compressing files in '{appdir}' directory to zip file '{zip_str}'" ) for idx, target in enumerate(targets): - print(f" {idx + 1:>3d}. adding " + f"'{target}' to zipfile") + print(f" {idx + 1:>3d}. adding '{target}' to zipfile") # compress the compiled executables if not zip_all( - zip_pth, - dir_pths=appdir, - patterns=targets, - append=self.keep, + zip_pth, dir_pths=appdir, patterns=targets, append=self.keep ): self.returncode = 1 @@ -404,11 +390,7 @@ def download_target( """ # setup the download self.download_setup( - target, - url=url, - download_path=download_path, - verify=verify, - timeout=timeout, + target, url=url, download_path=download_path, verify=verify, timeout=timeout ) return self.download_url() @@ -466,13 +448,15 @@ def _download_cleanup(self): if self.verbose: print( "removing download " - + f"directory...'{self.download_dir}'" + f"directory...'{self.download_dir}'" ) break except: if self.verbose: - msg = f" removal attempt {itries + 1:>2d} " - msg += f"of {ntries:>2d}" + msg = ( + f" removal attempt {itries + 1:>2d} " + f"of {ntries:>2d}" + ) print(msg) # wait prior to returning on windows @@ -649,7 +633,7 @@ def _set_extrafiles(self): fpth = os.path.join(srcdir, extrafiles) extrafiles = os.path.normpath(fpth) else: - msg = "invalid extrafiles format - " + "must be a list or string" + msg = "invalid extrafiles format - must be a list or string" raise ValueError(msg) # reset extrafiles diff --git a/pymake/pymake_base.py b/pymake/pymake_base.py index 3b1b1e8..966b6f2 100644 --- a/pymake/pymake_base.py +++ b/pymake/pymake_base.py @@ -42,10 +42,11 @@ import inspect import os -import pathlib as pl import shutil import sys import traceback +from pathlib import Path +from textwrap import dedent from .config import __version__ from .utils._compiler_language_files import ( @@ -185,13 +186,12 @@ def main( inplace = True print( f"Using meson to build {os.path.basename(target)}, " - + "resetting inplace to True" + "resetting inplace to True" ) if srcdir is not None and target is not None: objdir_temp, moddir_temp, srcdir_temp = get_temporary_directories( - appdir=appdir, - target=pl.Path(target).stem, + appdir=appdir, target=Path(target).stem ) if inplace: srcdir_temp = srcdir @@ -213,8 +213,8 @@ def main( cc = None if fc is None and cc is None: msg = ( - "Nothing to do the fortran (-fc) and c/c++ compilers (-cc)" - + "are both 'none'." + "Nothing to do the fortran (-fc) and c/c++ compilers (-cc) " + "are both 'none'." ) raise ValueError(msg) @@ -360,9 +360,8 @@ def main( ) else: msg = ( - f"Nothing to do, the srcdir ({srcdir}) " - + f"and/or target ({target}) " - + "are not specified." + f"Nothing to do, the srcdir ({srcdir}) and/or target ({target}) " + "are not specified." ) raise ValueError(msg) @@ -437,9 +436,7 @@ def _pymake_initialize( shutil.rmtree(srcdir_temp) if excludefiles: shutil.copytree( - srcdir, - srcdir_temp, - ignore=shutil.ignore_patterns(*excludefiles), + srcdir, srcdir_temp, ignore=shutil.ignore_patterns(*excludefiles) ) else: shutil.copytree(srcdir, srcdir_temp) @@ -456,11 +453,7 @@ def _pymake_initialize( srcdir_temp, os.path.basename(os.path.normpath(commonsrc)) ) if excludefiles: - shutil.copytree( - src, - dst, - ignore=shutil.ignore_patterns(*excludefiles), - ) + shutil.copytree(src, dst, ignore=shutil.ignore_patterns(*excludefiles)) else: shutil.copytree(src, dst) else: @@ -635,7 +628,7 @@ def _clean_temp_files( # remove temporary directories if verbose: - msg = "\nCleaning up temporary source, object, " + "and module directories..." + msg = "\nCleaning up temporary source, object, and module directories..." print(msg) if not inplace: if os.path.isdir(srcdir_temp): @@ -714,14 +707,14 @@ def _create_openspec(intelwin, srcfiles, verbose): data_access = "STREAM" data_form = "UNFORMATTED" - line = ( - "c -- created by pymake_base.py\n" - + " CHARACTER*20 ACCESS,FORM,ACTION(2)\n" - + f" DATA ACCESS/'{data_access}'/\n" - + f" DATA FORM/'{data_form}'/\n" - + " DATA (ACTION(I),I=1,2)/'READ','READWRITE'/\n" - + "c -- end of include file\n" - ) + line = dedent(f"""\ + c -- created by pymake_base.py + CHARACTER*20 ACCESS,FORM,ACTION(2) + DATA ACCESS/'{data_access}'/ + DATA FORM/'{data_form}'/ + DATA (ACTION(I),I=1,2)/'READ','READWRITE'/ + c -- end of include file + """) f.write(line) f.close() @@ -772,7 +765,7 @@ def _pymake_compile( """Standard compile method. Parameters - ------- + ---------- srcfiles : list list of source file names target : str @@ -836,8 +829,7 @@ def _pymake_compile( # get temporary object and module directories objdir_temp, moddir_temp, _ = get_temporary_directories( - os.path.dirname(target), - target=pl.Path(target).stem, + os.path.dirname(target), target=Path(target).stem ) # set optimization levels @@ -1044,24 +1036,19 @@ def _pymake_compile( # execute each command in cmdlists if not dryrun: + target_str = os.path.basename(target) for idx, cmdlist in enumerate(cmdlists): if idx == 0: if intelwin: msg = ( - f"\nCompiling '{os.path.basename(target)}' " - + "for Windows using Intel compilers..." + f"\nCompiling '{target_str}' " + "for Windows using Intel compilers..." ) else: - msg = ( - "\nCompiling object files for " - + f"'{os.path.basename(target)}'" - ) + msg = f"\nCompiling object files for '{target_str}'" print(msg) if idx > 0 and idx == ilink: - msg = ( - "\nLinking object files " - + f"to make '{os.path.basename(target)}'..." - ) + msg = f"\nLinking object files to make '{target_str}'..." print(msg) # write the command to the terminal @@ -1110,7 +1097,7 @@ def _create_win_batch( """Make an intel compiler batch file for compiling on windows. Parameters - ------- + ---------- batchfile : str batch file name to create fc : str @@ -1153,10 +1140,10 @@ def _create_win_batch( if on_env_var == oneapi_list[0]: cpvars = ( "C:\\Program Files (x86)\\Intel\\oneAPI\\compiler\\" - + f"{latest_version}\\env\\vars.bat" + f"{latest_version}\\env\\vars.bat" ) else: - cpvars = "C:\\Program Files (x86)\\Intel\\oneAPI\\" + "setvars.bat" + cpvars = "C:\\Program Files (x86)\\Intel\\oneAPI\\setvars.bat" if not os.path.isfile(cpvars): raise Exception(f"Could not find cpvars: {cpvars}") intel_setvars = f'"{cpvars}"' @@ -1174,9 +1161,7 @@ def _create_win_batch( break # check if either OneAPI or stand alone intel is installed if intel_setvars is None: - err_msg = ( - "OneAPI or stand alone version of Intel compilers " + "is not installed" - ) + err_msg = "OneAPI or stand alone version of Intel compilers is not installed" raise ValueError(err_msg) # open the batch file @@ -1195,7 +1180,8 @@ def _create_win_batch( searchdir.append(dirname) # write commands to build object files - line = "echo Creating object files to create '" + os.path.basename(target) + "'\n" + target_str = os.path.basename(target) + line = f"echo Creating object files to create '{target_str}'\n" f.write(line) for srcfile in srcfiles: if srcfile.endswith(".c") or srcfile.endswith(".cpp"): @@ -1209,8 +1195,7 @@ def _create_win_batch( cmd += f"/I{sd} " obj = os.path.join( - objdir_temp, - os.path.splitext(os.path.basename(srcfile))[0] + ".obj", + objdir_temp, os.path.splitext(os.path.basename(srcfile))[0] + ".obj" ) cmd += "/Fo:" + obj + " " cmd += srcfile @@ -1229,7 +1214,7 @@ def _create_win_batch( f.write(cmd + "\n") # write commands to link - line = "echo Linking object files to create '" + os.path.basename(target) + "'\n" + line = f"echo Linking object files to create '{target_str}'\n" f.write(line) # assemble the link command @@ -1408,8 +1393,8 @@ def _create_makefile( for ext in cext: line += f"{ext} " line += objext - f.write(f"{line}\n") - f.write("\n") + f.write(line) + f.write("\n\n") f.write("OBJECTS = \\\n") for idx, srcfile in enumerate(srcfiles): @@ -1434,16 +1419,16 @@ def _create_makefile( f.write("\t@mkdir -p $(@D)\n") line = ( "\t$(FC) $(OPTLEVEL) $(FFLAGS) -c $< -o $@ " - + "$(INCSWITCH) $(MODSWITCH)\n" + "$(INCSWITCH) $(MODSWITCH)\n\n" ) - f.write(f"{line}\n") + f.write(line) if cext is not None: for ext in cext: f.write(f"$(OBJDIR)/%{objext} : %{ext}\n") f.write("\t@mkdir -p $(@D)\n") - line = "\t$(CC) $(OPTLEVEL) $(CFLAGS) -c $< -o $@ " + "$(INCSWITCH)\n" - f.write(f"{line}\n") + line = "\t$(CC) $(OPTLEVEL) $(CFLAGS) -c $< -o $@ $(INCSWITCH)\n\n" + f.write(line) # close the makefile f.close() @@ -1463,7 +1448,7 @@ def _create_makefile( line += "\tdetected_OS = Windows\n" line += "\tOS_macro = -D_WIN32\n" line += "else\n" - line += "\tdetected_OS = $(shell sh -c 'uname 2>/dev/null " + "|| echo Unknown')\n" + line += "\tdetected_OS = $(shell sh -c 'uname 2>/dev/null || echo Unknown')\n" line += "\tifeq ($(detected_OS), Darwin)\n" line += "\t\tOS_macro = -D__APPLE__\n" line += "\telse\n" @@ -1482,7 +1467,7 @@ def _create_makefile( # write header line = ( "# Define the directories for the object and module files\n" - + "# and the executable and its path.\n" + "# and the executable and its path.\n" ) tpth = dpth.replace("\\", "/") line += f"BINDIR = {tpth}\n" diff --git a/pymake/pymake_build_apps.py b/pymake/pymake_build_apps.py index b976c01..5a9415c 100644 --- a/pymake/pymake_build_apps.py +++ b/pymake/pymake_build_apps.py @@ -99,8 +99,7 @@ def build_apps( pmobj = pymake_object else: msg = ( - f"pymake_object ({type(pymake_object)}) " - + f"is not of type {type(Pymake())}" + f"pymake_object ({type(pymake_object)}) is not of type {type(Pymake())}" ) raise TypeError(msg) @@ -147,7 +146,7 @@ def build_apps( if pmobj.verbose: print( f"{target} will be built " - + f'for the "{sys.platform}" operating system\n' + f'for the "{sys.platform}" operating system\n' ) # save initial compiler settings diff --git a/pymake/pymake_parser.py b/pymake/pymake_parser.py index 33f1b12..68d05ad 100644 --- a/pymake/pymake_parser.py +++ b/pymake/pymake_parser.py @@ -13,6 +13,7 @@ """ import argparse +from textwrap import dedent from .config import __description__ @@ -323,14 +324,15 @@ def parser(examples=None): Namespace with command line arguments """ - epilog = ( - "Note that the source directory should not contain any bad \n" - + "or duplicate source files as all source files in the source \n" - + "directory, the common source file directory (srcdir2), and \n" - + "the extra files (extrafiles) will be built and linked. \n" - + "Files can be excluded by using the excludefiles command \n" - + "line switch.\n\n" - ) + epilog = dedent("""\ + Note that the source directory should not contain any bad + or duplicate source files as all source files in the source + directory, the common source file directory (srcdir2), and + the extra files (extrafiles) will be built and linked. + Files can be excluded by using the excludefiles command + line switch. + + """) if examples is not None: epilog += examples description = __description__ diff --git a/pymake/utils/_compiler_language_files.py b/pymake/utils/_compiler_language_files.py index 6c2c9d8..621045b 100644 --- a/pymake/utils/_compiler_language_files.py +++ b/pymake/utils/_compiler_language_files.py @@ -9,7 +9,7 @@ def _get_fortran_files(srcfiles, extensions=False): """Return a list of fortran files or unique fortran file extensions. Parameters - ------- + ---------- srcfiles : list list of source file names extensions : bool @@ -48,7 +48,7 @@ def _get_c_files(srcfiles, extensions=False): """Return a list of c and cpp files or unique c and cpp file extensions. Parameters - ------- + ---------- srcfiles : list list of source file names extensions : bool diff --git a/pymake/utils/_compiler_switches.py b/pymake/utils/_compiler_switches.py index b5f3c79..74fdedf 100644 --- a/pymake/utils/_compiler_switches.py +++ b/pymake/utils/_compiler_switches.py @@ -96,7 +96,7 @@ def _get_osname(): """Return the lower case OS platform name. Parameters - ------- + ---------- Returns ------- @@ -140,7 +140,7 @@ def _get_prepend(compiler, osname): """Return the appropriate prepend for a compiler switch for a OS. Parameters - ------- + ---------- compiler : str compiler name osname : str @@ -166,7 +166,7 @@ def _get_optlevel(target, fc, cc, debug, fflags, cflags, osname=None): """Return a compiler optimization switch. Parameters - ------- + ---------- target : str executable to create fc : str @@ -182,8 +182,6 @@ def _get_optlevel(target, fc, cc, debug, fflags, cflags, osname=None): osname : str optional lower case OS name. If not passed it will be determined using sys.platform - verbose : bool - boolean for verbose output to terminal Returns ------- @@ -269,7 +267,7 @@ def _get_fortran_flags( """Return a list of pymake and user specified fortran compiler switches. Parameters - ------- + ---------- target : str executable to create fc : str @@ -428,7 +426,7 @@ def _get_c_flags( """Return a list of standard and user specified c/c++ compiler switches. Parameters - ------- + ---------- target : str executable to create cc : str @@ -582,7 +580,7 @@ def _get_linker_flags( specified linker switches (syslibs). Parameters - ------- + ---------- target : str executable to create fc : str @@ -847,7 +845,7 @@ def _set_fflags(target, fc="gfortran", argv=True, osname=None, verbose=False): if verbose: msg = ( f"{target} fortran code " - + "will be built with the following predefined flags:\n" + "will be built with the following predefined flags:\n" ) msg += f" {' '.join(fflags)}\n" print(msg) @@ -917,7 +915,7 @@ def _set_cflags(target, cc="gcc", argv=True, osname=None, verbose=False): if verbose: msg = ( f"{target} c/c++ code " - + "will be built with the following predefined flags:\n" + "will be built with the following predefined flags:\n" ) msg += f" {' '.join(cflags)}\n" print(msg) diff --git a/pymake/utils/_dag.py b/pymake/utils/_dag.py index 8f119e6..7e55fa2 100644 --- a/pymake/utils/_dag.py +++ b/pymake/utils/_dag.py @@ -14,7 +14,7 @@ import os -class Node(object): +class Node: def __init__(self, name): self.name = name self.dependencies = [] @@ -27,7 +27,7 @@ def add_dependency(self, dependency): return -class DirectedAcyclicGraph(object): +class DirectedAcyclicGraph: def __init__(self, nodelist, networkx=False): self.nodelist = nodelist self.networkx = networkx diff --git a/pymake/utils/_meson_build.py b/pymake/utils/_meson_build.py index 386f349..aa816e3 100644 --- a/pymake/utils/_meson_build.py +++ b/pymake/utils/_meson_build.py @@ -84,19 +84,19 @@ def meson_build( else: print( "Could not run 'meson setup' using the meson build file " - + f"'{meson_test_path}'" + f"'{meson_test_path}'" ) # return if setup and install were successful if returncode == 0: print( "\n\nBuilt executable(s) using the meson build file " - + f"'{meson_test_path}'" + f"'{meson_test_path}'" ) else: print( "Could not run 'meson install' using the meson build file " - + f"'{meson_test_path}'" + f"'{meson_test_path}'" ) else: returncode = 1 @@ -264,7 +264,7 @@ def _meson_build( """Build the target using meson Parameters - ------- + ---------- target : str path for executable to create srcdir : str @@ -305,12 +305,7 @@ def _meson_build( """ # use existing build file if it already exists - returncode = meson_build( - mesondir, - fc=fc, - cc=cc, - appdir=os.path.dirname(target), - ) + returncode = meson_build(mesondir, fc=fc, cc=cc, appdir=os.path.dirname(target)) if returncode == 0: return returncode @@ -325,10 +320,7 @@ def _meson_build( source_path_dict["extra"] = common_path # write meson.build files with directories in each source directory - _create_source_meson_build( - source_path_dict, - srcfiles, - ) + _create_source_meson_build(source_path_dict, srcfiles) # write main meson.build file main_meson_file, fc_meson, cc_meson = _create_main_meson_build( @@ -389,7 +381,7 @@ def _create_main_meson_build( source_path_dict verbose Parameters - ------- + ---------- mesondir : str Main meson.build file path target : str @@ -431,7 +423,7 @@ def _create_main_meson_build( """ appdir = os.path.relpath(os.path.dirname(target), mesondir) - target = os.path.splitext((os.path.basename(target)))[0] + target = os.path.splitext(os.path.basename(target))[0] osname = _get_osname() # get target version number @@ -452,7 +444,7 @@ def _create_main_meson_build( if mainfile is None: linker_language = "fortran" else: - main_ext = os.path.splitext((os.path.basename(mainfile)))[1].lower() + main_ext = os.path.splitext(os.path.basename(mainfile))[1].lower() if fext is not None: if main_ext in fext: linker_language = "fortran" @@ -520,15 +512,7 @@ def _create_main_meson_build( ) # optimization level - optlevel = _get_optlevel( - target, - fc, - cc, - debug, - fflags, - cflags, - verbose=verbose, - ) + optlevel = _get_optlevel(target, fc, cc, debug, fflags, cflags) optlevel_int = int(optlevel.replace("-O", "").replace("/O", "")) main_meson_file = Path(mesondir) / "meson.build" @@ -593,9 +577,9 @@ def _create_main_meson_build( compiler_abbr = "cc" line += ( "add_project_arguments(" - + f"{compiler_abbr}.get_supported_arguments(" - + f"{compiler_abbr}_compile_args), " - + f"language: '{language}')\n" + f"{compiler_abbr}.get_supported_arguments(" + f"{compiler_abbr}_compile_args), " + f"language: '{language}')\n" ) if linker_language == "fortran": linker_abbr = "fc" @@ -637,13 +621,13 @@ def _create_main_meson_build( if sharedobject: line = ( f"library('{target}', sources{include_text}" - + ", install: true, name_prefix: '', " - + f"install_dir: '{appdir}')\n\n" + ", install: true, name_prefix: '', " + f"install_dir: '{appdir}')\n\n" ) else: line = ( f"executable('{target}', sources{include_text}" - + f", install: true, install_dir: '{appdir}')\n\n" + f", install: true, install_dir: '{appdir}')\n\n" ) f.write(line) diff --git a/pymake/utils/_usgs_src_update.py b/pymake/utils/_usgs_src_update.py index 9a15480..a167e7d 100644 --- a/pymake/utils/_usgs_src_update.py +++ b/pymake/utils/_usgs_src_update.py @@ -4,10 +4,10 @@ """ import os -import pathlib as pl import shutil import sys import types +from pathlib import Path from typing import Union from ..utils.usgsprograms import usgs_program_data @@ -68,7 +68,7 @@ def _build_replace(targets): # remove exe extension from targets, as well as # d (debug) and dbl (double precision) suffixes for idx, target in enumerate(targets): - tgt = pl.Path(target).with_suffix("").name.replace("dbl", "") + tgt = Path(target).with_suffix("").name.replace("dbl", "") if tgt.endswith("d") and tgt[:-1] in usgs_program_data.get_keys(): tgt = tgt[:-1] targets[idx] = tgt @@ -329,17 +329,20 @@ def _update_mfusg_gsi_files(srcdir, fc, cc, arch, double): """ tags = { - "FMTARG = 'BINARY'": "FMTARG = 'UNFORMATTED'\n ACCARG = 'STREAM'", # noqa: E501 + "FMTARG = 'BINARY'": "FMTARG = 'UNFORMATTED'\n ACCARG = 'STREAM'", ",SHARED,ACCESS='SEQUENTIAL'": ",ACCESS='SEQUENTIAL'", "FORM=FMTARG,SHARED,": "FORM=FMTARG,", ",BUFFERED='YES',": ",", ", BUFFERED='NO')": ")", ",SHARE = 'DENYNONE'": ",", ", SHARE = 'DENYNONE',": ",", - "FORM='FORMATTED',ACCESS='SEQUENTIAL',": "FORM='FORMATTED',ACCESS='SEQUENTIAL'", # noqa: E501 + "FORM='FORMATTED',ACCESS='SEQUENTIAL',": "FORM='FORMATTED',ACCESS='SEQUENTIAL'", } - fpth = pl.Path(srcdir) / "glo2basu1.f" + if not isinstance(srcdir, Path): + srcdir = Path(srcdir) + + fpth = srcdir / "glo2basu1.f" if fpth.exists(): with open(fpth) as f: lines = f.readlines() @@ -353,7 +356,7 @@ def _update_mfusg_gsi_files(srcdir, fc, cc, arch, double): tags = {",share='DENYNONE',": ","} - fpth = pl.Path(srcdir) / "UpdtSt.for" + fpth = srcdir / "UpdtSt.for" if fpth.exists(): with open(fpth) as f: lines = f.readlines() @@ -367,7 +370,7 @@ def _update_mfusg_gsi_files(srcdir, fc, cc, arch, double): tag = "DEALLOCATE(ITHFLG)" tag2 = "DEALLOCATE(LAYTYP)" - fpth = pl.Path(srcdir) / "gwf2bcf-lpf-u1.f" + fpth = srcdir / "gwf2bcf-lpf-u1.f" if fpth.exists(): with open(fpth) as f: lines = f.readlines() @@ -382,7 +385,7 @@ def _update_mfusg_gsi_files(srcdir, fc, cc, arch, double): tag = "FORM = 'BINARY'," tag2 = "FORM = FORMC," - fpth = pl.Path(srcdir) / "gwt2dptu1.f" + fpth = srcdir / "gwt2dptu1.f" if fpth.exists(): with open(fpth) as f: lines = f.readlines() @@ -395,7 +398,7 @@ def _update_mfusg_gsi_files(srcdir, fc, cc, arch, double): tag = "FORM = 'BINARY'," tag2 = "FORM = FORM," - fpth = pl.Path(srcdir) / "glo2btnu1.f" + fpth = srcdir / "glo2btnu1.f" if fpth.exists(): with open(fpth) as f: lines = f.readlines() @@ -407,9 +410,9 @@ def _update_mfusg_gsi_files(srcdir, fc, cc, arch, double): f.close() # rename "utl7u1 RD.f" to "utl7u1_RD.f" - fpth = pl.Path(srcdir) / "utl7u1 RD.f" + fpth = srcdir / "utl7u1 RD.f" if fpth.exists(): - fpth_rename = pl.Path(srcdir) / "utl7u1_RD.f" + fpth_rename = srcdir / "utl7u1_RD.f" if fpth_rename.exists(): os.remove(fpth_rename) os.rename(fpth, fpth_rename) @@ -638,12 +641,12 @@ def _update_vs2dt_files(srcdir, fc, cc, arch, double): f1 = os.path.join(srcdir, "..", "vs2dt3_3.f") f1 = os.path.abspath(f1) if not os.path.isfile(f1): - raise IOError(f"{f1} does not exist") + raise OSError(f"{f1} does not exist") f2 = os.path.join(srcdir, "vs2dt3_3.f") f2 = os.path.abspath(f2) shutil.move(f1, f2) if not os.path.isfile(f2): - raise IOError(f"{f2} does not exist") + raise OSError(f"{f2} does not exist") f1 = open(os.path.join(srcdir, "vs2dt3_3.f"), "r") f2 = open(os.path.join(srcdir, "vs2dt3_3.f.tmp"), "w") @@ -749,8 +752,8 @@ def _update_mf6_external_dependencies( None """ - if not isinstance(srcdir, pl.Path): - srcdir = pl.Path(srcdir) + if not isinstance(srcdir, Path): + srcdir = Path(srcdir) if target == "libmf6": srcdir = srcdir.parent / "src" parallel_files = ( diff --git a/pymake/utils/download.py b/pymake/utils/download.py index a755be5..ab11ad1 100644 --- a/pymake/utils/download.py +++ b/pymake/utils/download.py @@ -12,12 +12,12 @@ """ import os -import pathlib as pl import shutil import sys import tarfile import timeit from http.client import responses +from pathlib import Path from zipfile import ZIP_DEFLATED, ZipFile, ZipInfo import requests @@ -143,7 +143,7 @@ def compressall( if dir_pths is None: dir_pths = [] else: - if isinstance(dir_pths, (str, pl.Path)): + if isinstance(dir_pths, (str, Path)): dir_pths = [dir_pths] # convert find to a list if a str (a tuple is allowed) @@ -168,7 +168,7 @@ def compressall( tlist.append(file_pth) file_pths = tlist - if append and pl.Path(path).exists(): + if append and Path(path).exists(): mode = "a" else: mode = "w" @@ -575,7 +575,7 @@ def _get_default_url(): """ - return f"https://github.com/{_get_default_repo()}/" + "releases/latest/download/" + return f"https://github.com/{_get_default_repo()}/releases/latest/download/" def _get_default_json(tag_name=None): @@ -584,7 +584,7 @@ def _get_default_json(tag_name=None): Parameters ---------- - tag_name : str + tag_name : str, optional github repository release tag Returns @@ -598,13 +598,9 @@ def _get_default_json(tag_name=None): json_obj = {"tag_name": tag_name} # create appropriate url - if tag_name is not None: - url = ( - f"https://github.com/{_get_default_repo()}/" - + f"releases/latest/download/{tag_name}/" - ) - else: - url = f"https://github.com/{_get_default_repo()}/" + "releases/latest/download/" + url = f"https://github.com/{_get_default_repo()}/releases/latest/download/" + if tag_name: + url += f"{tag_name}/" # define asset names and paths for assets names = ["mac.zip", "linux.zip", "win32.zip", "win64.zip"] @@ -704,7 +700,7 @@ def _repo_json( request_url = release["url"] break if request_url is None: - msg = f"Could not find tag_name ('{tag_name}') " + "in release catalog" + msg = f"Could not find tag_name ('{tag_name}') in release catalog" if error_return: print(msg) return None @@ -955,8 +951,7 @@ def getmfnightly( # Determine path for file download and then download and unzip # https://github.com/MODFLOW-USGS/modflow6-nightly-build/releases/latest/download/ download_url = ( - "https://github.com/MODFLOW-USGS/" - + "modflow6-nightly-build/releases/latest/download/" + "https://github.com/MODFLOW-USGS/modflow6-nightly-build/releases/latest/download/" + zipname ) download_and_unzip( diff --git a/pymake/utils/usgsprograms.py b/pymake/utils/usgsprograms.py index 100e320..b577bcf 100644 --- a/pymake/utils/usgsprograms.py +++ b/pymake/utils/usgsprograms.py @@ -22,8 +22,8 @@ import datetime import json import os -import pathlib as pl import sys +from pathlib import Path from .download import _request_header, zip_all @@ -352,9 +352,8 @@ def export_json( elif current: sel = "the current" print( - f'writing a json file ("{fpth}") ' - + f"of {sel} USGS programs\n" - + f'in the "{program_data_file}" database.\n' + f'writing a json file ("{fpth}") of {sel} USGS programs\n' + f'in the "{program_data_file}" database.\n' ) if prog_data is not None: for idx, key in enumerate(prog_data.keys()): @@ -362,19 +361,19 @@ def export_json( # process the passed file path into appdir and file_name if appdir is None: - appdir = pl.Path(".") - file_name = pl.Path(fpth) + appdir = Path(".") + file_name = Path(fpth) if file_name.parent != str(appdir): appdir = file_name.parent file_name = file_name.name else: for idx, argv in enumerate(sys.argv): if argv in ("--appdir", "-ad"): - appdir = pl.Path(sys.argv[idx + 1]) + appdir = Path(sys.argv[idx + 1]) else: if isinstance(appdir, str): - appdir = pl.Path(appdir) - file_name = pl.Path(fpth).name + appdir = Path(appdir) + file_name = Path(fpth).name if str(appdir) != ".": appdir.mkdir(parents=True, exist_ok=True) @@ -422,7 +421,7 @@ def export_json( if partial_json: # find targets in appdir found_targets = [] - for appdir_file in pl.Path(appdir).iterdir(): + for appdir_file in appdir.iterdir(): temp_target = appdir_file.stem if temp_target.endswith("dbl"): temp_target = temp_target.replace("dbl", "") @@ -441,7 +440,7 @@ def export_json( del prog_data[target] # update double_switch based on executables in appdir - for appdir_file in pl.Path(appdir).iterdir(): + for appdir_file in appdir.iterdir(): temp_target = appdir_file.stem if temp_target.endswith("dbl"): temp_target = temp_target.replace("dbl", "") @@ -454,7 +453,7 @@ def export_json( json.dump(prog_data, file_obj, indent=4, sort_keys=True) except: msg = f'could not export json file "{file_name}"' - raise IOError(msg) + raise OSError(msg) # write code.json if appdir is not the root directory if str(appdir) != ".": @@ -486,16 +485,8 @@ def export_json( # zip code.json if prog_data is not None and zip_path is not None: if verbose: - print( - "Compressing code.json to " - + f"zipfile '{pl.Path(zip_path).resolve()}'" - ) - zip_all( - zip_path, - dir_pths=appdir, - patterns=["code.json"], - append=True, - ) + print(f"Compressing code.json to zipfile '{Path(zip_path).resolve()}'") + zip_all(zip_path, dir_pths=appdir, patterns=["code.json"], append=True) return @@ -559,7 +550,7 @@ def list_json(fpth="code.json"): print(f" {kkey}: {vvalue}") else: msg = f'could not load json file "{fpth}".' - raise IOError(msg) + raise OSError(msg) # print continuation line print("\n") diff --git a/pyproject.toml b/pyproject.toml index fb2b817..7f0a162 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -94,7 +94,14 @@ fallback_version = "999" line-length = 88 [tool.ruff.lint] -select = ["F", "E", "I001"] +select = [ + "D409", # pydocstyle - section-underline-matches-section-length + "E", # pycodestyle error + "F", # Pyflakes + "I001", # isort - unsorted-imports + "RUF", # Ruff-specific rules + "UP", # Pyupgrade +] ignore = [ "E402", # module level import not at top of file "E712", # Avoid equality comparisons to `True` @@ -106,4 +113,7 @@ ignore = [ "F811", # Redefinition of unused variable "F821", # undefined name TODO FIXME "F841", # local variable assigned but never used + "RUF005", # collection literal concatenation + "RUF022", # Apply an isort-style sorting to `__all__` + "UP015", # redundant open modes ] From 776f634932bbd680883a5839f83da2f846999a06 Mon Sep 17 00:00:00 2001 From: Mike Taves Date: Thu, 28 Nov 2024 11:39:04 +1300 Subject: [PATCH 2/3] resolve codacy issue - don't hide exception --- pymake/utils/usgsprograms.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/pymake/utils/usgsprograms.py b/pymake/utils/usgsprograms.py index b577bcf..3aacc80 100644 --- a/pymake/utils/usgsprograms.py +++ b/pymake/utils/usgsprograms.py @@ -448,12 +448,8 @@ def export_json( prog_data[temp_target]["double_switch"] = True # write code.json to root directory - used by executables CI - try: - with open(file_name, "w") as file_obj: - json.dump(prog_data, file_obj, indent=4, sort_keys=True) - except: - msg = f'could not export json file "{file_name}"' - raise OSError(msg) + with open(file_name, "w") as file_obj: + json.dump(prog_data, file_obj, indent=4, sort_keys=True) # write code.json if appdir is not the root directory if str(appdir) != ".": From b46c77d2ee1553bfa7bbd1006482c969eeaf245e Mon Sep 17 00:00:00 2001 From: Mike Taves Date: Thu, 28 Nov 2024 11:46:38 +1300 Subject: [PATCH 3/3] Remove Ruff ignores that have no effect --- pyproject.toml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7f0a162..816ba64 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -103,15 +103,9 @@ select = [ "UP", # Pyupgrade ] ignore = [ - "E402", # module level import not at top of file - "E712", # Avoid equality comparisons to `True` "E722", # do not use bare `except` "E741", # ambiguous variable name "F401", # unused import - "F403", # unable to detect undefined names (star imports) - "F524", # `.format` missing argument(s) for placeholder(s) - "F811", # Redefinition of unused variable - "F821", # undefined name TODO FIXME "F841", # local variable assigned but never used "RUF005", # collection literal concatenation "RUF022", # Apply an isort-style sorting to `__all__`