From bf811687d9af1d4da4b56b04fe5abe68ca6fd690 Mon Sep 17 00:00:00 2001 From: Bob Date: Mon, 24 Aug 2026 15:41:46 -0600 Subject: [PATCH 1/5] Extract embedded helper scripts in util/ to share/ramble/scripts/ Signed-off-by: Bob --- lib/ramble/ramble/cmd/style.py | 1 + lib/ramble/ramble/test/util/cleaner.py | 103 +++++++++++++++++ lib/ramble/ramble/util/cleaner.py | 88 ++------------ lib/ramble/ramble/util/file_editor.py | 122 ++------------------ share/ramble/scripts/_ramble_cleaner.py | 79 +++++++++++++ share/ramble/scripts/_ramble_file_editor.py | 109 +++++++++++++++++ 6 files changed, 311 insertions(+), 191 deletions(-) create mode 100644 lib/ramble/ramble/test/util/cleaner.py create mode 100644 share/ramble/scripts/_ramble_cleaner.py create mode 100644 share/ramble/scripts/_ramble_file_editor.py diff --git a/lib/ramble/ramble/cmd/style.py b/lib/ramble/ramble/cmd/style.py index 0cbcddefa6..c6797914ce 100644 --- a/lib/ramble/ramble/cmd/style.py +++ b/lib/ramble/ramble/cmd/style.py @@ -46,6 +46,7 @@ def is_object(f): "bin/**", "lib/ramble/ramble/**", "var/ramble/repos/**", + "share/ramble/scripts/**", "conftest.py", ] diff --git a/lib/ramble/ramble/test/util/cleaner.py b/lib/ramble/ramble/test/util/cleaner.py new file mode 100644 index 0000000000..6a2aeeb654 --- /dev/null +++ b/lib/ramble/ramble/test/util/cleaner.py @@ -0,0 +1,103 @@ +# Copyright 2022-2026 The Ramble Authors +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +import subprocess +import sys + +import pytest + +import ramble.util.cleaner as cleaner +import ramble.util.file_editor as file_editor + + +def test_cleaner_and_editor_script_getters(): + cleaner_content = cleaner.get_cleaner_script() + assert "Ramble Directory Cleaner" in cleaner_content + assert cleaner.get_cleaner_exec_path() == "{workspace_shared}/utilities/_ramble_cleaner.py" + + editor_content = file_editor.get_file_editor_script() + assert "Ramble File Editor" in editor_content + assert ( + file_editor.get_file_editor_exec_path() + == "{workspace_shared}/utilities/_ramble_file_editor.py" + ) + + +@pytest.fixture +def cleaner_script_path(tmpdir): + script_path = tmpdir.join("test_cleaner.py") + script_path.write(cleaner.get_cleaner_script()) + return str(script_path) + + +@pytest.fixture +def run_cleaner(cleaner_script_path): + def _run(*args): + return subprocess.run( + [sys.executable, cleaner_script_path] + list(args), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + check=False, + ) + + return _run + + +def test_cleaner_script_execution_non_recursive(tmpdir, run_cleaner): + # Setup test files + dir_path = tmpdir.mkdir("test_clean_dir") + file_keep = dir_path.join("keep.txt") + file_keep.write("keep me") + file_remove = dir_path.join("remove.tmp") + file_remove.write("delete me") + + res = run_cleaner( + "--directory", + str(dir_path), + "--regex", + r".*\.tmp$", + ) + + assert res.returncode == 0 + assert file_keep.exists() + assert not file_remove.exists() + + +def test_cleaner_script_execution_recursive(tmpdir, run_cleaner): + # Setup test nested structure + dir_path = tmpdir.mkdir("test_recursive_dir") + subdir = dir_path.mkdir("sub") + file_sub_keep = subdir.join("keep.txt") + file_sub_keep.write("keep me") + file_sub_del = subdir.join("delete.log") + file_sub_del.write("delete me") + + res = run_cleaner( + "--directory", + str(dir_path), + "--regex", + r".*\.log$", + "--recurse", + ) + + assert res.returncode == 0 + assert file_sub_keep.exists() + assert not file_sub_del.exists() + + +def test_cleaner_script_invalid_regex(tmpdir, run_cleaner): + dir_path = tmpdir.mkdir("test_invalid_dir") + res = run_cleaner( + "--directory", + str(dir_path), + "--regex", + r"[invalid", + ) + assert res.returncode == 1 + assert "Invalid regex pattern" in res.stderr diff --git a/lib/ramble/ramble/util/cleaner.py b/lib/ramble/ramble/util/cleaner.py index a48672efb4..b0ef0d2a5e 100644 --- a/lib/ramble/ramble/util/cleaner.py +++ b/lib/ramble/ramble/util/cleaner.py @@ -6,6 +6,12 @@ # option. This file may not be copied, modified, or distributed # except according to those terms. +import os + +import ramble.paths + +HELPER_SCRIPT_NAME = "_ramble_cleaner.py" + def get_cleaner_exec_path(): """Returns the path to the cleaner script to use""" @@ -14,82 +20,6 @@ def get_cleaner_exec_path(): def get_cleaner_script(): """Returns the content of the standalone cleaner script""" - - return r"""# Copyright 2022-2026 The Ramble Authors -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. - -import os -import re -import shutil -import sys -import argparse - -def matches_whole_path(regex, path): - return regex.fullmatch(path) is not None - -def perform_cleanup(args): - directory = args.directory - pattern = args.regex - recurse = args.recurse - - if not os.path.isdir(directory): - print(f"Directory {directory} does not exist. Skipping cleanup.", file=sys.stderr) - return 0 - - try: - regex = re.compile(pattern) - except re.error as e: - print(f"Invalid regex pattern '{pattern}': {e}", file=sys.stderr) - return 1 - - paths_to_delete = [] - if recurse: - for root, dirs, files in os.walk(directory, topdown=True): - for d in list(dirs): - d_path = os.path.join(root, d) - if matches_whole_path(regex, d_path): - paths_to_delete.append(d_path) - dirs.remove(d) - for f in files: - f_path = os.path.join(root, f) - if matches_whole_path(regex, f_path): - paths_to_delete.append(f_path) - else: - try: - for entry in os.scandir(directory): - if matches_whole_path(regex, entry.path): - paths_to_delete.append(entry.path) - except Exception as e: - print(f"Error scanning directory {directory}: {e}", file=sys.stderr) - return 1 - - for path in paths_to_delete: - try: - if os.path.islink(path): - os.remove(path) - elif os.path.isdir(path): - shutil.rmtree(path) - else: - os.remove(path) - except Exception as e: - print(f"Error removing {path}: {e}", file=sys.stderr) - - return 0 - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Ramble Directory Cleaner') - parser.add_argument('--directory', required=True, help='Directory to clean') - parser.add_argument('--regex', required=True, help='Regex pattern to match paths') - parser.add_argument('--recurse', action='store_true', help='Search recursively') - - args = parser.parse_args() - sys.exit(perform_cleanup(args)) -""" - - -HELPER_SCRIPT_NAME = "_ramble_cleaner.py" + script_path = os.path.join(ramble.paths.share_path, "scripts", HELPER_SCRIPT_NAME) + with open(script_path, "r", encoding="utf-8") as f: + return f.read() diff --git a/lib/ramble/ramble/util/file_editor.py b/lib/ramble/ramble/util/file_editor.py index 17719b7bf4..c731ce20f9 100644 --- a/lib/ramble/ramble/util/file_editor.py +++ b/lib/ramble/ramble/util/file_editor.py @@ -6,6 +6,13 @@ # option. This file may not be copied, modified, or distributed # except according to those terms. +import os + +import ramble.paths + +HELPER_SCRIPT_NAME = "_ramble_file_editor.py" +CUSTOM_EDIT_FUNCTIONS_NAME = "custom_edit_functions.py" + def get_file_editor_exec_path(): """Returns the path to the file editor script to use""" @@ -14,115 +21,6 @@ def get_file_editor_exec_path(): def get_file_editor_script(): """Returns the content of the standalone file editor script""" - - return r"""# Copyright 2022-2026 The Ramble Authors -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. - - -import re -import os -import argparse -import sys -import shutil - -def edit_file(args): - if not os.path.exists(args.file): - content = "" - else: - # Use newline='' to preserve original line endings across platforms - with open(args.file, 'r', newline='', encoding='utf-8') as f: - content = f.read() - - def unescape(s): - if s is None: - return None - return s.encode('utf-8').decode('unicode_escape') - - new_content = content - if args.mode == 'regex': - if args.function: - func = None - if args.import_module: - import importlib.util - if not os.path.exists(args.import_module): - print(f"Error: Module file {args.import_module} not found.") - return 1 - spec = importlib.util.spec_from_file_location( - "custom_functions", args.import_module - ) - custom_module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(custom_module) - func = getattr(custom_module, args.function, None) - else: - func = globals().get(args.function) - - if func: - new_content = func(content) - if not isinstance(new_content, str): - print( - f"Error: Function {args.function} must return a string, " - f"not {type(new_content).__name__}" - ) - return 1 - else: - print(f"Error: Function {args.function} not found.") - return 1 - - if args.match and args.replace is not None: - - new_content = re.sub(args.match, unescape(args.replace), new_content) - if args.append: - new_content = new_content + unescape(args.append) - if args.prepend: - new_content = unescape(args.prepend) + new_content - elif args.mode == 'patch': - if not shutil.which('patch'): - print("Error: 'patch' utility not found.") - print("On Windows, please ensure a tool providing 'patch' " - "(e.g., Git Bash, MSYS2, Cygwin) is installed and in your PATH.") - return 1 - - import subprocess - try: - subprocess.run(['patch', args.file, args.patch_file], check=True) - return 0 - except Exception as e: - print(f"Error applying patch: {e}") - return 1 - - needs_write = new_content != content or not os.path.exists(args.file) - if needs_write: - # Ensure the directory exists - os.makedirs(os.path.dirname(os.path.abspath(args.file)), exist_ok=True) - with open(args.file, 'w', newline='', encoding='utf-8') as f: - f.write(new_content) - - return 0 - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Ramble File Editor') - parser.add_argument('--mode', choices=['regex', 'patch'], required=True) - parser.add_argument('--file', required=True) - parser.add_argument('--match', help='Regex pattern to match') - parser.add_argument('--replace', help='Replacement string') - parser.add_argument('--append', help='String to append') - parser.add_argument('--prepend', help='String to prepend') - parser.add_argument('--patch-file', help='Path to patch file') - parser.add_argument('--function', help='Name of custom function to execute') - parser.add_argument( - '--import-module', - help='Path to python module containing custom functions' - ) - - args = parser.parse_args() - sys.exit(edit_file(args)) -""" - - -HELPER_SCRIPT_NAME = "_ramble_file_editor.py" -CUSTOM_EDIT_FUNCTIONS_NAME = "custom_edit_functions.py" + script_path = os.path.join(ramble.paths.share_path, "scripts", HELPER_SCRIPT_NAME) + with open(script_path, "r", encoding="utf-8") as f: + return f.read() diff --git a/share/ramble/scripts/_ramble_cleaner.py b/share/ramble/scripts/_ramble_cleaner.py new file mode 100644 index 0000000000..dc8aae8ad5 --- /dev/null +++ b/share/ramble/scripts/_ramble_cleaner.py @@ -0,0 +1,79 @@ +# Copyright 2022-2026 The Ramble Authors +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +import argparse +import os +import re +import shutil +import sys + + +def matches_whole_path(regex, path): + return regex.fullmatch(path) is not None + + +def perform_cleanup(args): + directory = args.directory + pattern = args.regex + recurse = args.recurse + + if not os.path.isdir(directory): + print(f"Directory {directory} does not exist. Skipping cleanup.", file=sys.stderr) + return 0 + + try: + regex = re.compile(pattern) + except re.error as e: + print(f"Invalid regex pattern '{pattern}': {e}", file=sys.stderr) + return 1 + + paths_to_delete = [] + if recurse: + for root, dirs, files in os.walk(directory, topdown=True): + for d in list(dirs): + d_path = os.path.join(root, d) + if matches_whole_path(regex, d_path): + paths_to_delete.append(d_path) + dirs.remove(d) + for f in files: + f_path = os.path.join(root, f) + if matches_whole_path(regex, f_path): + paths_to_delete.append(f_path) + else: + try: + paths_to_delete.extend( + entry.path + for entry in os.scandir(directory) + if matches_whole_path(regex, entry.path) + ) + except Exception as e: + print(f"Error scanning directory {directory}: {e}", file=sys.stderr) + return 1 + + for path in paths_to_delete: + try: + if os.path.islink(path): + os.remove(path) + elif os.path.isdir(path): + shutil.rmtree(path) + else: + os.remove(path) + except Exception as e: + print(f"Error removing {path}: {e}", file=sys.stderr) + + return 0 + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Ramble Directory Cleaner") + parser.add_argument("--directory", required=True, help="Directory to clean") + parser.add_argument("--regex", required=True, help="Regex pattern to match paths") + parser.add_argument("--recurse", action="store_true", help="Search recursively") + + args = parser.parse_args() + sys.exit(perform_cleanup(args)) diff --git a/share/ramble/scripts/_ramble_file_editor.py b/share/ramble/scripts/_ramble_file_editor.py new file mode 100644 index 0000000000..a889c5ca34 --- /dev/null +++ b/share/ramble/scripts/_ramble_file_editor.py @@ -0,0 +1,109 @@ +# Copyright 2022-2026 The Ramble Authors +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +import argparse +import os +import re +import shutil +import sys + + +def edit_file(args): + if not os.path.exists(args.file): + content = "" + else: + # Use newline='' to preserve original line endings across platforms + with open(args.file, "r", newline="", encoding="utf-8") as f: + content = f.read() + + def unescape(s): + if s is None: + return None + return s.encode("utf-8").decode("unicode_escape") + + new_content = content + if args.mode == "regex": + if args.function: + func = None + if args.import_module: + import importlib.util + + if not os.path.exists(args.import_module): + print(f"Error: Module file {args.import_module} not found.") + return 1 + spec = importlib.util.spec_from_file_location( + "custom_functions", args.import_module + ) + custom_module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(custom_module) + func = getattr(custom_module, args.function, None) + else: + func = globals().get(args.function) + + if func: + new_content = func(content) + if not isinstance(new_content, str): + print( + f"Error: Function {args.function} must return a string, " + f"not {type(new_content).__name__}" + ) + return 1 + else: + print(f"Error: Function {args.function} not found.") + return 1 + + if args.match and args.replace is not None: + new_content = re.sub(args.match, unescape(args.replace), new_content) + if args.append: + new_content = new_content + unescape(args.append) + if args.prepend: + new_content = unescape(args.prepend) + new_content + elif args.mode == "patch": + if not shutil.which("patch"): + print("Error: 'patch' utility not found.") + print( + "On Windows, please ensure a tool providing 'patch' " + "(e.g., Git Bash, MSYS2, Cygwin) is installed and in your PATH." + ) + return 1 + + import subprocess + + try: + subprocess.run(["patch", args.file, args.patch_file], check=True) + return 0 + except Exception as e: + print(f"Error applying patch: {e}") + return 1 + + needs_write = new_content != content or not os.path.exists(args.file) + if needs_write: + # Ensure the directory exists + os.makedirs(os.path.dirname(os.path.abspath(args.file)), exist_ok=True) + with open(args.file, "w", newline="", encoding="utf-8") as f: + f.write(new_content) + + return 0 + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Ramble File Editor") + parser.add_argument("--mode", choices=["regex", "patch"], required=True) + parser.add_argument("--file", required=True) + parser.add_argument("--match", help="Regex pattern to match") + parser.add_argument("--replace", help="Replacement string") + parser.add_argument("--append", help="String to append") + parser.add_argument("--prepend", help="String to prepend") + parser.add_argument("--patch-file", help="Path to patch file") + parser.add_argument("--function", help="Name of custom function to execute") + parser.add_argument( + "--import-module", help="Path to python module containing custom functions" + ) + + args = parser.parse_args() + sys.exit(edit_file(args)) From 5a9ced0370d9c39e1f5640da2ecca698770b3c61 Mon Sep 17 00:00:00 2001 From: Bob Date: Mon, 24 Aug 2026 16:19:54 -0600 Subject: [PATCH 2/5] Use shutil.copyfile for workspace helper script installation Signed-off-by: Bob --- lib/ramble/ramble/util/cleaner.py | 10 +++++++--- lib/ramble/ramble/util/file_editor.py | 10 +++++++--- lib/ramble/ramble/workspace/workspace.py | 16 ++++++---------- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/lib/ramble/ramble/util/cleaner.py b/lib/ramble/ramble/util/cleaner.py index b0ef0d2a5e..690db6a27e 100644 --- a/lib/ramble/ramble/util/cleaner.py +++ b/lib/ramble/ramble/util/cleaner.py @@ -13,13 +13,17 @@ HELPER_SCRIPT_NAME = "_ramble_cleaner.py" +def get_cleaner_source_path(): + """Returns the source path to the cleaner script in Ramble's share directory""" + return os.path.join(ramble.paths.share_path, "scripts", HELPER_SCRIPT_NAME) + + def get_cleaner_exec_path(): - """Returns the path to the cleaner script to use""" + """Returns the path to the cleaner script to use in a workspace""" return f"{{workspace_shared}}/utilities/{HELPER_SCRIPT_NAME}" def get_cleaner_script(): """Returns the content of the standalone cleaner script""" - script_path = os.path.join(ramble.paths.share_path, "scripts", HELPER_SCRIPT_NAME) - with open(script_path, "r", encoding="utf-8") as f: + with open(get_cleaner_source_path(), "r", encoding="utf-8") as f: return f.read() diff --git a/lib/ramble/ramble/util/file_editor.py b/lib/ramble/ramble/util/file_editor.py index c731ce20f9..b9a8d99205 100644 --- a/lib/ramble/ramble/util/file_editor.py +++ b/lib/ramble/ramble/util/file_editor.py @@ -14,13 +14,17 @@ CUSTOM_EDIT_FUNCTIONS_NAME = "custom_edit_functions.py" +def get_file_editor_source_path(): + """Returns the source path to the file editor script in Ramble's share directory""" + return os.path.join(ramble.paths.share_path, "scripts", HELPER_SCRIPT_NAME) + + def get_file_editor_exec_path(): - """Returns the path to the file editor script to use""" + """Returns the path to the file editor script to use in a workspace""" return f"{{workspace_shared}}/utilities/{HELPER_SCRIPT_NAME}" def get_file_editor_script(): """Returns the content of the standalone file editor script""" - script_path = os.path.join(ramble.paths.share_path, "scripts", HELPER_SCRIPT_NAME) - with open(script_path, "r", encoding="utf-8") as f: + with open(get_file_editor_source_path(), "r", encoding="utf-8") as f: return f.read() diff --git a/lib/ramble/ramble/workspace/workspace.py b/lib/ramble/ramble/workspace/workspace.py index 56a764ee9b..555f071e4e 100644 --- a/lib/ramble/ramble/workspace/workspace.py +++ b/lib/ramble/ramble/workspace/workspace.py @@ -823,23 +823,19 @@ def write_utilities(self): if ramble.config.get("config:generate_file_editing_scripts", True): fs.mkdirp(self.shared_utilities_dir) - # Write to base shared utilities directory - base_script_content = ramble.util.file_editor.get_file_editor_script() + # Copy file editor helper script to workspace shared utilities base_script_path = os.path.join( self.shared_utilities_dir, ramble.util.file_editor.HELPER_SCRIPT_NAME ) - with open(base_script_path, "w+", encoding="utf-8") as f: - f.write(base_script_content) + shutil.copyfile( + ramble.util.file_editor.get_file_editor_source_path(), base_script_path + ) - # Write cleaner utility script - # TODO: Currently this is guarded by the generate_file_editing_scripts - # config. Should it use its own config setting? - cleaner_script_content = ramble.util.cleaner.get_cleaner_script() + # Copy cleaner helper script to workspace shared utilities cleaner_script_path = os.path.join( self.shared_utilities_dir, ramble.util.cleaner.HELPER_SCRIPT_NAME ) - with open(cleaner_script_path, "w+", encoding="utf-8") as f: - f.write(cleaner_script_content) + shutil.copyfile(ramble.util.cleaner.get_cleaner_source_path(), cleaner_script_path) def write_auxiliary_software_files(self): """Write all auxiliary software files out to workspace""" From 80a8cee7997387191c706583f99152140de5c160 Mon Sep 17 00:00:00 2001 From: rfbgo <109985755+rfbgo@users.noreply.github.com> Date: Mon, 24 Aug 2026 16:25:11 -0600 Subject: [PATCH 3/5] Apply suggestions from code review Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Bob --- share/ramble/scripts/_ramble_cleaner.py | 11 ++++++----- share/ramble/scripts/_ramble_file_editor.py | 3 +++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/share/ramble/scripts/_ramble_cleaner.py b/share/ramble/scripts/_ramble_cleaner.py index dc8aae8ad5..43448fcdce 100644 --- a/share/ramble/scripts/_ramble_cleaner.py +++ b/share/ramble/scripts/_ramble_cleaner.py @@ -46,11 +46,12 @@ def perform_cleanup(args): paths_to_delete.append(f_path) else: try: - paths_to_delete.extend( - entry.path - for entry in os.scandir(directory) - if matches_whole_path(regex, entry.path) - ) + with os.scandir(directory) as entries: + paths_to_delete.extend( + entry.path + for entry in entries + if matches_whole_path(regex, entry.path) + ) except Exception as e: print(f"Error scanning directory {directory}: {e}", file=sys.stderr) return 1 diff --git a/share/ramble/scripts/_ramble_file_editor.py b/share/ramble/scripts/_ramble_file_editor.py index a889c5ca34..137f474404 100644 --- a/share/ramble/scripts/_ramble_file_editor.py +++ b/share/ramble/scripts/_ramble_file_editor.py @@ -39,6 +39,9 @@ def unescape(s): spec = importlib.util.spec_from_file_location( "custom_functions", args.import_module ) + if spec is None or spec.loader is None: + print(f"Error: Could not load spec for module {args.import_module}.") + return 1 custom_module = importlib.util.module_from_spec(spec) spec.loader.exec_module(custom_module) func = getattr(custom_module, args.function, None) From 7bb599fdd7ac3735963ca7690c91a2b60b4e4f34 Mon Sep 17 00:00:00 2001 From: Bob Date: Mon, 24 Aug 2026 17:02:21 -0600 Subject: [PATCH 4/5] Apply pyupgrade modern syntax recommendations Signed-off-by: Bob --- lib/ramble/ramble/util/cleaner.py | 2 +- lib/ramble/ramble/util/file_editor.py | 2 +- share/ramble/scripts/_ramble_file_editor.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ramble/ramble/util/cleaner.py b/lib/ramble/ramble/util/cleaner.py index 690db6a27e..05bf3162f6 100644 --- a/lib/ramble/ramble/util/cleaner.py +++ b/lib/ramble/ramble/util/cleaner.py @@ -25,5 +25,5 @@ def get_cleaner_exec_path(): def get_cleaner_script(): """Returns the content of the standalone cleaner script""" - with open(get_cleaner_source_path(), "r", encoding="utf-8") as f: + with open(get_cleaner_source_path(), encoding="utf-8") as f: return f.read() diff --git a/lib/ramble/ramble/util/file_editor.py b/lib/ramble/ramble/util/file_editor.py index b9a8d99205..36838f4b5a 100644 --- a/lib/ramble/ramble/util/file_editor.py +++ b/lib/ramble/ramble/util/file_editor.py @@ -26,5 +26,5 @@ def get_file_editor_exec_path(): def get_file_editor_script(): """Returns the content of the standalone file editor script""" - with open(get_file_editor_source_path(), "r", encoding="utf-8") as f: + with open(get_file_editor_source_path(), encoding="utf-8") as f: return f.read() diff --git a/share/ramble/scripts/_ramble_file_editor.py b/share/ramble/scripts/_ramble_file_editor.py index 137f474404..ccd80dfec5 100644 --- a/share/ramble/scripts/_ramble_file_editor.py +++ b/share/ramble/scripts/_ramble_file_editor.py @@ -18,7 +18,7 @@ def edit_file(args): content = "" else: # Use newline='' to preserve original line endings across platforms - with open(args.file, "r", newline="", encoding="utf-8") as f: + with open(args.file, newline="", encoding="utf-8") as f: content = f.read() def unescape(s): From 4632b98dbd8720159cc953a26ccbd09247065526 Mon Sep 17 00:00:00 2001 From: Bob Date: Tue, 25 Aug 2026 13:05:35 -0600 Subject: [PATCH 5/5] apply mypy to share/ramble/scripts/ Signed-off-by: Bob --- lib/ramble/ramble/cmd/style.py | 4 +++- pyproject.toml | 11 +++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/ramble/ramble/cmd/style.py b/lib/ramble/ramble/cmd/style.py index c6797914ce..43a000e3a0 100644 --- a/lib/ramble/ramble/cmd/style.py +++ b/lib/ramble/ramble/cmd/style.py @@ -593,7 +593,9 @@ def run_mypy(mypy_cmd, file_list, args): mypy_args.extend(get_tool_args(args, "mypy")) if file_list: - mypy_files = [f for f in file_list if f.startswith("lib/ramble/ramble/")] + mypy_files = [ + f for f in file_list if f.startswith(("lib/ramble/ramble/", "share/ramble/scripts/")) + ] if not mypy_files: print_tool_result("mypy", 0) return 0 diff --git a/pyproject.toml b/pyproject.toml index 7800914fcc..e48262b9bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -149,14 +149,19 @@ no_site_packages = true # For now, only type-check the util directory files = [ "lib/ramble/ramble/**/*.py", + "share/ramble/scripts/**/*.py", ] ignore_errors = true ignore_missing_imports = true allow_redefinition = true [[tool.mypy.overrides]] -# Only enable ramble modules -module = "ramble.*" +# Only enable ramble modules and share helper scripts +module = [ + "ramble.*", + "_ramble_cleaner", + "_ramble_file_editor", +] ignore_errors = false [[tool.mypy.overrides]] @@ -171,6 +176,8 @@ module = [ "ramble.error", "ramble.paths", "ramble.filters", + "_ramble_cleaner", + "_ramble_file_editor", ] check_untyped_defs = true