diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 8d24e28c5..0013fcd82 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -58,7 +58,7 @@ from yunohost.utils.config import ( ask_questions_and_parse_answers, Question, DomainQuestion, - PathQuestion + PathQuestion, ) from yunohost.utils.i18n import _value_for_locale from yunohost.utils.error import YunohostError, YunohostValidationError @@ -903,7 +903,11 @@ def app_install( # Retrieve arguments list for install script raw_questions = manifest.get("arguments", {}).get("install", {}) questions = ask_questions_and_parse_answers(raw_questions, prefilled_answers=args) - args = {question.name: question.value for question in questions if question.value is not None} + args = { + question.name: question.value + for question in questions + if question.value is not None + } # Validate domain / path availability for webapps path_requirement = _guess_webapp_path_requirement(questions, extracted_app_folder) @@ -1642,13 +1646,15 @@ def app_action_run(operation_logger, app, action, args=None): # Retrieve arguments list for install script raw_questions = actions[action].get("arguments", {}) questions = ask_questions_and_parse_answers(raw_questions, prefilled_answers=args) - args = {question.name: question.value for question in questions if question.value is not None} + args = { + question.name: question.value + for question in questions + if question.value is not None + } tmp_workdir_for_app = _make_tmp_workdir_for_app(app=app) - env_dict = _make_environment_for_app_script( - app, args=args, args_prefix="ACTION_" - ) + env_dict = _make_environment_for_app_script(app, args=args, args_prefix="ACTION_") env_dict["YNH_ACTION"] = action env_dict["YNH_APP_BASEDIR"] = tmp_workdir_for_app @@ -2404,15 +2410,15 @@ def _guess_webapp_path_requirement(questions: List[Question], app_folder: str) - if re.search( r"\npath(_url)?=[\"']?/[\"']?", install_script_content - ) and re.search( - r"ynh_webpath_register", install_script_content - ): + ) and re.search(r"ynh_webpath_register", install_script_content): return "full_domain" return "?" -def _validate_webpath_requirement(questions: List[Question], path_requirement: str) -> None: +def _validate_webpath_requirement( + questions: List[Question], path_requirement: str +) -> None: domain_questions = [question for question in questions if question.type == "domain"] path_questions = [question for question in questions if question.type == "path"] diff --git a/src/yunohost/tests/test_app_config.py b/src/yunohost/tests/test_app_config.py index 248f035f5..0eb813672 100644 --- a/src/yunohost/tests/test_app_config.py +++ b/src/yunohost/tests/test_app_config.py @@ -148,9 +148,9 @@ def test_app_config_regular_setting(config_app): assert app_config_get(config_app, "main.components.boolean") == "1" assert app_setting(config_app, "boolean") == "1" - with pytest.raises(YunohostValidationError), \ - patch.object(os, "isatty", return_value=False), \ - patch.object(Moulinette, "prompt", return_value="pwet"): + with pytest.raises(YunohostValidationError), patch.object( + os, "isatty", return_value=False + ), patch.object(Moulinette, "prompt", return_value="pwet"): app_config_set(config_app, "main.components.boolean", "pwet") diff --git a/src/yunohost/tests/test_questions.py b/src/yunohost/tests/test_questions.py index 99b5339ca..cf4e67733 100644 --- a/src/yunohost/tests/test_questions.py +++ b/src/yunohost/tests/test_questions.py @@ -14,7 +14,7 @@ from yunohost.utils.config import ( DomainQuestion, PathQuestion, BooleanQuestion, - FileQuestion + FileQuestion, ) from yunohost.utils.error import YunohostError, YunohostValidationError @@ -94,7 +94,6 @@ def test_question_string_default_type(): assert out.value == "some_value" - def test_question_string_no_input(): questions = [ { @@ -486,12 +485,7 @@ def test_question_password_no_input_optional(): assert out.value == "" questions = [ - { - "name": "some_password", - "type": "password", - "optional": True, - "default": "" - } + {"name": "some_password", "type": "password", "optional": True, "default": ""} ] with patch.object(os, "isatty", return_value=False): @@ -1725,6 +1719,7 @@ def test_question_number_input(): assert out.type == "number" assert out.value == 0 + def test_question_number_input_no_ask(): questions = [ { @@ -1933,13 +1928,7 @@ def test_question_number_input_test_ask_with_help(): def test_question_display_text(): - questions = [ - { - "name": "some_app", - "type": "display_text", - "ask": "foobar" - } - ] + questions = [{"name": "some_app", "type": "display_text", "ask": "foobar"}] answers = {} with patch.object(sys, "stdout", new_callable=StringIO) as stdout, patch.object( diff --git a/src/yunohost/user.py b/src/yunohost/user.py index 22168d3e7..7d89af443 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -420,7 +420,9 @@ def user_update( # without a specified value, change_password will be set to the const 0. # In this case we prompt for the new password. if Moulinette.interface.type == "cli" and not change_password: - change_password = Moulinette.prompt(m18n.n("ask_password"), is_password=True, confirm=True) + change_password = Moulinette.prompt( + m18n.n("ask_password"), is_password=True, confirm=True + ) # Ensure sufficiently complex password assert_password_is_strong_enough("user", change_password) diff --git a/src/yunohost/utils/config.py b/src/yunohost/utils/config.py index 78fb52252..27a9e1533 100644 --- a/src/yunohost/utils/config.py +++ b/src/yunohost/utils/config.py @@ -102,7 +102,9 @@ class ConfigPanel: ) # FIXME: semantics, technically here this is not about a prompt... if question_class.hide_user_input_in_prompt: - result[key]["value"] = "**************" # Prevent displaying password in `config get` + result[key][ + "value" + ] = "**************" # Prevent displaying password in `config get` if mode == "full": return self.config @@ -269,9 +271,7 @@ class ConfigPanel: # Now fill the sublevels (+ apply filter_key) i = list(format_description).index(level) - sublevel = ( - list(format_description)[i + 1] if level != "options" else None - ) + sublevel = list(format_description)[i + 1] if level != "options" else None search_key = filter_key[i] if len(filter_key) > i else False for key, value in raw_infos.items(): @@ -385,11 +385,13 @@ class ConfigPanel: # Check and ask unanswered questions questions = ask_questions_and_parse_answers(section["options"], self.args) - self.new_values.update({ - question.name: question.value - for question in questions - if question.value is not None - }) + self.new_values.update( + { + question.name: question.value + for question in questions + if question.value is not None + } + ) self.errors = None @@ -506,7 +508,7 @@ class Question(object): prefill=prefill, is_multiline=(self.type == "text"), autocomplete=self.choices, - help=_value_for_locale(self.help) + help=_value_for_locale(self.help), ) def ask_if_needed(self): @@ -574,12 +576,18 @@ class Question(object): # Prevent displaying a shitload of choices # (e.g. 100+ available users when choosing an app admin...) - choices = list(self.choices.values()) if isinstance(self.choices, dict) else self.choices + choices = ( + list(self.choices.values()) + if isinstance(self.choices, dict) + else self.choices + ) choices_to_display = choices[:20] remaining_choices = len(choices[20:]) if remaining_choices > 0: - choices_to_display += [m18n.n("other_available_options", n=remaining_choices)] + choices_to_display += [ + m18n.n("other_available_options", n=remaining_choices) + ] choices_to_display = " | ".join(choices_to_display) @@ -744,7 +752,7 @@ class PathQuestion(Question): raise YunohostValidationError( "app_argument_invalid", name=option.get("name"), - error="Question is mandatory" + error="Question is mandatory", ) return "/" + value.strip().strip(" /") @@ -794,8 +802,12 @@ class BooleanQuestion(Question): no_answers = BooleanQuestion.no_answers yes_answers = BooleanQuestion.yes_answers - assert str(technical_yes).lower() not in no_answers, f"'yes' value can't be in {no_answers}" - assert str(technical_no).lower() not in yes_answers, f"'no' value can't be in {yes_answers}" + assert ( + str(technical_yes).lower() not in no_answers + ), f"'yes' value can't be in {no_answers}" + assert ( + str(technical_no).lower() not in yes_answers + ), f"'no' value can't be in {yes_answers}" no_answers += [str(technical_no).lower()] yes_answers += [str(technical_yes).lower()] @@ -851,9 +863,9 @@ class DomainQuestion(Question): @staticmethod def normalize(value, option={}): if value.startswith("https://"): - value = value[len("https://"):] + value = value[len("https://") :] elif value.startswith("http://"): - value = value[len("http://"):] + value = value[len("http://") :] # Remove trailing slashes value = value.rstrip("/").lower() @@ -915,7 +927,7 @@ class NumberQuestion(Question): raise YunohostValidationError( "app_argument_invalid", name=option.get("name"), - error=m18n.n("invalid_number") + error=m18n.n("invalid_number"), ) def _prevalidate(self): @@ -1044,7 +1056,9 @@ ARGUMENTS_TYPE_PARSERS = { } -def ask_questions_and_parse_answers(questions: Dict, prefilled_answers: Union[str, Mapping[str, Any]] = {}) -> List[Question]: +def ask_questions_and_parse_answers( + questions: Dict, prefilled_answers: Union[str, Mapping[str, Any]] = {} +) -> List[Question]: """Parse arguments store in either manifest.json or actions.json or from a config panel against the user answers when they are present. @@ -1062,7 +1076,9 @@ def ask_questions_and_parse_answers(questions: Dict, prefilled_answers: Union[st # whereas parse.qs return list of values (which is useful for tags, etc) # For now, let's not migrate this piece of code to parse_qs # Because Aleks believes some bits of the app CI rely on overriding values (e.g. foo=foo&...&foo=bar) - prefilled_answers = dict(urllib.parse.parse_qsl(prefilled_answers or "", keep_blank_values=True)) + prefilled_answers = dict( + urllib.parse.parse_qsl(prefilled_answers or "", keep_blank_values=True) + ) if not prefilled_answers: prefilled_answers = {}