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

CLI: verdi export to a yaml file with keys from code class #5860

Merged
merged 2 commits into from
Jan 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions aiida/cmdline/commands/cmd_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import click
import tabulate
import yaml

from aiida.cmdline.commands.cmd_verdi import verdi
from aiida.cmdline.groups.dynamic import DynamicEntryPointCommandGroup
Expand Down Expand Up @@ -235,6 +236,26 @@ def show(code):
echo.echo(tabulate.tabulate(table))


@verdi_code.command()
@arguments.CODE()
@arguments.OUTPUT_FILE(type=click.Path(exists=False))
@with_dbenv()
def export(code, output_file):
"""Export code to a yaml file."""
code_data = {}

for key in code.get_cli_options().keys():
if key == 'computer':
value = getattr(code, key).label
else:
value = getattr(code, key)

code_data[key] = str(value)
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to convert to str? Should the yaml serializer not take care of this. Then the boolean question would be solved in any case. I don't think we have codes that have any attributes that are not YAML-serializable. I think the data is guaranteed to be JSON-serializable, since it comes from the database, so it should also be YAML-serializable.

Copy link
Member Author

Choose a reason for hiding this comment

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

The problem comes from the filepath_executable which I read by code._get_cli_options. The type is PurePosixPath which is not yaml-serializable. I can make another elif for this, but I think should be fine that all converted to string?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah I agree, fine to keep the cast to str then as long as the round-trip works


with open(output_file, 'w', encoding='utf-8') as yfhandle:
yaml.dump(code_data, yfhandle)


@verdi_code.command()
@arguments.CODES()
@options.DRY_RUN()
Expand Down
1 change: 0 additions & 1 deletion aiida/orm/nodes/data/code/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ def __init__(
self.append_text = append_text
self.prepend_text = prepend_text
self.use_double_quotes = use_double_quotes
self.use_double_quotes = use_double_quotes
self.is_hidden = is_hidden

@abc.abstractmethod
Expand Down
1 change: 1 addition & 0 deletions docs/source/reference/command_line.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ Below is a list with all available subcommands.
create Create a new code.
delete Delete a code.
duplicate Duplicate a code allowing to change some parameters.
export Export code to a yaml file.
hide Hide one or more codes from `verdi code list`.
list List the available codes.
relabel Relabel a code.
Expand Down
25 changes: 25 additions & 0 deletions tests/cmdline/commands/test_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,31 @@ def test_code_duplicate_ignore(run_cli_command, aiida_local_code_factory, non_in
assert duplicate.description == ''


@pytest.mark.usefixtures('aiida_profile_clean')
def test_code_export(run_cli_command, aiida_local_code_factory, tmp_path, file_regression):
"""Test export the code setup to str."""
prepend_text = 'module load something\n some command'
code = aiida_local_code_factory('core.arithmetic.add', '/bin/cat', label='code', prepend_text=prepend_text)
filepath = tmp_path / 'code.yml'
options = [str(code.pk), str(filepath)]
run_cli_command(cmd_code.export, options)

# file regression check
with open(filepath, 'r', encoding='utf-8') as fhandle:
content = fhandle.read()
file_regression.check(content, extension='.yml')

# round trip test by create code from the config file
# we pass the new label to override since cannot have two code with same labels
new_label = 'code0'
run_cli_command(
cmd_code.code_create, ['core.code.installed', '--non-interactive', '--config', filepath, '--label', new_label]
)
new_code = load_code(new_label)
assert code.base.attributes.all == new_code.base.attributes.all
assert isinstance(new_code, InstalledCode)


@pytest.mark.parametrize('non_interactive_editor', ('vim -cwq',), indirect=True)
def test_from_config_local_file(non_interactive_editor, run_cli_command, aiida_localhost):
"""Test setting up a code from a config file on disk."""
Expand Down
8 changes: 8 additions & 0 deletions tests/cmdline/commands/test_code/test_code_export.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
append_text: ''
computer: localhost
default_calc_job_plugin: core.arithmetic.add
description: code
filepath_executable: /bin/cat
label: code
prepend_text: "module load something\n some command"
use_double_quotes: 'False'