From 760b66d9d9fe180980e906eb3ecbfc52ca8d4311 Mon Sep 17 00:00:00 2001 From: phiwuu Date: Mon, 13 Jan 2025 04:13:09 +0100 Subject: [PATCH] Split `LOBSTER_Tool.process_commandline_options` The function `LOBSTER_Tool.process_commandline_options` does two things. It processes - the common command line options - and the tool specific command line options. The code has been refactored such that there are dedicated functions for each of these two steps. This increases readability of the code. Type hints have been added to some functions. The implementation of the method `LOBSTER_Per_File_Tool.process` has been replaced by a single `pass`, because it is abstract. --- lobster/tool.py | 36 ++++++++++++++++++++++++++---------- lobster/tools/json/json.py | 17 ++++++++++++----- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/lobster/tool.py b/lobster/tool.py index 435f3811..665e6be1 100644 --- a/lobster/tool.py +++ b/lobster/tool.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # LOBSTER - Lightweight Open BMW Software Traceability Evidence Report -# Copyright (C) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (C) 2023, 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -24,6 +24,7 @@ from abc import ABCMeta, abstractmethod from functools import partial +from typing import List, Union, Tuple from lobster.version import FULL_NAME, get_version from lobster.errors import Message_Handler @@ -95,9 +96,15 @@ def __init__(self, name, description, extensions, official): self.add_argument = self.g_tool.add_argument @get_version - def process_commandline_options(self): + def process_commandline_options(self) -> Tuple[argparse.Namespace, List[Tuple[File_Reference, str]]]: + """Processes all command line options""" options = self.ap.parse_args() + work_list = self.process_common_options(options) + self.process_tool_options(options, work_list) + return options, work_list + def process_common_options(self, options: argparse.Namespace) -> List[Tuple[File_Reference, str]]: + """Generates the exact list of files to work on later. The list is sorted alphabetically.""" # Sanity check output if options.out and \ os.path.exists(options.out) and \ @@ -137,13 +144,16 @@ def process_commandline_options(self): elif os.path.isdir(item): for path, dirs, files in os.walk(item): - for n, dir_name in reversed(list(enumerate(dirs))): + for n, dir_name in reversed(list(enumerate(dirs))): # TODO: why in reverse order? keep = True + # TODO: "exclude_pat" is always empty, and "traverse-bazel-dirs" is always ignored! for pattern in self.exclude_pat: if pattern.match(dir_name): keep = False break if not keep: + # TODO: can be simplified, "keep" is not necessary! + # TODO: But unit test should be written first, just to make sure! del dirs[n] for file_name in files: @@ -161,11 +171,9 @@ def process_commandline_options(self): work_list.sort() - self.process_tool_options(options, work_list) - - return options, work_list + return work_list - def write_output(self, ok, options, items): + def write_output(self, ok: bool, options: argparse.Namespace, items: List[Union[Activity, Implementation, Requirement]]): assert isinstance(ok, bool) assert isinstance(options, argparse.Namespace) assert isinstance(items, list) @@ -190,7 +198,11 @@ def write_output(self, ok, options, items): return 1 @abstractmethod - def process_tool_options(self, options, work_list): + def process_tool_options( + self, + options: argparse.Namespace, + work_list: List[Tuple[File_Reference, str]], + ): assert isinstance(options, argparse.Namespace) assert isinstance(work_list, list) @@ -211,8 +223,12 @@ def __init__(self, name, description, extensions, official=False): @classmethod @abstractmethod - def process(cls, options, file_name): - return True, [] + def process( + cls, + options, + file_name, + ) -> Tuple[bool, List[Union[Activity, Implementation, Requirement]]]: + pass def execute(self): options, work_list = self.process_commandline_options() diff --git a/lobster/tools/json/json.py b/lobster/tools/json/json.py index eea67e99..4b80458f 100755 --- a/lobster/tools/json/json.py +++ b/lobster/tools/json/json.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # # lobster_json - Extract JSON tags for LOBSTER -# Copyright (C) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (C) 2023-2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -16,11 +16,12 @@ # You should have received a copy of the GNU Affero General Public # License along with this program. If not, see # . - +import argparse import sys import json from pathlib import PurePath from pprint import pprint +from typing import Tuple, List from lobster.tool import LOBSTER_Per_File_Tool from lobster.items import Tracing_Tag, Activity @@ -98,12 +99,16 @@ def __init__(self): help = ("Member name indicator for " " justifications.")) - def process_tool_options(self, options, work_list): + def process_tool_options( + self, + options: argparse.Namespace, + work_list: List[Tuple[File_Reference, str]], + ): + super().process_tool_options(options, work_list) self.schema = Activity - return True @classmethod - def process(cls, options, file_name): + def process(cls, options, file_name) -> Tuple[bool, List[Activity]]: try: with open(file_name, "r", encoding="UTF-8") as fd: data = json.load(fd) @@ -158,6 +163,8 @@ def process(cls, options, file_name): required = False) else: item_just = [] + + # TODO: this can be moved into a function, as it is duplicated code if isinstance(item_just, list): pass elif isinstance(item_just, str):