Skip to content

Commit

Permalink
cmd-config-for-lobster-tool
Browse files Browse the repository at this point in the history
  • Loading branch information
SurajBDeore committed Feb 12, 2025
1 parent 6ac041a commit e2a1cd9
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 33 deletions.
59 changes: 26 additions & 33 deletions lobster/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import os
import sys
import argparse
import yaml
import multiprocessing

from abc import ABCMeta, abstractmethod
Expand Down Expand Up @@ -62,45 +63,43 @@ def __init__(self, name, description, extensions, official):
allow_abbrev = False)

self.g_common = self.ap.add_argument_group("common options")
self.g_debug = self.ap.add_argument_group("debug options")
self.g_tool = self.ap.add_argument_group("tool specific options")

self.g_common.add_argument(
"--out",
default = None,
help = "Write output to given file instead of stdout.")

self.g_common.add_argument(
"--inputs-from-file",
metavar = "FILE",
default = None,
help = ("Read input files or directories from this file."
" Each non-empty line is interpreted as one input."
" Supports comments starting with #."))
"--config",
required=True,
help="Path to the YAML configuration file."
)

self.g_common.add_argument(
"inputs",
"--out",
default = None,
nargs = "*",
metavar = "FILE_OR_DIR",
help = ("List of files to process or directories to search"
" for relevant input files."))

self.g_common.add_argument(
"--traverse-bazel-dirs",
default = False,
action = "store_true",
help = ("Enter bazel-* directories, which are"
" excluded by default."))
help = "Write output to given file instead of stdout.")

self.add_argument = self.g_tool.add_argument

def load_yaml_config(self, config_path):
"""Loads configuration from a YAML file."""
if not os.path.isfile(config_path):
sys.exit(f"Error: Config file '{config_path}' not found.")
with open(config_path, "r", encoding="UTF-8") as f:
return yaml.safe_load(f) or {}

@get_version
def process_commandline_options(
self,
) -> Tuple[argparse.Namespace, List[Tuple[File_Reference, str]]]:
"""Processes all command line options"""

options = self.ap.parse_args()
config = self.load_yaml_config(options.config)

options.out = config.get("out")
options.inputs_from_file = config.get("inputs_from_file")
options.inputs = config.get("inputs", [])
options.traverse_bazel_dirs = config.get("traverse_bazel_dirs", False)
options.single = config.get("single", False)

work_list = self.process_common_options(options)
self.process_tool_options(options, work_list)
return options, work_list
Expand All @@ -121,7 +120,7 @@ def process_common_options(
# Assemble input requests
inputs = []
if options.inputs:
inputs += [(File_Reference("<cmdline>"), item)
inputs += [(File_Reference("<config>"), item)
for item in options.inputs]
if options.inputs_from_file:
if not os.path.isfile(options.inputs_from_file):
Expand All @@ -135,7 +134,7 @@ def process_common_options(
line_no),
line))
if not options.inputs and not options.inputs_from_file:
inputs.append((File_Reference("<cmdline>"), "."))
inputs.append((File_Reference("<config>"), "."))

# Sanity check and search directories
work_list = []
Expand Down Expand Up @@ -223,12 +222,6 @@ class LOBSTER_Per_File_Tool(LOBSTER_Tool):
def __init__(self, name, description, extensions, official=False):
super().__init__(name, description, extensions, official)

self.g_debug.add_argument(
"--single",
default = False,
action = "store_true",
help = "Avoid use of multiprocessing.")

@classmethod
@abstractmethod
def process(
Expand All @@ -255,4 +248,4 @@ def execute(self):
ok &= new_ok
items += new_items

return self.write_output(ok, options, items)
return self.write_output(ok, options, items)
69 changes: 69 additions & 0 deletions tests-unit/test_tool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import unittest
import os
import argparse
import yaml
from tempfile import NamedTemporaryFile, TemporaryDirectory
from lobster.errors import Message_Handler

from lobster.tool import LOBSTER_Tool

class ConcreteLOSBER_Tool(LOBSTER_Tool):
def __init__(self, name, description, extensions, official=False):
super().__init__(name, description, extensions, official)

def process_tool_options(self, options, work_list):
return work_list

def execute(self):
return 0

class TestLOBSTER_Tool(unittest.TestCase):

def setUp(self):
self.tool = ConcreteLOSBER_Tool("test", "Test description", ["lobster"], True)

def test_init(self):
self.assertEqual(self.tool.name, "lobster-test")
self.assertEqual(self.tool.description, "Test description")
self.assertEqual(self.tool.extensions, [".lobster"])
self.assertIsInstance(self.tool.mh, Message_Handler)

def test_load_yaml_config_valid_file(self):
with NamedTemporaryFile("w", delete=False) as temp:
yaml.dump({"key": "value"}, temp)
temp_path = temp.name

try:
config = self.tool.load_yaml_config(temp_path)
self.assertEqual(config, {"key": "value"})
finally:
os.remove(temp_path)

def test_load_yaml_config_missing_file(self):
with self.assertRaises(SystemExit) as context:
self.tool.load_yaml_config("non_existent.yaml")
self.assertEqual(str(context.exception), "Error: Config file 'non_existent.yaml' not found.")

def test_process_common_options_output_exits_not_file(self):
with TemporaryDirectory() as temp_dir:
options = argparse.Namespace(out=temp_dir, inputs=[], inputs_from_file=None)
with self.assertRaises(SystemExit):
self.tool.process_common_options(options)

def test_process_common_options_valid_inputs(self):
with TemporaryDirectory() as temp_dir:
with NamedTemporaryFile("w", delete=False) as temp:
temp_path = temp.name

options = argparse.Namespace(out=None, inputs=[temp_path], inputs_from_file=None)
work_list = self.tool.process_common_options(options)
print(work_list)
self.assertEqual(work_list, [temp_path])

def test_process_common_options_invalid_inputs(self):
options = argparse.Namespace(out=None, inputs=["invalid.txt"], inputs_from_file=None)
with self.assertRaises(SystemExit):
self.tool.process_common_options(options)

if __name__ == "__main__":
unittest.main()

0 comments on commit e2a1cd9

Please sign in to comment.