Skip to content

Commit

Permalink
Process: refactor out input handling specific to CalcJob (#5539)
Browse files Browse the repository at this point in the history
The `CalcJob` subclass of `Process` adds inputs that are specific to its
implementation, namely `code`, `metadata.computer` and `metadata.options`.
Nevertheless, these were being handled in the `Process` class making
assumptions about the exact type of the `code` input.

The code is refactored to move the `CalcJob` specific logic to its
implementation. The signature of `_setup_metadata` had to be changed to
pass in a copy of the metadata inputs as the subclass needs to pop its
custom keys such that `Process` won't raise on keys it doesn't recognize.

The `_setup_inputs` implementation on `CalcJob` is simplified as it now
just needs to set the `Computer` of the `Code` if one hasn't already
been set in the `_setup_metadata` which is called before it.
  • Loading branch information
sphuber authored May 25, 2022
1 parent 9d903ca commit 408efa7
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 15 deletions.
23 changes: 23 additions & 0 deletions aiida/engine/processes/calcjobs/calcjob.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,29 @@ def prepare_for_submission(self, folder: Folder) -> CalcInfo:
"""
raise NotImplementedError()

def _setup_metadata(self, metadata: dict) -> None:
"""Store the metadata on the ProcessNode."""
computer = metadata.pop('computer', None)
if computer is not None:
self.node.computer = computer

options = metadata.pop('options', {})
for option_name, option_value in options.items():
self.node.set_option(option_name, option_value)

super()._setup_metadata(metadata)

def _setup_inputs(self) -> None:
"""Create the links between the input nodes and the ProcessNode that represents this process."""
super()._setup_inputs()

# If a computer has not yet been set, which should have been done in ``_setup_metadata`` if it was specified
# in the ``metadata`` inputs, set the computer associated with the ``code`` input. Note that not all ``code``s
# will have an associated computer, but in that case the ``computer`` property should return ``None`` and
# nothing would change anyway.
if not self.node.computer:
self.node.computer = self.inputs.code.computer # type: ignore[union-attr]

def _perform_dry_run(self):
"""Perform a dry run.
Expand Down
20 changes: 6 additions & 14 deletions aiida/engine/processes/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import asyncio
import collections
from collections.abc import Mapping
import copy
import enum
import inspect
import logging
Expand Down Expand Up @@ -684,27 +685,22 @@ def _setup_db_record(self) -> None:
elif isinstance(self.node, orm.WorkflowNode):
self.node.base.links.add_incoming(parent_calc, LinkType.CALL_WORK, self.metadata.call_link_label)

self._setup_metadata()
self._setup_metadata(copy.copy(dict(self.inputs.metadata)))
self._setup_inputs()

def _setup_metadata(self) -> None:
def _setup_metadata(self, metadata: dict) -> None:
"""Store the metadata on the ProcessNode."""
version_info = self.runner.plugin_version_provider.get_version_info(self.__class__)
self.node.base.attributes.set_many(version_info)

for name, metadata in self.metadata.items():
for name, value in metadata.items():
if name in ['store_provenance', 'dry_run', 'call_link_label']:
continue

if name == 'label':
self.node.label = metadata
self.node.label = value
elif name == 'description':
self.node.description = metadata
elif name == 'computer':
self.node.computer = metadata
elif name == 'options':
for option_name, option_value in metadata.items():
self.node.set_option(option_name, option_value)
self.node.description = value
else:
raise RuntimeError(f'unsupported metadata key: {name}')

Expand All @@ -716,10 +712,6 @@ def _setup_inputs(self) -> None:
if node is None:
continue

# Special exception: set computer if node is a remote Code and our node does not yet have a computer set
if isinstance(node, orm.InstalledCode) and not self.node.computer:
self.node.computer = node.computer

# Need this special case for tests that use ProcessNodes as classes
if isinstance(self.node, orm.CalculationNode):
self.node.base.links.add_incoming(node, LinkType.INPUT_CALC, name)
Expand Down
3 changes: 2 additions & 1 deletion tests/engine/processes/calcjobs/test_calc_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -874,11 +874,12 @@ class TestImport:
"""Test the functionality to import existing calculations completed outside of AiiDA."""

@pytest.fixture(autouse=True)
def init_profile(self, aiida_profile_clean, aiida_localhost): # pylint: disable=unused-argument
def init_profile(self, aiida_profile_clean, aiida_localhost, aiida_local_code_factory): # pylint: disable=unused-argument
"""Initialize the profile."""
# pylint: disable=attribute-defined-outside-init
self.computer = aiida_localhost
self.inputs = {
'code': aiida_local_code_factory('core.arithmetic.add', '/bin/bash', computer=aiida_localhost),
'x': orm.Int(1),
'y': orm.Int(2),
'metadata': {
Expand Down

0 comments on commit 408efa7

Please sign in to comment.