Beispiel #1
0
def get_plugin(category, name):
    """
    Return an instance of the class registered under the given name and
    for the specified plugin category.

    :param category: the plugin category to load the plugin from, e.g. 'transports'.
    :param name: the name of the plugin
    """
    group = 'aiida.{}'.format(category)

    eps = [ep for ep in epm.iter_entry_points(group=group) if ep.name == name]

    if not eps:
        raise MissingPluginError("No plugin named '{}' found for '{}'".format(
            name, category))

    if len(eps) > 1:
        raise MissingPluginError(
            "Multiple plugins found for '{}' in '{}'".format(name, category))

    entrypoint = eps[0]

    try:
        plugin = entrypoint.load()
    except ImportError as exception:
        import traceback
        raise LoadingPluginFailed("Loading the plugin '{}' failed:\n{}".format(
            name, traceback.format_exc()))

    return plugin
Beispiel #2
0
    def get_builder(self):
        """
        Create and return a new ProcessBuilder for the default Calculation
        plugin, as obtained by the self.get_input_plugin_name() method.

        :note: it also sets the ``builder.code`` value.

        :raise MissingPluginError: if the specified plugin does not exist.
        :raise ValueError: if no default plugin was specified.

        :return:
        """
        from aiida.orm.utils import CalculationFactory
        plugin_name = self.get_input_plugin_name()
        if plugin_name is None:
            raise ValueError(
                "You did not specify a default input plugin for this code")
        try:
            C = CalculationFactory(plugin_name)
        except MissingPluginError:
            raise MissingPluginError("The input_plugin name for this code is "
                                     "'{}', but it is not an existing plugin"
                                     "name".format(plugin_name))

        builder = C.get_builder()
        # Setup the code already
        builder.code = self

        return builder
Beispiel #3
0
def existing_plugins(base_class,
                     plugins_module_name,
                     max_depth=5,
                     suffix=None):
    """
    Return a list of strings of valid plugins.


    :param base_class: Identify all subclasses of the base_class
    :param plugins_module_name: a string with the full module name separated
        with dots that points to the folder with plugins.
        It must be importable by python.
    :param max_depth: Maximum depth (of nested modules) to be used when
            looking for plugins
    :param suffix: The suffix that is appended to the basename when looking
        for the (sub)class name. If not provided (or None), use the base
        class name.
    :return: a list of valid strings that can be used using a Factory or with
        load_plugin.
    """
    try:
        pluginmod = importlib.import_module(plugins_module_name)
    except ImportError:
        raise MissingPluginError(
            "Unable to load the plugin module {}".format(plugins_module_name))

    return _existing_plugins_with_module(base_class, pluginmod.__path__[0],
                                         plugins_module_name, "", max_depth,
                                         suffix)
Beispiel #4
0
def BaseFactory(module, base_class, base_modname, suffix=None):
    """
    Return a given subclass of Calculation, loading the correct plugin.

    :example: If `module='quantumespresso.pw'`, `base_class=JobCalculation`,
      `base_modname = 'aiida.orm.calculation.job'`, and `suffix='Calculation'`,
      the code will first look for a pw subclass of JobCalculation
      inside the quantumespresso module. Lacking such a class, it will try to look
      for a 'PwCalculation' inside the quantumespresso.pw module.
      In the latter case, the plugin class must have a specific name and be
      located in a specific file:
      if for instance plugin_name == 'ssh' and base_class.__name__ == 'Transport',
      then there must be a class named 'SshTransport' which is a subclass of base_class
      in a file 'ssh.py' in the plugins_module folder.
      To create the class name to look for, the code will attach the string
      passed in the base_modname (after the last dot) and the suffix parameter,
      if passed, with the proper CamelCase capitalization. If suffix is not
      passed, the default suffix that is used is the base_class class name.

    :param module: a string with the module of the plugin to load, e.g.
          'quantumespresso.pw'.
    :param base_class: a base class from which the returned class should inherit.
           e.g.: JobCalculation
    :param base_modname: a basic module name, under which the module should be
            found. E.g., 'aiida.orm.calculation.job'.
    :param suffix: If specified, the suffix that the class name will have.
      By default, use the name of the base_class.
    """
    try:
        return load_plugin(base_class, base_modname, module)
    except MissingPluginError as e1:
        # Automatically add subclass name and try again
        if suffix is None:
            actual_suffix = base_class.__name__
        else:
            actual_suffix = suffix
        mname = module.rpartition('.')[2].capitalize() + actual_suffix
        new_module = module + '.' + mname
        try:
            return load_plugin(base_class, base_modname, new_module)
        except MissingPluginError as e2:
            err_msg = ("Neither {} or {} could be loaded from {}. "
                       "Error messages were: '{}', '{}'").format(
                           module, new_module, base_modname, e1, e2)
            raise MissingPluginError(err_msg)
Beispiel #5
0
    def new_calc(self, *args, **kwargs):
        """
        Create and return a new Calculation object (unstored) with the correct
        plugin subclass, as obtained by the self.get_input_plugin_name() method.

        Parameters are passed to the calculation __init__ method.

        :note: it also directly creates the link to this code (that will of
            course be cached, since the new node is not stored yet).

        :raise MissingPluginError: if the specified plugin does not exist.
        :raise ValueError: if no default plugin was specified in the code.
        """
        import warnings
        warnings.warn(
            'directly creating and submitting calculations is deprecated, use the {}\nSee:{}'
            .format('ProcessBuilder',
                    DEPRECATION_DOCS_URL), DeprecationWarning)

        from aiida.orm.utils import CalculationFactory
        plugin_name = self.get_input_plugin_name()
        if plugin_name is None:
            raise ValueError("You did not specify an input plugin "
                             "for this code")

        try:
            C = CalculationFactory(plugin_name)

        except MissingPluginError:
            raise MissingPluginError("The input_plugin name for this code is "
                                     "'{}', but it is not an existing plugin"
                                     "name".format(plugin_name))

        # For remote codes, automatically set the computer,
        # unless explicitly set by the user
        if not self.is_local():
            if 'computer' not in kwargs:
                kwargs['computer'] = self.get_remote_computer()

        new_calc = C(*args, **kwargs)
        # I link to the code
        new_calc.use_code(self)
        return new_calc
Beispiel #6
0
def load_plugin(base_class, plugins_module, plugin_type):
    """
    Load a specific plugin for the given base class.

    This is general and works for any plugin used in AiiDA.

    NOTE: actually, now plugins_module and plugin_type are joined with a dot,
        and the plugin is retrieved splitting using the last dot of the resulting
        string.
    TODO: understand if it is probably better to join the two parameters above
        to a single one.

    Args:
        base_class
            the abstract base class of the plugin.
        plugins_module
            a string with the full module name separated with dots
            that points to the folder with plugins. It must be importable by python.
        plugin_type
            the name of the plugin.

    Return:
        the class of the required plugin.

    Raise:
        MissingPluginError if the plugin cannot be loaded

    Example:
       plugin_class = load_plugin(
           aiida.transport.Transport,'aiida.transport.plugins','ssh.SshTransport')

       and plugin_class will be the class 'aiida.transport.plugins.ssh.SshTransport'
    """

    module_name = ".".join([plugins_module, plugin_type])
    real_plugin_module, plugin_name = module_name.rsplit('.', 1)

    try:
        pluginmod = importlib.import_module(real_plugin_module)
    except ImportError:
        raise MissingPluginError(
            "Unable to load the plugin module {}".format(real_plugin_module))

    try:
        pluginclass = pluginmod.__dict__[plugin_name]
    except KeyError:
        raise MissingPluginError(
            "Unable to load the class {} within {}".format(
                plugin_name, real_plugin_module))

    try:
        if issubclass(pluginclass, base_class):
            return pluginclass
        else:
            # Quick way of going into the except case
            err_msg = "{} is not a subclass of {}".format(
                module_name, base_class.__name__)
            raise MissingPluginError(err_msg)
    except TypeError:
        # This happens when we pass a non-class to issubclass;
        err_msg = "{} is not a class".format(module_name)
        raise MissingPluginError(err_msg)