Skip to content

Commit

Permalink
Using option command line argument instead of defining separate input…
Browse files Browse the repository at this point in the history
… option
  • Loading branch information
sol1105 committed Jul 12, 2024
1 parent a389e6a commit d5993f7
Show file tree
Hide file tree
Showing 5 changed files with 21 additions and 70 deletions.
70 changes: 14 additions & 56 deletions cchecker.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,48 +22,28 @@ def _print_checker_name_header(checker_str):

def parse_options(opts):
"""
Helper function to parse possible options. Splits option after the first
colon to split into key/value pairs.
Helper function to parse possible options. Splits option into key/value
pairs and optionally a value for the checker option. The separator
is a colon.
:param opts: Iterable of strings with options
:rtype: dict
:return: Dictionary with keys as checker type (i.e. "cf", "acdd")
:return: Dictionary with keys as checker type (i.e. "cf", "acdd").
Each value is a dictionary where keys are checker options and values
are checker option values or None if not provided.
"""
options_dict = defaultdict(set)
options_dict = defaultdict(dict)
for opt_str in opts:
try:
checker_type, checker_opt = opt_str.split(":", 1)
checker_type, checker_opt, *checker_val = opt_str.split(":", 2)
checker_val = checker_val[0] if checker_val else None
except ValueError:
warnings.warn(f"Could not split option {opt_str}, ignoring", stacklevel=2)
else:
options_dict[checker_type].add(checker_opt)
options_dict[checker_type][checker_opt] = checker_val
return options_dict


def parse_inputs(inputs):
"""
Helper function to parse possible inputs. Splits input after the first
and second colon to split into checker/key/value triplets.
:param inputs: Iterable of strings with inputs
:rtype: dict
:return: Dictionary of dictionaries, with first level keys as checker
type (i.e. "cf", "acdd") and second level keys as options.
"""
inputs_dict = {}
for input_str in inputs:
try:
checker_type, checker_opt, checker_val = input_str.split(":", 2)
except ValueError:
warnings.warn(f"Could not split input {input_str}, ignoring", stacklevel=2)
else:
try:
inputs_dict[checker_type].update({checker_opt: checker_val})
except KeyError:
inputs_dict[checker_type] = {checker_opt: checker_val}
return inputs_dict


def main():
# Load all available checker classes
check_suite = CheckSuite()
Expand Down Expand Up @@ -198,8 +178,9 @@ def main():
checkers. Multiple options can be specified
via multiple invocations of this switch.
Options should be prefixed with a the
checker name followed by the option, e.g.
'<checker>:<option_name>'
checker name followed by the option,
potentially followed by a value, e.g.
'<checker>:<option_name>[:<option_value>]'
Available options:
'cf:enable_appendix_a_checks' - Allow check
Expand All @@ -209,26 +190,6 @@ def main():
),
)

parser.add_argument(
"-I",
"--input",
default=[],
action="append",
help=dedent(
"""
Additional input options to be passed to the
checkers. Multiple input options can be specified
via multiple invocations of this switch.
Input options should be prefixed with a the
checker name followed by the input name and the value , e.g.
'<checker>:<input_name>:<input_value>'
For now this switch exists to support more complex command line options
for plugins.
""",
),
)

parser.add_argument(
"-V",
"--version",
Expand Down Expand Up @@ -279,8 +240,7 @@ def main():
print(f"IOOS compliance checker version {__version__}")
sys.exit(0)

options_dict = parse_options(args.option) if args.option else defaultdict(set)
inputs_dict = parse_inputs(args.input) if args.input else {}
options_dict = parse_options(args.option) if args.option else defaultdict(dict)

if args.describe_checks:
error_stat = 0
Expand Down Expand Up @@ -351,7 +311,6 @@ def main():
args.output[0],
args.format or ["text"],
options=options_dict,
inputs=inputs_dict,
)
return_values.append(return_value)
had_errors.append(errors)
Expand All @@ -372,7 +331,6 @@ def main():
output,
args.format or ["text"],
options=options_dict,
inputs=inputs_dict,
)
return_values.append(return_value)
had_errors.append(errors)
Expand Down
8 changes: 2 additions & 6 deletions compliance_checker/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,16 +154,12 @@ def setup(self, ds):
Automatically run when running a CheckSuite. Define this method in your Checker class.
"""

def __init__(self, options=None, inputs=None):
def __init__(self, options=None):
self._defined_results = defaultdict(lambda: defaultdict(dict))
if options is None:
self.options = set()
self.options = {}
else:
self.options = options
if inputs is None:
self.inputs = {}
else:
self.inputs = inputs

def get_test_ctx(self, severity, name, variable=None):
"""
Expand Down
3 changes: 1 addition & 2 deletions compliance_checker/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ def run_checker(
output_filename="-",
output_format="text",
options=None,
inputs=None,
):
"""
Static check runner.
Expand All @@ -59,7 +58,7 @@ def run_checker(
@returns If the tests failed (based on the criteria)
"""
all_groups = []
cs = CheckSuite(options=options or {}, inputs=inputs or {})
cs = CheckSuite(options=options or {})
# using OrderedDict is important here to preserve the order
# of multiple datasets which may be passed in
score_dict = OrderedDict()
Expand Down
8 changes: 3 additions & 5 deletions compliance_checker/suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,9 @@ class CheckSuite:
) # Base dict of checker names to BaseCheck derived types, override this in your CheckSuite implementation
templates_root = "compliance_checker" # modify to load alternative Jinja2 templates

def __init__(self, options=None, inputs=None):
def __init__(self, options=None):
self.col_width = 40
self.options = options or {}
self.inputs = inputs or {}

@classmethod
def _get_generator_plugins(cls):
Expand Down Expand Up @@ -402,12 +401,11 @@ def run_all(self, ds, checker_names, include_checks=None, skip_checks=None):
# use some kind of checker object with checker type and
# version baked in
checker_type_name = checker_name.split(":")[0]
checker_opts = self.options.get(checker_type_name, set())
checker_inpts = self.inputs.get(checker_type_name, {})
checker_opts = self.options.get(checker_type_name, {})

# instantiate a Checker object
try:
checker = checker_class(options=checker_opts, inputs=checker_inpts)
checker = checker_class(options=checker_opts)
# hacky fix for no options in constructor
except TypeError:
checker = checker_class()
Expand Down
2 changes: 1 addition & 1 deletion compliance_checker/tests/test_cf.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ def test_appendix_a(self):
dataset = self.load_dataset(STATIC_FILES["bad_data_type"])
# Ordinarily, options would be specified in the checker constructor, but
# we set them manually here so we don't have to monkey patch `setUp`
self.cf.options = {"enable_appendix_a_checks"}
self.cf.options = {"enable_appendix_a_checks": None}
new_check = copy.deepcopy(self.cf)
self.cf.setup(dataset)
aa_results = self.cf.check_appendix_a(dataset)
Expand Down

0 comments on commit d5993f7

Please sign in to comment.