From 67ddfdd95dac7724b44370b7b8a5ffe086ed7d17 Mon Sep 17 00:00:00 2001 From: "Felipe N. Schuch" Date: Fri, 8 Dec 2023 14:56:57 -0300 Subject: [PATCH] fix(cli): BooleanList now avoids name conflicts --- jobbergate-cli/CHANGELOG.md | 1 + .../subapps/applications/questions.py | 27 +++++++------------ .../subapps/applications/test_questions.py | 21 +++++++++++++++ 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/jobbergate-cli/CHANGELOG.md b/jobbergate-cli/CHANGELOG.md index 9670fbef..b77163b7 100644 --- a/jobbergate-cli/CHANGELOG.md +++ b/jobbergate-cli/CHANGELOG.md @@ -5,6 +5,7 @@ This file keeps track of all notable changes to jobbergate-cli ## Unreleased - Fixed the setting `CACHE_DIR` to expand the user home directory, allowing more flexibility on the path [ASP-4053] +- Fixed the question `BooleanList` to allow subquestion to have the same name [ASP-4228] ## 4.2.0a3 -- 2023-11-30 diff --git a/jobbergate-cli/jobbergate_cli/subapps/applications/questions.py b/jobbergate-cli/jobbergate_cli/subapps/applications/questions.py index 039e45d4..2a7e2933 100644 --- a/jobbergate-cli/jobbergate_cli/subapps/applications/questions.py +++ b/jobbergate-cli/jobbergate_cli/subapps/applications/questions.py @@ -12,6 +12,7 @@ from copy import deepcopy from functools import partial +from itertools import chain from typing import Any, Callable, Dict, Optional, Type, TypeVar, cast import inquirer @@ -243,8 +244,8 @@ def __init__( :param whentrue: List of questions to show if user answers 'false' on this question """ super().__init__(variablename, message, **kwargs) - self.whentrue_child_map = {c.variablename: c for c in (whentrue if whentrue is not None else [])} - self.whenfalse_child_map = {c.variablename: c for c in (whenfalse if whenfalse is not None else [])} + self.whentrue_child = whentrue or list() + self.whenfalse_child = whenfalse or list() def ignore_child(self, child: QuestionBase, answers: Dict[str, Any]) -> bool: """ @@ -258,20 +259,11 @@ def ignore_child(self, child: QuestionBase, answers: Dict[str, Any]) -> bool: my_answer is not None, "Questions were asked out of order. Please check your Application for consistency", ) - if my_answer is True: - if child.variablename in self.whentrue_child_map: - return False - elif child.variablename in self.whenfalse_child_map: - return True - else: - return False # This child wasn't registered. This should not happen. But, don't ignore to be safe. - else: - if child.variablename in self.whentrue_child_map: - return True - elif child.variablename in self.whenfalse_child_map: - return False - else: - return False # This child wasn't registered. This should not happen. But, don't ignore to be safe. + if (my_answer is True and child in self.whenfalse_child) or ( + my_answer is False and child in self.whentrue_child + ): + return True + return False def make_ignore_partial(self, child: QuestionBase) -> Callable[[Dict[str, Any]], bool]: """ @@ -289,8 +281,7 @@ def make_prompts(self, **override_kwargs): """ retval = super().make_prompts(**override_kwargs) - all_children = [*self.whentrue_child_map.values(), *self.whenfalse_child_map.values()] - for child in all_children: + for child in chain(self.whentrue_child, self.whenfalse_child): retval.extend(child.make_prompts(ignore=self.make_ignore_partial(child))) return retval diff --git a/jobbergate-cli/tests/subapps/applications/test_questions.py b/jobbergate-cli/tests/subapps/applications/test_questions.py index 543931f4..fb733ada 100644 --- a/jobbergate-cli/tests/subapps/applications/test_questions.py +++ b/jobbergate-cli/tests/subapps/applications/test_questions.py @@ -210,6 +210,27 @@ def test_BooleanList__success(dummy_render_class, mocker): ) +@pytest.mark.parametrize("parent_answer", [True, False]) +def test_BooleanList__same_variable_name(dummy_render_class, parent_answer): + """Assert that BooleanList works when multiple children have the same variable name.""" + variablename = "child" + question_a = Text(variablename, message="Question A") + question_b = Text(variablename, message="Question B") + + question = BooleanList("parent", message="Parent", whentrue=[question_a], whenfalse=[question_b], default=False) + prompts = question.make_prompts() + + expected_answers = {"parent": parent_answer, variablename: "any-answer"} + dummy_render_class.prepared_input = expected_answers + + actual_answers = prompt(prompts, answers=expected_answers, render=dummy_render_class()) + assert actual_answers == expected_answers + + expected_ignored_questions = [False, not parent_answer, parent_answer] + actual_ignored_questions = [q.ignore for q in prompts] + assert actual_ignored_questions == expected_ignored_questions + + def test_gather_config_values__basic(dummy_render_class, mocker): variablename1 = "foo" question1 = Text(variablename1, message="gimme the foo!")