Skip to content
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

FIX: InteractiveOption Remove double call of normal callback #5064

Conversation

janssenhenning
Copy link
Contributor

@janssenhenning janssenhenning commented Aug 11, 2021

Fixes #5023.

In the InteractiveOption providing a normal callback is wrapped
in the method after_callback while the actual callback is replaced
by prompt_callback. Now the following situation occurs when the
parameter should be prompted for:

  1. prompt_loop is entered via prompt_callback and the value
    is converted in safely_convert
  2. safely_convert also includes a call to
    prompt_callback, where a call to after_callback is
    performed
  3. After a value succeeds the code goes back into the place, where
    the prompt_loop was entered in prompt_callback
    and continues with an additional call to after_callback
    which already executed before

The fix here is to directly return after the prompt_loop finishes and
handle the case, where the value should not be prompted separately

In the ``InteractiveOption`` providing a normal callback is wrapped
in the method ``after_callback`` while the actual callback is replaced
by ``prompt_callback``. Now the following situation occurs when the
parameter should be prompted for:

   1. ``prompt_loop`` is entered via ``prompt_callback`` and the value
      is converted in ``safely_convert``
   2. ``safely_convert`` also includes a call to
      ``prompt_callback``, where a call to ``after_callback`` is
      performed
   3. After a value succeeds the code goes back into the place, where
      the ``prompt_loop`` was entered in ``prompt_callback``
      and continues with an additional call to ``after_callback``
      which already executed before

The fix here is to directly return after the prompt_loop finishes and
handle the case, where the value should not be prompted separately
@sphuber sphuber self-requested a review August 11, 2021 18:55
@codecov
Copy link

codecov bot commented Aug 11, 2021

Codecov Report

Merging #5064 (722cdf4) into develop (bf80fde) will increase coverage by 0.01%.
The diff coverage is 100.00%.

Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #5064      +/-   ##
===========================================
+ Coverage    80.39%   80.39%   +0.01%     
===========================================
  Files          529      529              
  Lines        36881    36881              
===========================================
+ Hits         29645    29647       +2     
+ Misses        7236     7234       -2     
Flag Coverage Δ
django 74.90% <100.00%> (+0.01%) ⬆️
sqlalchemy 73.80% <100.00%> (+0.03%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
aiida/cmdline/params/options/interactive.py 84.85% <100.00%> (ø)
aiida/transports/plugins/local.py 81.66% <0.00%> (+0.26%) ⬆️
aiida/transports/util.py 65.63% <0.00%> (+3.13%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update bf80fde...722cdf4. Read the comment docs.

Copy link
Contributor

@sphuber sphuber left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution @janssenhenning . The solution looks fine at first look. Just have a small suggestion for the test that I think makes the chances for false positives smaller and is slightly easier to follow as to what is happening. Let me know what you think.

:param value: the value passed for the parameter
:raises `click.BadParameter`: if the value is not a positive number
"""
click.echo('Validating Number')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering if we can make the testing of the output more robust, by including the value in the echo

Suggested change
click.echo('Validating Number')
click.echo(f'Validating {value}')

Comment on lines 122 to 125
lines = result.output.split('\n')
#The callback should be called once per prompt
#where type conversion was successful
self.assertEqual(lines.count('Validating Number'), 3)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the change of including the value being passed in the echo string, I think we can make the test more robust and make the changes on false positives smaller. Since the echo should be the only output (right?) we should also be able to check the entire output verbatim. So I would suggest something like the following:

Suggested change
lines = result.output.split('\n')
#The callback should be called once per prompt
#where type conversion was successful
self.assertEqual(lines.count('Validating Number'), 3)
self.assertEqual(result.output, 'Validating string\nValidating -1\nValidating 1\n')

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that the test can be made more robust by including the value in the echo, but the change will not work as is, since for example 'Validating string' will not appear in the output. This value will fail when it is converted to a float so the callback will never be called.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see, because it would already fail the validation at the type which is expecting a float. Well, by accident, I think that is actually good to keep the string value in, as it tests that that doesn't hit the callback as it shouldn't. So maybe all you have to change in my suggestion is just to remove Validate string\n from the expected output

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will probably need to check specific lines of the output, since there is more stuff in result.output besides the 'Validating {value}' lines like the lines for entering the values for example but in principle yes

"""
cmd = self.simple_command(type=float, callback=validate_positive_number_with_echo)
runner = CliRunner()
result = runner.invoke(cmd, [], input='string\n-1\n-1\n1\n')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you accept my other suggestions, the input should also be updated.

Suggested change
result = runner.invoke(cmd, [], input='string\n-1\n-1\n1\n')
result = runner.invoke(cmd, [], input='string\n-1\n1\n')

@sphuber sphuber self-requested a review August 12, 2021 13:26
Copy link
Contributor

@sphuber sphuber left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot @janssenhenning

@sphuber sphuber merged commit 6f93253 into aiidateam:develop Aug 12, 2021
@janssenhenning janssenhenning deleted the issue_5023_callback_called_twice_interactive_option branch August 12, 2021 15:49
sphuber pushed a commit that referenced this pull request Aug 12, 2021
…5064)

In the `InteractiveOption` providing a normal callback is wrapped
in the method `after_callback` while the actual callback is replaced
by `prompt_callback`. Now the following situation occurs when the
parameter should be prompted for:

   1. `prompt_loop` is entered via `prompt_callback` and the value
      is converted in `safely_convert`
   2. `safely_convert` also includes a call to
      `prompt_callback`, where a call to `after_callback` is
      performed
   3. After a value succeeds the code goes back into the place, where
      the `prompt_loop` was entered in `prompt_callback`
      and continues with an additional call to `after_callback`
      which already executed before

The fix here is to directly return after the `prompt_loop` finishes and
handle the case, where the value should not be prompted separately

Cherry-pick: 6f93253
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Callback always called twice when providing prompt_fn and callback to InteractiveOption
2 participants