diff --git a/news/skip_creation_config.rst b/news/skip_creation_config.rst new file mode 100644 index 00000000..4cde85cf --- /dev/null +++ b/news/skip_creation_config.rst @@ -0,0 +1,23 @@ +**Added:** + +* functionality to run `get_user_info` without execution interruption + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* diff --git a/src/diffpy/utils/tools.py b/src/diffpy/utils/tools.py index 3fc10031..83bfb348 100644 --- a/src/diffpy/utils/tools.py +++ b/src/diffpy/utils/tools.py @@ -1,11 +1,20 @@ import importlib.metadata import json -import os from copy import copy from pathlib import Path +user_info_imsg = ( + "No global configuration file was found containing " + "information about the user to associate with the data.\n" + "By following the prompts below you can add your name and email to this file on the current " + "computer and your name will be automatically associated with subsequent diffpy data by default.\n" + "This is not recommended on a shared or public computer. " + "You will only have to do that once.\n" + "For more information, please refer to www.diffpy.org/diffpy.utils/examples/toolsexample.html." +) -def clean_dict(obj): + +def _clean_dict(obj): """ Remove keys from the dictionary where the corresponding value is None. @@ -27,7 +36,7 @@ def clean_dict(obj): return obj -def stringify(obj): +def _stringify(obj): """ Convert None to an empty string. @@ -44,7 +53,7 @@ def stringify(obj): return obj if obj is not None else "" -def load_config(file_path): +def _load_config(file_path): """ Load configuration from a .json file. @@ -71,67 +80,70 @@ def load_config(file_path): def _sorted_merge(*dicts): merged = {} for d in dicts: + d = _clean_dict(d) merged.update(d) return merged -def _create_global_config(args): +def _get_config_info(user_info): + global_config = _load_config(Path().home() / "diffpyconfig.json") + local_config = _load_config(Path().cwd() / "diffpyconfig.json") + if global_config or local_config: + return _sorted_merge(global_config, local_config, user_info) + return None + + +def _create_global_config(user_info): + print(user_info_imsg) username = input( - f"Please enter the name you would want future work to be credited to " f"[{args.get('username', '')}]: " - ).strip() or args.get("username", "") - email = input(f"Please enter the your email " f"[{args.get('email', '')}]: ").strip() or args.get("email", "") - return_bool = False if username is None or email is None else True - with open(Path().home() / "diffpyconfig.json", "w") as f: - f.write(json.dumps({"username": stringify(username), "email": stringify(email)})) - print( - f"You can manually edit the config file at {Path().home() / 'diffpyconfig.json'} using any text editor.\n" - f"Or you can update the config file by passing new values to get_user_info(), " - f"see examples here: https://www.diffpy.org/diffpy.utils/examples/toolsexample.html" + f"Please enter the name you would want future work to be credited to " + f"[{user_info.get('username', '')}]: " + ).strip() or user_info.get("username", "") + email = input(f"Please enter the your email " f"[{user_info.get('email', '')}]: ").strip() or user_info.get( + "email", "" ) - return return_bool + config = {"username": _stringify(username), "email": _stringify(email)} + if username and email: + with open(Path().home() / "diffpyconfig.json", "w") as f: + f.write(json.dumps(config)) + print( + f"You can manually edit the config file at " + f"{Path().home() / 'diffpyconfig.json'} using any text editor.\n" + f"Or you can update the config file by passing new values to get_user_info(), " + f"see examples here: https://www.diffpy.org/diffpy.utils/examples/toolsexample.html." + ) + return config -def get_user_info(args=None): +def get_user_info(user_info=None, skip_config_creation=False): """ - Get username and email configuration. + Retrieve or create user configuration (username and email). - First attempts to load config file from global and local paths. - If neither exists, creates a global config file. - It prioritizes values from args, then local, then global. - Removes invalid global config file if creation is needed, replacing it with empty username and email. + Workflow: + 1. Loads config from global and local paths, prioritizing user_info, then local, then global. + 2. If no config files are found and skip_config_creation is False, prompts the user for input. + Creates a global config file only if both username and email are valid. + 3. If no config files and skip_config_creation is True, uses user_info (even if empty). Parameters ---------- - args argparse.Namespace - The arguments from the parser, default is None. + user_info dict or None + A dictionary containing the user input, default is None. Returns ------- dict or None: The dictionary containing username and email with corresponding values. - """ - config_bool = True - global_config = load_config(Path().home() / "diffpyconfig.json") - local_config = load_config(Path().cwd() / "diffpyconfig.json") - if global_config is None and local_config is None: - print( - "No global configuration file was found containing " - "information about the user to associate with the data.\n" - "By following the prompts below you can add your name and email to this file on the current " - "computer and your name will be automatically associated with subsequent diffpy data by default.\n" - "This is not recommended on a shared or public computer. " - "You will only have to do that once.\n" - "For more information, please refer to www.diffpy.org/diffpy.utils/examples/toolsexample.html" - ) - config_bool = _create_global_config(args) - global_config = load_config(Path().home() / "diffpyconfig.json") - config = _sorted_merge(clean_dict(global_config), clean_dict(local_config), clean_dict(args)) - if config_bool is False: - os.remove(Path().home() / "diffpyconfig.json") - config = {"username": "", "email": ""} - - return config + config = _get_config_info(user_info) + if config: + return config + if not skip_config_creation: + return _create_global_config(user_info) + return { + "username": _stringify(user_info.get("username", "")), + "email": _stringify(user_info.get("email", "")), + } def get_package_info(package_names, metadata=None): diff --git a/tests/test_tools.py b/tests/test_tools.py index 0a42332f..61fe3080 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -16,109 +16,201 @@ def _setup_dirs(monkeypatch, user_filesystem): return home_dir -def _run_tests(inputs, expected): - args = {"username": inputs[0], "email": inputs[1]} - expected_username, expected_email = expected - config = get_user_info(args) +def _run_tests(cli_inputs, expected): + user_info = {"username": cli_inputs["cli_username"], "email": cli_inputs["cli_email"]} + config = get_user_info(user_info=user_info, skip_config_creation=cli_inputs["skip_config_creation"]) + expected_username = expected["expected_username"] + expected_email = expected["expected_email"] assert config.get("username") == expected_username assert config.get("email") == expected_email params_user_info_with_home_conf_file = [ - (["", ""], ["home_username", "home@email.com"]), - (["cli_username", ""], ["cli_username", "home@email.com"]), - (["", "cli@email.com"], ["home_username", "cli@email.com"]), - ([None, None], ["home_username", "home@email.com"]), - (["cli_username", None], ["cli_username", "home@email.com"]), - ([None, "cli@email.com"], ["home_username", "cli@email.com"]), - (["cli_username", "cli@email.com"], ["cli_username", "cli@email.com"]), + ( + {"cli_username": None, "cli_email": None, "skip_config_creation": False}, + {"expected_username": "home_username", "expected_email": "home@email.com"}, + ), + ( + {"cli_username": "cli_username", "cli_email": None, "skip_config_creation": False}, + {"expected_username": "cli_username", "expected_email": "home@email.com"}, + ), + ( + {"cli_username": None, "cli_email": "cli@email.com", "skip_config_creation": False}, + {"expected_username": "home_username", "expected_email": "cli@email.com"}, + ), + ( + {"cli_username": "cli_username", "cli_email": "cli@email.com", "skip_config_creation": False}, + {"expected_username": "cli_username", "expected_email": "cli@email.com"}, + ), + ( + {"cli_username": None, "cli_email": None, "skip_config_creation": True}, + {"expected_username": "home_username", "expected_email": "home@email.com"}, + ), + ( + {"cli_username": "cli_username", "cli_email": None, "skip_config_creation": True}, + {"expected_username": "cli_username", "expected_email": "home@email.com"}, + ), + ( + {"cli_username": None, "cli_email": "cli@email.com", "skip_config_creation": True}, + {"expected_username": "home_username", "expected_email": "cli@email.com"}, + ), + ( + {"cli_username": "cli_username", "cli_email": "cli@email.com", "skip_config_creation": True}, + {"expected_username": "cli_username", "expected_email": "cli@email.com"}, + ), ] params_user_info_with_local_conf_file = [ - (["", ""], ["cwd_username", "cwd@email.com"]), - (["cli_username", ""], ["cli_username", "cwd@email.com"]), - (["", "cli@email.com"], ["cwd_username", "cli@email.com"]), - ([None, None], ["cwd_username", "cwd@email.com"]), - (["cli_username", None], ["cli_username", "cwd@email.com"]), - ([None, "cli@email.com"], ["cwd_username", "cli@email.com"]), - (["cli_username", "cli@email.com"], ["cli_username", "cli@email.com"]), + ( + {"cli_username": None, "cli_email": None, "skip_config_creation": False}, + {"expected_username": "cwd_username", "expected_email": "cwd@email.com"}, + ), + ( + {"cli_username": "cli_username", "cli_email": None, "skip_config_creation": False}, + {"expected_username": "cli_username", "expected_email": "cwd@email.com"}, + ), + ( + {"cli_username": None, "cli_email": "cli@email.com", "skip_config_creation": False}, + {"expected_username": "cwd_username", "expected_email": "cli@email.com"}, + ), + ( + {"cli_username": "cli_username", "cli_email": "cli@email.com", "skip_config_creation": False}, + {"expected_username": "cli_username", "expected_email": "cli@email.com"}, + ), + ( + {"cli_username": None, "cli_email": None, "skip_config_creation": True}, + {"expected_username": "cwd_username", "expected_email": "cwd@email.com"}, + ), + ( + {"cli_username": "cli_username", "cli_email": None, "skip_config_creation": True}, + {"expected_username": "cli_username", "expected_email": "cwd@email.com"}, + ), + ( + {"cli_username": None, "cli_email": "cli@email.com", "skip_config_creation": True}, + {"expected_username": "cwd_username", "expected_email": "cli@email.com"}, + ), + ( + {"cli_username": "cli_username", "cli_email": "cli@email.com", "skip_config_creation": True}, + {"expected_username": "cli_username", "expected_email": "cli@email.com"}, + ), ] -params_user_info_with_no_home_conf_file = [ +params_user_info_with_no_conf_file = [ + # Case 1: None or only one of username or email is provided, config file should not be created ( - [None, None], - ["input_username", "input@email.com"], - ["input_username", "input@email.com"], + {"cli_username": None, "cli_email": None, "skip_config_creation": False}, + {"input_username": "", "input_email": ""}, + {"expected_username": "", "expected_email": "", "config_file_exists": False}, ), ( - ["cli_username", None], - ["", "input@email.com"], - ["cli_username", "input@email.com"], + {"cli_username": "cli_username", "cli_email": None, "skip_config_creation": False}, + {"input_username": "", "input_email": ""}, + {"expected_username": "cli_username", "expected_email": "", "config_file_exists": False}, ), ( - [None, "cli@email.com"], - ["input_username", ""], - ["input_username", "cli@email.com"], + {"cli_username": None, "cli_email": "cli@email.com", "skip_config_creation": False}, + {"input_username": "", "input_email": ""}, + {"expected_username": "", "expected_email": "cli@email.com", "config_file_exists": False}, ), ( - ["", ""], - ["input_username", "input@email.com"], - ["input_username", "input@email.com"], + {"cli_username": None, "cli_email": None, "skip_config_creation": False}, + {"input_username": "input_username", "input_email": ""}, + {"expected_username": "input_username", "expected_email": "", "config_file_exists": False}, ), ( - ["cli_username", ""], - ["", "input@email.com"], - ["cli_username", "input@email.com"], + {"cli_username": None, "cli_email": None, "skip_config_creation": False}, + {"input_username": "", "input_email": "input@email.com"}, + {"expected_username": "", "expected_email": "input@email.com", "config_file_exists": False}, ), ( - ["", "cli@email.com"], - ["input_username", ""], - ["input_username", "cli@email.com"], + {"cli_username": "cli_username", "cli_email": None, "skip_config_creation": False}, + {"input_username": "input_username", "input_email": ""}, + {"expected_username": "input_username", "expected_email": "", "config_file_exists": False}, ), ( - ["cli_username", "cli@email.com"], - ["input_username", "input@email.com"], - ["cli_username", "cli@email.com"], + {"cli_username": None, "cli_email": "cli@email.com", "skip_config_creation": False}, + {"input_username": "", "input_email": "input@email.com"}, + {"expected_username": "", "expected_email": "input@email.com", "config_file_exists": False}, + ), + # Case 2: Both username and email are provided, config file should be created + ( + {"cli_username": "cli_username", "cli_email": "cli@email.com", "skip_config_creation": False}, + {"input_username": "", "input_email": ""}, + {"expected_username": "cli_username", "expected_email": "cli@email.com", "config_file_exists": True}, + ), + ( + {"cli_username": "cli_username", "cli_email": None, "skip_config_creation": False}, + {"input_username": "", "input_email": "input@email.com"}, + {"expected_username": "cli_username", "expected_email": "input@email.com", "config_file_exists": True}, + ), + ( + {"cli_username": None, "cli_email": "cli@email.com", "skip_config_creation": False}, + {"input_username": "input_username", "input_email": ""}, + {"expected_username": "input_username", "expected_email": "cli@email.com", "config_file_exists": True}, + ), + ( + {"cli_username": None, "cli_email": None, "skip_config_creation": False}, + {"input_username": "input_username", "input_email": "input@email.com"}, + {"expected_username": "input_username", "expected_email": "input@email.com", "config_file_exists": True}, + ), + ( + {"cli_username": "cli_username", "cli_email": "cli@email.com", "skip_config_creation": False}, + {"input_username": "input_username", "input_email": "input@email.com"}, + {"expected_username": "input_username", "expected_email": "input@email.com", "config_file_exists": True}, ), ] params_user_info_no_conf_file_no_inputs = [ - ([None, None], ["", ""], ["", ""]), + ( + {"cli_username": None, "cli_email": None, "skip_config_creation": True}, + {"expected_username": "", "expected_email": ""}, + ), + ( + {"cli_username": "cli_username", "cli_email": None, "skip_config_creation": True}, + {"expected_username": "cli_username", "expected_email": ""}, + ), + ( + {"cli_username": None, "cli_email": "cli@email.com", "skip_config_creation": True}, + {"expected_username": "", "expected_email": "cli@email.com"}, + ), + ( + {"cli_username": "cli_username", "cli_email": "cli@email.com", "skip_config_creation": True}, + {"expected_username": "cli_username", "expected_email": "cli@email.com"}, + ), ] -@pytest.mark.parametrize("inputs, expected", params_user_info_with_home_conf_file) -def test_get_user_info_with_home_conf_file(monkeypatch, inputs, expected, user_filesystem): +@pytest.mark.parametrize("cli_inputs, expected", params_user_info_with_home_conf_file) +def test_get_user_info_with_home_conf_file(monkeypatch, cli_inputs, expected, user_filesystem): _setup_dirs(monkeypatch, user_filesystem) - _run_tests(inputs, expected) + _run_tests(cli_inputs, expected) -@pytest.mark.parametrize("inputs, expected", params_user_info_with_local_conf_file) -def test_get_user_info_with_local_conf_file(monkeypatch, inputs, expected, user_filesystem): +@pytest.mark.parametrize("cli_inputs, expected", params_user_info_with_local_conf_file) +def test_get_user_info_with_local_conf_file(monkeypatch, cli_inputs, expected, user_filesystem): _setup_dirs(monkeypatch, user_filesystem) local_config_data = {"username": "cwd_username", "email": "cwd@email.com"} with open(Path(user_filesystem) / "diffpyconfig.json", "w") as f: json.dump(local_config_data, f) - _run_tests(inputs, expected) + _run_tests(cli_inputs, expected) + # Run tests again without global config, results should be the same os.remove(Path().home() / "diffpyconfig.json") - _run_tests(inputs, expected) + _run_tests(cli_inputs, expected) -@pytest.mark.parametrize("inputsa, inputsb, expected", params_user_info_with_no_home_conf_file) -def test_get_user_info_with_no_home_conf_file(monkeypatch, inputsa, inputsb, expected, user_filesystem): +@pytest.mark.parametrize("cli_inputs, inputs, expected", params_user_info_with_no_conf_file) +def test_get_user_info_with_no_conf_file(monkeypatch, cli_inputs, inputs, expected, user_filesystem): _setup_dirs(monkeypatch, user_filesystem) os.remove(Path().home() / "diffpyconfig.json") - inp_iter = iter(inputsb) + inp_iter = iter([inputs["input_username"], inputs["input_email"]]) monkeypatch.setattr("builtins.input", lambda _: next(inp_iter)) - _run_tests(inputsa, expected) + _run_tests(cli_inputs, expected) confile = Path().home() / "diffpyconfig.json" - assert confile.is_file() + assert confile.is_file() == expected["config_file_exists"] -@pytest.mark.parametrize("inputsa, inputsb, expected", params_user_info_no_conf_file_no_inputs) -def test_get_user_info_no_conf_file_no_inputs(monkeypatch, inputsa, inputsb, expected, user_filesystem): +@pytest.mark.parametrize("cli_inputs, expected", params_user_info_no_conf_file_no_inputs) +def test_get_user_info_no_conf_file_no_inputs(monkeypatch, cli_inputs, expected, user_filesystem): _setup_dirs(monkeypatch, user_filesystem) os.remove(Path().home() / "diffpyconfig.json") - inp_iter = iter(inputsb) - monkeypatch.setattr("builtins.input", lambda _: next(inp_iter)) - _run_tests(inputsa, expected) + _run_tests(cli_inputs, expected) confile = Path().home() / "diffpyconfig.json" assert confile.exists() is False