-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
change args to user_info and fix docstring #253
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #253 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 8 8
Lines 380 379 -1
=========================================
- Hits 380 379 -1
|
I am still struggling a bit with this functionality. I think maybe I got to the botto of why this is the case (though maybe not!). I think the function is trying to do two things which is making me confused. The two things are (a) getting the config information from config files and (b) a workflow to create/maintain the config file. I wonder if this all gets a bit easier if we separate these behaviorws, for example, with a conditional, but this taking place outside the |
Yes, I agree we can make it a bit more readable! I drew a diagram on this. I am thinking that we can have one private function for each action in the diagram? |
@sbillinge Since we're redoing the workflow anyways, I'm adding the skip_config_creation for #244 here too. Here's a new diagram I'm thinking about: |
^push an intermediate commit in case anyone would like to comment, I will likely reformat some functions and make the comments clearer |
I edited the codes again and it's ready for some feedback now @sbillinge |
|
||
def clean_dict(obj): | ||
|
||
def _clean_dict(obj): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
made this function private
@@ -71,67 +80,64 @@ def load_config(file_path): | |||
def _sorted_merge(*dicts): | |||
merged = {} | |||
for d in dicts: | |||
d = _clean_dict(d) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add a step here so we don't need to pass in _clean_dict(d)
for every d
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", "" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
name change from args
to user_info
) | ||
return return_bool | ||
config = {"username": _stringify(username), "email": _stringify(email)} | ||
if username and email: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function originally returns a bool that passes back to get_user_info
to see if we need to create a global config file. If user only input none or one of username / email, an empty global config file will be created. I simplified the logic here so that we only create a global config file if both username and email are present. Does this make sense though?
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"]} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"cli_inputs" refer to function arguments passed to get_user_info
. Not sure if this is the best name here but I want to distinguish them from user inputs via prompts ("inputs").
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to still have a helper function called _run_tests
within each test func?
The name run_tests
to me is quite ambiguous - what test is it running? comparing username and email?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoops missed this comment. Yeah I think we can keep this test but maybe have a better name
confile = Path().home() / "diffpyconfig.json" | ||
assert confile.is_file() | ||
assert confile.is_file() == expected["config_file_exists"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
global config file should only be created when both username and email are given
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
config file is never created if user only provides function arguments
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 = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
params_user_info_with_home_conf_file
may not be needed - please see our discussion: https://github.com/diffpy/diffpy.utils/pull/236/files#r1885496247 @yucongalicechen
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is probably a different test case than that issue..?
{"expected_username": "cli_username", "expected_email": "[email protected]"}, | ||
), | ||
( | ||
{"cli_username": None, "cli_email": "[email protected]", "skip_config_creation": False}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have 5 variables used - cli_username, cli_email, skip_config, expected_username, expected_email, while we have a lots of repeated key values, making it a bi hard to manage.
can we pass variables to @pytest.mark.parametrize("input_username, input_email, ... ")
. Also, does it have to be called cli...
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey Bob, thanks for the feedback!! I think these repeated key values correspond to different test cases. Basically this function tries to get username/email from four places: (a) prompt input, (b) function arguments (which we called cli input because it was initially used by labpdfproc, where the function arguments are from cli), (c) local config file, (d) global config file, with priority order.
Currently we have four test functions that correspond to:
(1) we only have global config file, function prioritizes (b) to (d) (that is, replace (d) if (b) is non-empty).
(2) we have local config file with/without global config file, function prioritizes (b) to (c).
(3) we dont have config files, and user does not choose to skip config creation, then function prioritizes (a) to (b), and create a global config file if both username/email arguments are present.
(4) we dont have config files, and user chooses to skip config creation (this means we don't have (a)), then the function reads (b), and replace username/email with an empty string if no value given.
So I think all arguments and test values are necessary here. I will add a bit more comment to the tests so that it's clearer. We might be able to find a better way to organize the tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @yucongalicechen for the comments. Yeah I had a quick look at the src code.
Username/email from 4 places:
- prompt
- function args
- local config
- global config
and the test functions are basically testing the input/out and reading/creating config files as needed..
Noted.
@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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you could help me understanding - what's the role of moneypatch
and why do we need to setup a dir using _setup_dirs
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is to set up the global and local file paths for the test cases because the function will try to read files from these filepaths
return { | ||
"username": _stringify(user_info.get("username", "")), | ||
"email": _stringify(user_info.get("email", "")), | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
simplified the workflow here
@sbillinge I've simplified the workflow using the idea from the diagram, please check. |
replaced by #267 |
closes #245, closes #244
@sbillinge ready for review