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
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
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)
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)
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
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)