From 7a81342ed66640c7d80b0bb9892d661646454418 Mon Sep 17 00:00:00 2001 From: Sebastiaan Huber Date: Sun, 11 Oct 2020 16:22:10 +0200 Subject: [PATCH] Fix `UnboundLocalError` in `aiida.cmdline.utils.edit_multiline_template` If `click.edit` returns a falsy value, the following conditional would be skipped and the `value` variable would be undefined causing an `UnboundLocalError` to be raised. This bug was reported by @blokhin but the exact conditions under which it occurred are not clear. --- aiida/cmdline/utils/multi_line_input.py | 9 +++++---- tests/cmdline/utils/test_multiline.py | 22 +++++++++++++++++++++- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/aiida/cmdline/utils/multi_line_input.py b/aiida/cmdline/utils/multi_line_input.py index 23d0d3c969..def61c2720 100644 --- a/aiida/cmdline/utils/multi_line_input.py +++ b/aiida/cmdline/utils/multi_line_input.py @@ -20,18 +20,19 @@ def edit_multiline_template(template_name, comment_marker='#=', extension=None, :param comment_marker: the set of symbols that mark a comment line that should be stripped from the final value :param extension: the file extension to give to the rendered template file. :param kwargs: keywords that will be passed to the template rendering engine. - :return: the final string value entered in the editor with all comment lines stripped. + :return: the final string value entered in the editor with all comment lines stripped or an empty string if the + ``click.edit`` returned ``None``. """ from aiida.cmdline.utils.templates import env template = env.get_template(template_name) rendered = template.render(**kwargs) content = click.edit(rendered, extension=extension) - if content: + if content is not None: # Remove all comments, which are all lines that start with the comment marker - value = re.sub(f'(^{re.escape(comment_marker)}.*$\n)+', '', content, flags=re.M).strip() + return re.sub(f'(^{re.escape(comment_marker)}.*$\n)+', '', content, flags=re.M).strip() - return value + return '' def edit_comment(old_cmt=''): diff --git a/tests/cmdline/utils/test_multiline.py b/tests/cmdline/utils/test_multiline.py index 0073c731ce..e34272e0ae 100644 --- a/tests/cmdline/utils/test_multiline.py +++ b/tests/cmdline/utils/test_multiline.py @@ -9,9 +9,10 @@ ########################################################################### # pylint: disable=unused-argument """Unit tests for editing pre and post bash scripts, comments, etc.""" +import click import pytest -from aiida.cmdline.utils.multi_line_input import edit_comment +from aiida.cmdline.utils.multi_line_input import edit_comment, edit_multiline_template COMMAND = 'sleep 1 ; vim -c "g!/^#=/s/$/Test" -cwq' # Appends `Test` to every line NOT starting with `#=` @@ -27,3 +28,22 @@ def test_edit_comment(non_interactive_editor): old_comment = 'OldComment' new_comment = edit_comment(old_cmt=old_comment) assert new_comment == f'{old_comment}Test' + + +@pytest.mark.parametrize('non_interactive_editor', (COMMAND,), indirect=True) +def test_edit_multiline_template_empty(non_interactive_editor, monkeypatch): + """Test ``edit_multiline_template`` returns empty string when `click.edit` returns empty value e.g. ``None``.""" + + def edit(*args, **kwargs): + return None + + monkeypatch.setattr(click, 'edit', edit) + + assert edit_multiline_template(template_name='multiline.tpl') == '' + + +@pytest.mark.parametrize('non_interactive_editor', (COMMAND,), indirect=True) +def test_edit_multiline_template(non_interactive_editor): + """Test ``edit_multiline_template`` returns empty string when `click.edit` returns empty value e.g. ``None``.""" + # The template will contain two empty lines, and those will get `Test` appended to it + assert edit_multiline_template(template_name='multiline.tpl') == 'Test\nTest'