Пример #1
0
    def _infer_full_type(self, full_type):
        """Infer the full type based on the current namespace path and the given full type of the leaf."""
        from aiida.common.utils import strip_prefix

        if full_type or self._path is None:
            return full_type

        full_type = strip_prefix(self._path, 'node.')

        if full_type.startswith('process.'):
            for basepath, full_type_template in self.process_full_type_mapping.items():
                if full_type.startswith(basepath):
                    plugin_name = strip_prefix(full_type, basepath)
                    if plugin_name.startswith(DEFAULT_NAMESPACE_LABEL):
                        temp_type_template = self.process_full_type_mapping_unplugged[basepath]
                        plugin_name = strip_prefix(plugin_name, DEFAULT_NAMESPACE_LABEL + '.')
                        full_type = temp_type_template.format(plugin_name=plugin_name)
                    else:
                        full_type = full_type_template.format(plugin_name=plugin_name)
                    return full_type

        full_type += f'.{LIKE_OPERATOR_CHARACTER}{FULL_TYPE_CONCATENATOR}'

        if full_type.startswith('process.'):
            full_type += LIKE_OPERATOR_CHARACTER

        return full_type
Пример #2
0
    def _infer_full_type(self, full_type):
        """Infer the full type based on the current namespace path and the given full type of the leaf."""
        from aiida.common.utils import strip_prefix

        if full_type or self._path is None:
            return full_type

        full_type = strip_prefix(self._path, 'node.')

        if full_type.startswith('process.'):
            for basepath, full_type_template in self.process_full_type_mapping.items(
            ):
                if full_type.startswith(basepath):
                    plugin_name = strip_prefix(full_type, basepath)
                    full_type = full_type_template.format(
                        plugin_name=plugin_name)
                    return full_type

        full_type += '.{}{}'.format(LIKE_OPERATOR_CHARACTER,
                                    FULL_TYPE_CONCATENATOR)

        if full_type.startswith('process.'):
            full_type += LIKE_OPERATOR_CHARACTER

        return full_type
Пример #3
0
def load_node_class(type_string):
    """
    Return the `Node` sub class that corresponds to the given type string.

    :param type_string: the `type` string of the node
    :return: a sub class of `Node`
    """
    from aiida.orm import Data, Node
    from aiida.plugins.entry_point import load_entry_point

    if type_string == '':
        return Node

    if type_string == 'data.Data.':
        return Data

    if not type_string.endswith('.'):
        raise exceptions.DbContentError('The type string `{}` is invalid'.format(type_string))

    try:
        base_path = type_string.rsplit('.', 2)[0]
    except ValueError:
        raise exceptions.EntryPointError

    # This exception needs to be there to make migrations work that rely on the old type string starting with `node.`
    # Since now the type strings no longer have that prefix, we simply strip it and continue with the normal logic.
    if base_path.startswith('node.'):
        base_path = strip_prefix(base_path, 'node.')

    # Data nodes are the only ones with sub classes that are still external, so if the plugin is not available
    # we fall back on the base node type
    if base_path.startswith('data.'):
        entry_point_name = strip_prefix(base_path, 'data.')
        try:
            return load_entry_point('aiida.data', entry_point_name)
        except exceptions.MissingEntryPointError:
            return Data

    if base_path.startswith('process'):
        entry_point_name = strip_prefix(base_path, 'nodes.')
        return load_entry_point('aiida.node', entry_point_name)

    # At this point we really have an anomalous type string. At some point, storing nodes with unresolvable type strings
    # was allowed, for example by creating a sub class in a shell and then storing an instance. Attempting to load the
    # node then would fail miserably. This is now no longer allowed, but we need a fallback for existing cases, which
    # should be rare. We fallback on `Data` and not `Node` because bare node instances are also not storable and so the
    # logic of the ORM is not well defined for a loaded instance of the base `Node` class.
    warnings.warn('unknown type string `{}`, falling back onto `Data` class'.format(type_string))  # pylint: disable=no-member

    return Data
Пример #4
0
def get_type_string_from_class(class_module, class_name):
    """
    Given the module and name of a class, determine the orm_class_type string, which codifies the
    orm class that is to be used. The returned string will always have a terminating period, which
    is required to query for the string in the database

    :param class_module: module of the class
    :param class_name: name of the class
    """
    from aiida.plugins.entry_point import get_entry_point_from_class, ENTRY_POINT_GROUP_TO_MODULE_PATH_MAP

    group, entry_point = get_entry_point_from_class(class_module, class_name)

    # If we can reverse engineer an entry point group and name, we're dealing with an external class
    if group and entry_point:
        module_base_path = ENTRY_POINT_GROUP_TO_MODULE_PATH_MAP[group]
        type_string = '{}.{}.{}.'.format(module_base_path, entry_point.name, class_name)

    # Otherwise we are dealing with an internal class
    else:
        type_string = '{}.{}.'.format(class_module, class_name)

    prefixes = ('aiida.orm.nodes.',)

    # Sequentially and **in order** strip the prefixes if present
    for prefix in prefixes:
        type_string = strip_prefix(type_string, prefix)

    # This needs to be here as long as `aiida.orm.nodes.data` does not live in `aiida.orm.nodes.data` because all the
    # `Data` instances will have a type string that starts with `data.` instead of `nodes.`, so in order to match any
    # `Node` we have to look for any type string essentially.
    if type_string == 'node.Node.':
        type_string = ''

    return type_string
Пример #5
0
def load_node_class(type_string):
    """
    Return the `Node` sub class that corresponds to the given type string.

    :param type_string: the `type` string of the node
    :return: a sub class of `Node`
    """
    from aiida.orm import Data, Node
    from aiida.plugins.entry_point import load_entry_point

    if type_string == '':
        return Node

    if type_string == 'data.Data.':
        return Data

    if not type_string.endswith('.'):
        raise exceptions.DbContentError(
            'The type string `{}` is invalid'.format(type_string))

    try:
        base_path = type_string.rsplit('.', 2)[0]
    except ValueError:
        raise exceptions.EntryPointError

    # This exception needs to be there to make migrations work that rely on the old type string starting with `node.`
    # Since now the type strings no longer have that prefix, we simply strip it and continue with the normal logic.
    if base_path.startswith('node.'):
        base_path = strip_prefix(base_path, 'node.')

    # Data nodes are the only ones with sub classes that are still external, so if the plugin is not available
    # we fall back on the base node type
    if base_path.startswith('data.'):
        entry_point_name = strip_prefix(base_path, 'data.')
        try:
            return load_entry_point('aiida.data', entry_point_name)
        except exceptions.MissingEntryPointError:
            return Data

    if base_path.startswith('process'):
        entry_point_name = strip_prefix(base_path, 'nodes.')
        return load_entry_point('aiida.node', entry_point_name)

    raise exceptions.EntryPointError(
        'unknown type string {}'.format(type_string))
Пример #6
0
def load_entry_point_from_full_type(full_type):
    """Return the loaded entry point for the given `full_type` unique node identifier.

    :param full_type: the `full_type` unique node identifier
    :raises ValueError: if the `full_type` is invalid
    :raises TypeError: if the `full_type` is not a string type
    :raises `~aiida.common.exceptions.EntryPointError`: if the corresponding entry point cannot be loaded
    """
    from aiida.common import EntryPointError
    from aiida.common.utils import strip_prefix
    from aiida.plugins.entry_point import is_valid_entry_point_string, load_entry_point, load_entry_point_from_string

    data_prefix = 'data.'

    validate_full_type(full_type)

    node_type, process_type = full_type.split(FULL_TYPE_CONCATENATOR)

    if is_valid_entry_point_string(process_type):

        try:
            return load_entry_point_from_string(process_type)
        except EntryPointError:
            raise EntryPointError(
                f'could not load entry point `{process_type}`')

    elif node_type.startswith(data_prefix):

        base_name = strip_prefix(node_type, data_prefix)
        entry_point_name = base_name.rsplit('.', 2)[0]

        try:
            return load_entry_point('aiida.data', entry_point_name)
        except EntryPointError:
            raise EntryPointError(
                f'could not load entry point `{process_type}`')

    # Here we are dealing with a `ProcessNode` with a `process_type` that is not an entry point string.
    # Which means it is most likely a full module path (the fallback option) and we cannot necessarily load the
    # class from this. We could try with `importlib` but not sure that we should
    raise EntryPointError(
        'entry point of the given full type cannot be loaded')