# -*- coding: utf-8 -*- ########################################################################### # Copyright (c), The AiiDA team. All rights reserved. # # This file is part of the AiiDA code. # # # # The code is hosted on GitHub at https://github.com/aiidateam/aiida-core # # For further information on the license, see the LICENSE.txt file # # For further information please visit http://www.aiida.net # ########################################################################### """Utilities to operate on `CalcJobNode` instances.""" from aiida.common import exceptions __all__ = ('CalcJobResultManager',) [docs]class CalcJobResultManager: """ Utility class to easily access the contents of the 'default output' node of a `CalcJobNode`. A `CalcJob` process can mark one of its outputs as the 'default output'. The default output node will always be returned by the `CalcJob` and will always be a `Dict` node. If a `CalcJob` defines such a default output node, this utility class will simplify retrieving the result of said node through the `CalcJobNode` instance produced by the execution of the `CalcJob`. The default results are only defined if the `CalcJobNode` has a `process_type` that can be successfully used to load the corresponding `CalcJob` process class *and* if its process spec defines a `default_output_node`. If both these conditions are met, the results are defined as the dictionary contained within the default output node. """ [docs] def __init__(self, node): """Construct an instance of the `CalcJobResultManager`. :param calc: the `CalcJobNode` instance. """ self._node = node self._result_node = None self._results = None @property def node(self): """Return the `CalcJobNode` associated with this result manager instance.""" return self._node [docs] def _load_results(self): """Try to load the results for the `CalcJobNode` of this result manager. :raises ValueError: if no default output node could be loaded """ try: process_class = self._node.process_class except ValueError as exception: raise ValueError(f'cannot load results because process class cannot be loaded: {exception}') process_spec = process_class.spec() default_output_node_label = process_spec.default_output_node if default_output_node_label is None: raise ValueError(f'cannot load results as {process_class} does not specify a default output node') try: default_output_node = self.node.get_outgoing().get_node_by_label(default_output_node_label) except exceptions.NotExistent as exception: raise ValueError(f'cannot load results as the default node could not be retrieved: {exception}') self._result_node = default_output_node self._results = default_output_node.get_dict() [docs] def get_results(self): """Return the results dictionary of the default results node of the calculation node. This property will lazily load the dictionary. :return: the dictionary of the default result node """ if self._results is None: self._load_results() return self._results [docs] def __dir__(self): """Add the keys of the results dictionary such that they can be autocompleted.""" return sorted(list(self.get_results().keys())) [docs] def __iter__(self): """Return an iterator over the keys of the result dictionary.""" for key in self.get_results().keys(): yield key [docs] def __getattr__(self, name): """Return an attribute from the results dictionary. :param name: name of the result return :return: value of the attribute :raises AttributeError: if the results node cannot be retrieved or it does not contain the `name` attribute """ try: return self.get_results()[name] except ValueError as exception: raise AttributeError from exception except KeyError: raise AttributeError(f"Default result node<{self._result_node.pk}> does not contain key '{name}'") [docs] def __getitem__(self, name): """Return an attribute from the results dictionary. :param name: name of the result return :return: value of the attribute :raises KeyError: if the results node cannot be retrieved or it does not contain the `name` attribute """ try: return self.get_results()[name] except ValueError as exception: raise KeyError from exception except KeyError: raise KeyError(f"Default result node<{self._result_node.pk}> does not contain key '{name}'")